script ejemplo dynamically component javascript angular typescript

javascript - ejemplo - Comunicación de componente angular de 2 hermanos



use javascript in angular 2 (11)

Tengo un ListComponent. Cuando se hace clic en un elemento en ListComponent, los detalles de ese elemento deben mostrarse en DetailComponent. Ambos están en la pantalla al mismo tiempo, por lo que no hay enrutamiento involucrado.

¿Cómo le digo a DetailComponent en qué elemento de ListComponent se hizo clic?

He considerado emitir un evento hasta el padre (AppComponent), y tengo que el padre establezca el SelectedItem.id en DetailComponent con un @Input. O podría usar un servicio compartido con suscripciones observables.

EDITAR: Sin embargo, configurar el elemento seleccionado a través del evento + @Input no activa el DetailComponent, en caso de que necesite ejecutar código adicional. Así que no estoy seguro de que sea una solución aceptable.

Pero ambos métodos parecen mucho más complejos que la forma Angular 1 de hacer las cosas, que era a través de $ rootScope. $ Broadcast o $ scope. $ Parent. $ Broadcast.

Dado que todo en Angular 2 es un componente, me sorprende que no haya más información sobre comunicación de componentes.

¿Hay otra forma más directa de lograr esto?


Aquí hay una explicación práctica simple: simplemente explicada here

En call.service.ts

import { Observable } from ''rxjs''; import { Subject } from ''rxjs/Subject''; @Injectable() export class CallService { private subject = new Subject<any>(); sendClickCall(message: string) { this.subject.next({ text: message }); } getClickCall(): Observable<any> { return this.subject.asObservable(); } }

Componente desde donde desea llamar observable para informar a otro componente que se hace clic en el botón

import { CallService } from "../../../services/call.service"; export class MarketplaceComponent implements OnInit, OnDestroy { constructor(public Util: CallService) { } buttonClickedToCallObservable() { this.Util.sendClickCall(''Sending message to another comp that button is clicked''); } }

Componente donde desea realizar una acción en el botón en el que se hizo clic en otro componente

