En Angular 1.xx, simplemente solicita el mismo servicio y termina con la misma instancia, lo que hace posible compartir los datos en el servicio.
Ahora en Angular 2 tengo un componente que tiene una referencia a mi servicio. Puedo leer y modificar los datos en el servicio, lo cual es bueno. Cuando intento inyectar el mismo servicio en otro componente, parece que obtengo una nueva instancia.
¿Qué estoy haciendo mal? ¿Es el patrón en sí el que está mal (usar un servicio para compartir datos) o necesito marcar el servicio como singleton (dentro de una instancia de la aplicación) o algo así?
Estoy en por 2.0.0-alpha.27/
cierto
Inyecto un servicio a través de appInjector
(editar: ahora providers
) en la @Component
anotación y luego guardo una referencia en el constructor. Funciona localmente en el componente, pero no entre componentes (no comparten la misma instancia de servicio) como pensé que lo harían.
ACTUALIZACIÓN : A partir de Angular 2.0.0 ahora tenemos @ngModule donde definiría el servicio bajo la providers
propiedad en dicho @ngModule
. Eso asegurará que la misma instancia de ese servicio se pase a cada componente, servicio, etc. en ese módulo.
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
ACTUALIZACIÓN : Mucho ha sucedido con el desarrollo de Angular y FE en general. Como mencionó @noririco, también podría usar un sistema de gestión estatal como NgRx: https://ngrx.io/
fuente
Respuestas:
Un servicio singleton es una buena solución. De otra manera -
data/events bindings
.A continuación, se muestra un ejemplo de ambos:
class BazService{ n: number = 0; inc(){ this.n++; } } @Component({ selector: 'foo' }) @View({ template: `<button (click)="foobaz.inc()">Foo {{ foobaz.n }}</button>` }) class FooComponent{ constructor(foobaz: BazService){ this.foobaz = foobaz; } } @Component({ selector: 'bar', properties: ['prop'] }) @View({ template: `<button (click)="barbaz.inc()">Bar {{ barbaz.n }}, Foo {{ prop.foobaz.n }}</button>` }) class BarComponent{ constructor(barbaz: BazService){ this.barbaz = barbaz; } } @Component({ selector: 'app', viewInjector: [BazService] }) @View({ template: ` <foo #f></foo> <bar [prop]="f"></bar> `, directives: [FooComponent, BarComponent] }) class AppComponent{} bootstrap(AppComponent);
Ver en directo
fuente
viewInjector
.providers: [MyService]
. Al eliminar los proveedores, se convirtió en la única instancia de la aplicaciónEl comentario de @maufarinelli merece su propia respuesta porque hasta que lo vi, todavía estaba golpeándome la cabeza contra la pared con este problema incluso con la respuesta de @Alexander Ermolov.
El problema es que cuando agregas un
providers
a tucomponent
:@Component({ selector: 'my-selector', providers: [MyService], template: `<div>stuff</div>` })
Esto hace que se inyecte una nueva instancia de su servicio ... en lugar de ser un singleton .
Así que elimine todas las instancias de su
providers: [MyService]
en su aplicación, excepto en elmodule
, ¡y funcionará!fuente
Debe utilizar entradas y salidas de un decorador de @Component. Aquí está el ejemplo más básico de usar ambos;
import { bootstrap } from 'angular2/platform/browser'; import { Component, EventEmitter } from 'angular2/core'; import { NgFor } from 'angular2/common'; @Component({ selector: 'sub-component', inputs: ['items'], outputs: ['onItemSelected'], directives: [NgFor], template: ` <div class="item" *ngFor="#item of items; #i = index"> <span>{{ item }}</span> <button type="button" (click)="select(i)">Select</button> </div> ` }) class SubComponent { onItemSelected: EventEmitter<string>; items: string[]; constructor() { this.onItemSelected = new EventEmitter(); } select(i) { this.onItemSelected.emit(this.items[i]); } } @Component({ selector: 'app', directives: [SubComponent], template: ` <div> <sub-component [items]="items" (onItemSelected)="itemSelected($event)"> </sub-component> </div> ` }) class App { items: string[]; constructor() { this.items = ['item1', 'item2', 'item3']; } itemSelected(item: string): void { console.log('Selected item:', item); } } bootstrap(App);
fuente
ngFor
,En la plantilla de componente principal:
<hero-child [hero]="hero"> </hero-child>
En el componente hijo:
fuente
Hay muchas maneras. Este es un ejemplo que utiliza la propagación entre elementos padre e hijo. Esto es muy eficaz.
Envié un ejemplo que permite ver el uso de dos formas de enlace de datos dentro de dos formas. Si alguien puede proporcionar una muestra de plunkr, sería muy bueno ;-)
Puede buscar otra forma utilizando un proveedor de servicios. También puede ver este video como referencia: ( Compartir datos entre componentes en Angular )
mymodel.ts (datos para compartir)
// Some data we want to share against multiple components ... export class mymodel { public data1: number; public data2: number; constructor( ) { this.data1 = 8; this.data2 = 45; } }
Recuerde: debe haber un padre que comparta "mymodel" con los componentes secundarios.
Componente padre
import { Component, OnInit } from '@angular/core'; import { mymodel } from './mymodel'; @Component({ selector: 'app-view', template: '<!-- [model]="model" indicates you share model to the child component --> <app-mychild [model]="model" > </app-mychild>' <!-- I add another form component in my view, you will see two ways databinding is working :-) --> <app-mychild [model]="model" > </app-mychild>', }) export class MainComponent implements OnInit { public model: mymodel; constructor() { this.model = new mymodel(); } ngOnInit() { } }
Componente hijo, mychild.component.ts
import { Component, OnInit,Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; // <-- NgModel lives here import { mymodel } from './mymodel'; @Component({ selector: 'app-mychild', template: ' <form #myForm="ngForm"> <label>data1</label> <input type="number" class="form-control" required id="data1 [(ngModel)]="model.data1" name="data1"> <label>val {{model.data1}}</label> label>data2</label> <input id="data2" class="form-control" required [(ngModel)]="model.data2" name="data2" #data2="ngModel"> <div [hidden]="data2.valid || data2.pristine" class="alert alert-danger"> data2 is required </div> <label>val2 {{model.data2}}</label> </form> ', }) export class MychildComponent implements OnInit { @Input() model: mymodel ; // Here keywork @Input() is very important it indicates that model is an input for child component constructor() { } ngOnInit() { } }
Nota: En algunos casos raros, es posible que tenga un error cuando se analiza el código HTML, porque el modelo no está "listo" para usar en la inicialización de la página. En este caso, anteponga el código HTML con una condición ngIf:
<div *ngIf="model"> {{model.data1}} </div>
fuente
Depende, si hay un caso sencillo
a) A -> B -> C A tiene dos hijos B y C y si desea compartir datos entre A y B o A y C, utilice (entrada / salida)
Si desea compartir entre B y C, también puede usar (entrada / salida), pero se sugiere usar Service.
b) Si el árbol es grande y complejo. (si hay tantos niveles de conexiones entre padres e hijos). Y en este caso, si desea compartir datos, sugeriría ngrx
Implementa la arquitectura de flujo que crea una tienda del lado del cliente a la que cualquier componente puede suscribirse y puede actualizar sin crear ninguna condición de carrera.
fuente