В Angular 1.xx вы просто запрашиваете одну и ту же службу, и в итоге вы получаете один и тот же экземпляр, что позволяет обмениваться данными в службе.
Теперь в Angular 2 у меня есть компонент, который ссылается на мою службу. Я могу читать и изменять данные в сервисе, и это хорошо. Когда я пытаюсь внедрить ту же службу в другой компонент, мне кажется, что я получаю новый экземпляр.
Что я делаю неправильно? Это неправильный шаблон (использование службы для обмена данными), или мне нужно пометить службу как синглтон (в одном экземпляре приложения) или что-то в этом роде?
Я на 2.0.0-alpha.27/
BTW
Я добавляю службу через appInjector
(изменить: сейчас providers
) в @Component
аннотации, а затем сохраняю ссылку в конструкторе. Он работает локально в компоненте, а не между компонентами (они не используют один и тот же экземпляр службы), как я думал.
ОБНОВЛЕНИЕ : Начиная с Angular 2.0.0, у нас теперь есть @ngModule, где вы определяете службу в providers
свойстве, указанном @ngModule
. Это обеспечит передачу одного и того же экземпляра этой службы каждому компоненту, службе и т. Д. В этом модуле.
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
ОБНОВЛЕНИЕ : в разработке Angular и FE в целом произошло многое. Как упоминал @noririco, вы также можете использовать систему управления состоянием, такую как NgRx: https://ngrx.io/
источник
Ответы:
Одноэлементный сервис - хорошее решение. Другой способ -
data/events bindings
.Вот пример того и другого:
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);
Смотреть вживую
источник
viewInjector
.providers: [MyService]
. Удалив провайдеров, он стал единственным экземпляром приложенияКомментарий @maufarinelli заслуживает отдельного ответа, потому что, пока я его не увидел, я все еще бил головой об стену с этой проблемой даже с ответом @Alexander Ermolov.
Проблема в том, что когда вы добавляете
providers
в свойcomponent
:@Component({ selector: 'my-selector', providers: [MyService], template: `<div>stuff</div>` })
Это приводит к внедрению нового экземпляра вашей службы ... вместо того, чтобы быть синглтоном .
Поэтому удалите все экземпляры your
providers: [MyService]
в своем приложении, кроме как вmodule
, и он будет работать!источник
Вы должны использовать входы и выходы декоратора @Component. Вот самый простой пример использования обоих;
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);
источник
ngFor
,В шаблоне родительского компонента:
<hero-child [hero]="hero"> </hero-child>
В дочернем компоненте:
источник
Есть много способов. Это пример использования распространения между родительскими и дочерними элементами. Это очень эффективно.
Я представил пример, который позволяет увидеть использование двух способов привязки данных в двух формах. Если бы кто-нибудь мог предоставить образец plunkr, это было бы очень хорошо ;-)
Вы можете найти другой способ, обратившись к поставщику услуг. Вы также можете посмотреть это видео для справки: ( Обмен данными между компонентами в Angular )
mymodel.ts (данные для обмена)
// 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; } }
Помните: должен быть родитель, который будет использовать mymodel для дочерних компонентов.
Родительский компонент
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() { } }
Дочерний компонент, 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() { } }
Примечание. В некоторых редких случаях при анализе HTML-кода может возникнуть ошибка, поскольку модель не «готова» к использованию при инициализации страницы. В этом случае добавьте к HTML-коду префикс ngIf:
<div *ngIf="model"> {{model.data1}} </div>
источник
Это зависит, если есть простой случай
a) A -> B -> C У A есть два дочерних элемента B и C, и если вы хотите обмениваться данными между A и B или A и C, используйте (ввод / вывод)
Если вы хотите поделиться между B и C, вы также можете использовать (ввод / вывод), но рекомендуется использовать Service.
б) Если дерево большое и сложное. (если существует так много уровней родительских и дочерних соединений.) И в этом случае, если вы хотите поделиться данными, я бы предложил ngrx
Он реализует архитектуру потока, которая создает хранилище на стороне клиента, на которое любой компонент может подписаться и может обновляться без создания каких-либо условий гонки.
источник