import { Subscription } from ''rxjs/Subscription''; import { CallService } from "../../../services/call.service"; ngOnInit() { this.subscription = this.Util.getClickCall().subscribe(message => { this.message = message; console.log(''---button clicked at another component---''); //call you action which need to execute in this component on button clicked }); } import { Subscription } from ''rxjs/Subscription''; import { CallService } from "../../../services/call.service"; ngOnInit() { this.subscription = this.Util.getClickCall().subscribe(message => { this.message = message; console.log(''---button clicked at another component---''); //call you action which need to execute in this component on button clicked }); }

Mi comprensión clara sobre la comunicación de componentes al leer esto: here


Debe configurar la relación padre-hijo entre sus componentes. El problema es que simplemente puede inyectar los componentes secundarios en el constructor del componente principal y almacenarlos en una variable local. En su lugar, debe declarar los componentes secundarios en su componente primario utilizando el @ViewChild propiedades @ViewChild . Así es como debería verse su componente principal:

import { Component, ViewChild, AfterViewInit } from ''@angular/core''; import { ListComponent } from ''./list.component''; import { DetailComponent } from ''./detail.component''; @Component({ selector: ''app-component'', template: ''<list-component></list-component><detail-component></detail-component>'', directives: [ListComponent, DetailComponent] }) class AppComponent implements AfterViewInit { @ViewChild(ListComponent) listComponent:ListComponent; @ViewChild(DetailComponent) detailComponent: DetailComponent; ngAfterViewInit() { // afther this point the children are set, so you can use them this.detailComponent.doSomething(); } }

https://angular.io/docs/ts/latest/api/core/index/ViewChild-var.html

https://angular.io/docs/ts/latest/cookbook/component-communication.html#parent-to-view-child

Tenga cuidado, el componente hijo no estará disponible en el constructor del componente padre, justo después de que se ngAfterViewInit gancho del ciclo de vida ngAfterViewInit . Para atrapar este gancho, simplemente implemente la interfaz AfterViewInit en su clase principal de la misma manera que lo haría con OnInit .

Pero, hay otros declaradores de propiedades como se explica en esta nota de blog: http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/


El servicio compartido es una buena solución para este problema. Si también desea almacenar información sobre la actividad, puede agregar el Servicio compartido a la lista de proveedores de sus módulos principales (app.module).

@NgModule({ imports: [ ... ], bootstrap: [ AppComponent ], declarations: [ AppComponent, ], providers: [ SharedService, ... ] });

Luego puede proporcionarlo directamente a sus componentes,

constructor(private sharedService: SharedService)

Con el Servicio compartido, puede usar funciones o puede crear un Asunto para actualizar varios lugares a la vez.

@Injectable() export class FolderTagService { public clickedItemInformation: Subject<string> = new Subject(); }

En el componente de la lista, puede publicar información sobre elementos en los que se hizo clic

this.sharedService.clikedItemInformation.next("something");

y luego puede obtener esta información en su componente de detalle:

this.sharedService.clikedItemInformation.subscribe((information) => { // do something });

Obviamente, los datos que enumeran los componentes compartidos pueden ser cualquier cosa. Espero que esto ayude.


En el caso de 2 componentes diferentes (componentes no anidados, padre / hijo / nieto), le sugiero esto:

Servicio de misión:

import { Injectable } from ''@angular/core''; import { Subject } from ''rxjs/Subject''; @Injectable() export class MissionService { // Observable string sources private missionAnnouncedSource = new Subject<string>(); private missionConfirmedSource = new Subject<string>(); // Observable string streams missionAnnounced$ = this.missionAnnouncedSource.asObservable(); missionConfirmed$ = this.missionConfirmedSource.asObservable(); // Service message commands announceMission(mission: string) { this.missionAnnouncedSource.next(mission); } confirmMission(astronaut: string) { this.missionConfirmedSource.next(astronaut); } }

Astronauta Componente:

import { Component, Input, OnDestroy } from ''@angular/core''; import { MissionService } from ''./mission.service''; import { Subscription } from ''rxjs/Subscription''; @Component({ selector: ''my-astronaut'', template: ` <p> {{astronaut}}: <strong>{{mission}}</strong> <button (click)="confirm()" [disabled]="!announced || confirmed"> Confirm </button> </p> ` }) export class AstronautComponent implements OnDestroy { @Input() astronaut: string; mission = ''<no mission announced>''; confirmed = false; announced = false; subscription: Subscription; constructor(private missionService: MissionService) { this.subscription = missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; this.announced = true; this.confirmed = false; }); } confirm() { this.confirmed = true; this.missionService.confirmMission(this.astronaut); } ngOnDestroy() { // prevent memory leak when component destroyed this.subscription.unsubscribe(); } }

Fuente: padres e hijos se comunican a través de un servicio



He estado pasando métodos de establecimiento del padre a uno de sus hijos a través de un enlace, llamando a ese método con los datos del componente hijo, lo que significa que el componente padre se actualiza y luego puede actualizar su segundo componente hijo con los nuevos datos. Sin embargo, requiere vincular ''esto'' o usar una función de flecha.

Esto tiene el beneficio de que los niños no están tan unidos entre sí, ya que no necesitan un servicio compartido específico.

No estoy del todo seguro de que esta sea la mejor práctica, sería interesante escuchar las opiniones de otros sobre esto.


Una directiva puede tener sentido en ciertas situaciones para ''conectar'' componentes. De hecho, las cosas que se están conectando ni siquiera necesitan ser componentes completos, y a veces es más liviano y más simple si no lo son.

Por ejemplo, tengo un componente del Youtube Player (envolviendo la API de Youtube) y quería algunos botones de controlador para él. La única razón por la que los botones no son parte de mi componente principal es porque están ubicados en otra parte del DOM.

En este caso, en realidad es solo un componente de ''extensión'' que solo se utilizará alguna vez con el componente ''principal''. Digo ''padre'', pero en el DOM es un hermano, así que llámalo como quieras.

Como dije, ni siquiera necesita ser un componente completo, en mi caso es solo un <button> (pero podría ser un componente).

@Directive({ selector: ''[ytPlayerPlayButton]'' }) export class YoutubePlayerPlayButtonDirective { _player: YoutubePlayerComponent; @Input(''ytPlayerVideo'') private set player(value: YoutubePlayerComponent) { this._player = value; } @HostListener(''click'') click() { this._player.play(); } constructor(private elementRef: ElementRef) { // the button itself } }

En el HTML para ProductPage.component , donde youtube-player es obviamente mi componente que envuelve la API de Youtube.

<youtube-player #technologyVideo videoId=''NuU74nesR5A''></youtube-player> ... lots more DOM ... <button class="play-button" ytPlayerPlayButton [ytPlayerVideo]="technologyVideo">Play</button>

La directiva me conecta todo, y no tengo que declarar el evento (clic) en el HTML.

Por lo tanto, la directiva se puede conectar muy bien al reproductor de video sin tener que involucrar a ProductPage como mediador.

Esta es la primera vez que lo hago, así que aún no estoy seguro de cuán escalable podría ser para situaciones mucho más complejas. Sin embargo, por esto estoy contento y deja mi HTML simple y las responsabilidades de todo lo distinto.


Una forma de hacerlo es usar un servicio compartido .

Sin embargo, la siguiente solución me parece mucho más simple: permite compartir datos entre 2 hermanos (probé esto solo en Angular 5 )

En su plantilla de componente principal:

<!-- Assigns "AppSibling1Component" instance to variable "data" --> <app-sibling1 #data></app-sibling1> <!-- Passes the variable "data" to AppSibling2Component instance --> <app-sibling2 [data]="data"></app-sibling2>

app-sibling2.component.ts

import { AppSibling1Component } from ''../app-sibling1/app-sibling1.component''; ... export class AppSibling2Component { ... @Input() data: AppSibling1Component; ... }


Actualizado a rc.4: cuando se intenta pasar datos entre componentes hermanos en angular 2, la forma más simple en este momento (angular.rc.4) es aprovechar la inyección de dependencia jerárquica de angular2 y crear un servicio compartido.

Aquí estaría el servicio:

import {Injectable} from ''@angular/core''; @Injectable() export class SharedService { dataArray: string[] = []; insertData(data: string){ this.dataArray.unshift(data); } }

Ahora, aquí estaría el componente PADRE

import {Component} from ''@angular/core''; import {SharedService} from ''./shared.service''; import {ChildComponent} from ''./child.component''; import {ChildSiblingComponent} from ''./child-sibling.component''; @Component({ selector: ''parent-component'', template: ` <h1>Parent</h1> <div> <child-component></child-component> <child-sibling-component></child-sibling-component> </div> `, providers: [SharedService], directives: [ChildComponent, ChildSiblingComponent] }) export class parentComponent{ }

y sus dos hijos

niño 1

import {Component, OnInit} from ''@angular/core''; import {SharedService} from ''./shared.service'' @Component({ selector: ''child-component'', template: ` <h1>I am a child</h1> <div> <ul *ngFor="#data in data"> <li>{{data}}</li> </ul> </div> ` }) export class ChildComponent implements OnInit{ data: string[] = []; constructor( private _sharedService: SharedService) { } ngOnInit():any { this.data = this._sharedService.dataArray; } }

niño 2 (es hermano)

import {Component} from ''angular2/core''; import {SharedService} from ''./shared.service'' @Component({ selector: ''child-sibling-component'', template: ` <h1>I am a child</h1> <input type="text" [(ngModel)]="data"/> <button (click)="addData()"></button> ` }) export class ChildSiblingComponent{ data: string = ''Testing data''; constructor( private _sharedService: SharedService){} addData(){ this._sharedService.insertData(this.data); this.data = ''''; } }

AHORA: Cosas a tener en cuenta al usar este método.

  1. Incluya solo el proveedor de servicios para el servicio compartido en el componente PADRE y NO los hijos.
  2. Aún debe incluir constructores e importar el servicio en los elementos secundarios.
  3. Esta respuesta se respondió originalmente para una versión beta angular 2 temprana. Sin embargo, todo lo que ha cambiado son las declaraciones de importación, por lo que es todo lo que necesita actualizar si utilizó la versión original por casualidad.

Sujetos de comportamiento. Escribí un blog sobre eso.

import { BehaviorSubject } from ''rxjs/BehaviorSubject''; private noId = new BehaviorSubject<number>(0); defaultId = this.noId.asObservable(); newId(urlId) { this.noId.next(urlId); }

En este ejemplo, estoy declarando un tema de comportamiento de noid de número de tipo. También es un observable. Y si "algo sucedió" esto cambiará con la nueva función () {}.

Entonces, en los componentes del hermano, uno llamará a la función, para hacer el cambio, y el otro se verá afectado por ese cambio, o viceversa.

Por ejemplo, obtengo la identificación de la URL y actualizo el noid del sujeto de comportamiento.

public getId () { const id = +this.route.snapshot.paramMap.get(''id''); return id; } ngOnInit(): void { const id = +this.getId (); this.taskService.newId(id) }

Y desde el otro lado, puedo preguntar si esa identificación es "lo que quiera" y tomar una decisión después de eso, en mi caso, si quiero eliminar una tarea, y esa tarea es la url actual, debe redirigirme a la casa:

delete(task: Task): void { //we save the id , cuz after the delete function, we gonna lose it const oldId = task.id; this.taskService.deleteTask(task) .subscribe(task => { //we call the defaultId function from task.service. this.taskService.defaultId //here we are subscribed to the urlId, which give us the id from the view task .subscribe(urlId => { this.urlId = urlId ; if (oldId == urlId ) { // Location.call(''/home''); this.router.navigate([''/home'']); } }) }) }


Esto no es exactamente lo que quieres, pero seguro que te ayudará

Me sorprende que no haya más información sobre comunicación de componentes <=> considere este tutorial de angualr2

Para la comunicación de componentes hermanos, sugeriría ir con sharedService . Sin embargo, también hay otras opciones disponibles.

import {Component,bind} from ''angular2/core''; import {bootstrap} from ''angular2/platform/browser''; import {HTTP_PROVIDERS} from ''angular2/http''; import {NameService} from ''src/nameService''; import {TheContent} from ''src/content''; import {Navbar} from ''src/nav''; @Component({ selector: ''app'', directives: [TheContent,Navbar], providers: [NameService], template: ''<navbar></navbar><thecontent></thecontent>'' }) export class App { constructor() { console.log(''App started''); } } bootstrap(App,[]);

Consulte el enlace en la parte superior para obtener más código.

Editar: Esta es una demostración muy pequeña. Ya mencionó que ya lo intentó con sharedService . Por lo tanto, considere este tutorial de angualr2 para obtener más información.