Как обмениваться данными/изменениями между компонентами

Допустим, у вас есть интерфейс с панелью инструментов, боковой панелью и сеткой. На панели инструментов есть раскрывающийся список, в котором при изменении пользователя изменяется содержимое боковой панели и сетки. Вернувшись в Angular 1, я бы использовал Сервис, чтобы получить все свои динамические данные. Когда что-то меняется в службе, все компоненты, которые используют эту службу, также будут обновляться.

В Angular 2 похоже, что люди используют разные методы. Я хотел получить ваш вклад, который является предпочтительным способом.

  • Статическая служба
  • OnChanges
  • Входы и выходы

У меня остается вопрос: лучше ли создавать новую службу для каждого элемента данных, который используется совместно для компонентов, или же у нас может быть только одна служба, в которой есть объект, в котором хранятся все общие данные?

Original Plunker - Каждое изменение будет иметь свой собственный сервис

app.component.ts

import {Component} from 'angular2/core';
import {NavService} from '../services/NavService';

@Component({
  selector: 'obs-comp',
  template: 'obs component, item: {{item}}'
})
export class ObservingComponent {
  item: number;
  subscription: any;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.item = this._navService.navItem();
    this.subscription = this._navService.navChange$.subscribe(
      item => this.selectedNavItem(item));
  }
  selectedNavItem(item: number) {
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:'
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
  ',
})
export class Navigation {
  item = 1;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

@Component({
  selector: 'my-app',
  template: '{{title}}
  <p>
  <my-nav></my-nav>
  <button (click)="showObsComp = !showObsComp">toggle ObservingComponent</button>
  <div *ngIf='showObsComp'>
    <obs-comp></obs-comp>
  </div>
  ',
  directives: [Navigation, ObservingComponent]
})
export class AppComponent {
  title = "Angular 2 - event delegation";
  showObsComp = true;
  constructor() { console.clear(); }
}

NavService.ts:

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';

export class NavService {
  private _navItem = 0;
  navChange$: Observable<number>;
  private _observer: Observer;
  constructor() {
    this.navChange$ = new Observable(observer =>
      this._observer = observer).share();
    // share() allows multiple subscribers
  }
  changeNav(number) {
    this._navItem = number;
    this._observer.next(number);
  }
  navItem() {
    return this._navItem;
  }
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>User Input</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://code.angularjs.org/2.0.0-beta.11/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/tools/system.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.11/Rx.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.11/angular2.dev.js"></script>
    <script>
      System.config({
        transpiler: 'typescript', 
        typescriptOptions: { emitDecoratorMetadata: true }, 
        packages: {
          app:      {defaultExtension: 'ts'},
          services: {defaultExtension: 'ts'},
        } 
      });
      System.import('app/boot')
            .then(null, console.error.bind(console));
    </script>
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>

</html>

приложение /boot.ts

import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {NavService} from '../services/NavService';

bootstrap(AppComponent, [NavService]);

Revised Plunker for example - только один сервис, который хранит все данные в объекте. Тип будет передан каждому слушателю, чтобы проверить, нужно ли ему что-либо делать на основе этого типа.

Ответы

Ответ 1

Вы можете использовать общий сервис для этого. Он может содержать как данные, так и наблюдаемые, на которые можно подписаться, чтобы получать уведомления при обновлении данных.

  • Услуги

    export class ListService {
      list1Event: EventEmitter<any> = new EventEmitter();
    
      getLists() {
        return this.http.get(url).map(res => res.json())
          .subscribe(
            (data) => {
              this.list1Event.emit(data.list1);
            }
          );
      }
    }
    
  • Компонент

    @Component({
      selector: 'my-component1',
      template: '
        <ul>
         <li *ngFor="#item of list">{{item.name}}</li>
        </ul>
      '
    })
    export class MyComponent1 {
      constructor(private service:ListService) {
        this.service.list1Event.subscribe(data => {
          this.list = data;
        });
      }
    }
    
  • самонастройки

    bootstrap(AppComponent, [ ListService ]);
    

Смотрите этот вопрос для более подробной информации:

Ответ 2

В вашем случае использования я использовал бы службы. Службы используются для передачи своих данных другим компонентам. Один компонент мог обновить эти данные для службы, а другой компонент мог бы прочитать ее. Или оба компонента могли просто читать из него, а сам сервер получает данные из "внешнего мира".

Вы используете input для передачи данных от родителя к дочернему элементу, и вы используете output для вывода событий из дочернего элемента в родительский.

Вы используете ngOnChanges, чтобы что-то делать, когда что-то изменяется в самом компоненте, но я предпочитаю использовать для этого функции get() и set().

По крайней мере, это мое занятие:)