简体   繁体   中英

Observable not working correctly in nested AngularJS2 components

I am trying to implement a tree view component. This component uses itself recursively. I have gotten it to work properly and display all directories and subdirectories.

The problem i am facing is when dealing with events (item selected). All tree view components subscribe to the shared service event, as does the base component.

When i debug the code, i can see that at any given time, the service only has 1 listener to the event , which is wrong. This causes the base component to not receive any callbacks , as well as some other problems.

When clicking on a directory, i can see inside the shared service that the number of listeners is incorrectly set to 1.

Edit: I can see that the TreeViewService's constructor is called 4 times (instead of 1).

using AngularJS 2.0.0-rc.2

Here is the code.


TreeViewComponent

export class TreeViewComponent {
  @Input('items') items: TreeViewItem[] = [];
  @Input('item') item: TreeViewItem = new TreeViewItem();

  constructor(private treeViewService: TreeViewService) {
    let me = this;
    this.treeViewService.on('itemSelected', function(item) {
      me.item = item;
    });
  }

  treeViewItemSelected(item: TreeViewItem) {
    this.treeViewService.broadcast('itemSelected', item);
  }

treeview.component.html

 <ul class="treeview"> <li *ngFor="let selItem of items"> <a [class.file]="selItem.children.length == 0" [class.folder]="selItem.children.length > 0" [class.selected]="selItem.id == item.id" (click)="treeViewItemSelected(selItem)">{{selItem.title}}</a> <my-treeview [items]="selItem.children" *ngIf="selItem.children.length > 0"></my-treeview> </li> </ul> 

TreeViewService

import {Injectable} from '@angular/core';
import * as Rx from 'rxjs/Rx';

@Injectable()
export class TreeViewService {

  private listeners:any;
  private eventSubject: any;
  private events: any;

  constructor(){
    this.listeners = {};
    this.eventSubject = new Rx.Subject();
    this.events = Rx.Observable.from(this.eventSubject);

    this.events.subscribe(
      ({name, args}) => {
        if(this.listeners[name]) {
          for(let listener of this.listeners[name]) {
            listener(...args);
          }
        }
      }
    );
  }

  on(name, listener) {
    if(!this.listeners[name]) {
      this.listeners[name] = [];
    }

    this.listeners[name].push(listener);
  }

  broadcast(name, ...args) {
    this.eventSubject.next({
      name,
      args
    });
  }
}

BaseComponent

@Component({
  selector: 'my-article-form',
  template: require('./articleform.component.html'),
  styles: [require('./articleform.component.scss')],
  providers: [FileUploadService, TreeViewService],
  directives: [FileManagerComponent, TreeViewComponent]
})
.
.
.

  constructor(private fileUploadService:FileUploadService, private apiService:ApiService, private treeViewService:TreeViewService) {
    let me = this;
    this.treeViewService.on('itemSelected', function(item) {
      me.treeViewItem = item;
    });
  }

BaseComponent html

Selected: {{treeViewItem.title}}<br />
<my-treeview [items]="treeViewItems"></my-treeview>

The issue is that Services are instantiated once for every component. This works in a hierarchical manner bottom-up.

See answer by Günter Zöchbauer here: AngularJs 2 - multiple instance of service created

Service needs to be defined when bootstrapping the application and the only provided in the root component of all the other child components that need to share this same service instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM