简体   繁体   中英

Angular 2 / 4 Observables Unwanted Two-Way Binding

(I'm refactoring my question because I think the first one was super confusing...)

I'm trying to get my head around how Observables work in Angular 2 / 4. Specifically, I want to subscribe to a service's Observable when a component initialises, and later in the component's lifecycle I want to modify the data without it affecting the observable or other subscribers. I'm storing the data in a variable on the component, but when I modify it, the Observable's other subscribers also show the changes.

In my code there are three observables: clients, projects, and user. Right now I'm just trying to modify data from the projects Observable. I'm using the data as a source for ng2-ui autocomplete, so I can't use *ngFor as suggested in the comment below.

common.service.ts (Create the observable)

import { Observable } from 'rxjs'
import { BehaviorSubject } from 'rxjs';

export class UserService {
  public clients: BehaviorSubject<any> = new BehaviorSubject(null);
  public projects: BehaviorSubject<any> = new BehaviorSubject(null);
  public user: BehaviorSubject<any> = new BehaviorSubject(null);

  constructor(){};

}

login.ts (subscribe to the Observable, get the data from the API, then pass it to the Observables)

ngOnInit() {
  this.userService.clients.subscribe();
  this.userService.projects.subscribe();
}

userLogin() {
  this.commonService.request('login', 'post', '', {password: this.password, email: this.email})
    .then((response:any) => {
      if(response.token) {
        this.configService.token = response.token;
        if(response.clients) this.userService.clients.next(response.clients);
        if(response.projects) this.userService.projects.next(response.projects);
        this.configService.loggedIn = true;
        this.router.navigate(['dashboard']);
      }
    })
}

component.ts (subscribe, get the data from the Observable and then modify it.)

ngOnInit() {
  this.userService.clients.subscribe((clients) => {
    this.clients = clients;
  });
  this.userService.projects.subscribe((projects) => {
    this.projects = projects;
  });
  this.filteredProjects = this.projects;
  this.filteredClients = this.clients;
}

filterProjects(client) { // When a client is selected
  this.filteredProjects.forEach((project) => {
    if(client.projects.indexOf(project._id) == -1) {
      filteredProjects.splice(project._id, 1) // this splices the filteredProjects array, but also this.projects and any other subscriptions to the observable in the app.
    }
  });
}

When I filter this.filteredProjects, this.projects also get modified. I can see this with console.logs of both objects, and also because another component is subscribing to the projects observable, and this other component also shows the changes.

However if I don't filter this.filteredProjects, but instead I just say this.filteredProjects = []; then this.projects remains the same.

How can I modify data from an observable without affecting the observable or its other subscribers?

I'm not completely sure about what your trying to accomplish by looking at your code, but ill try to maybe give you a better direction to implement what your trying to do.

From my understanding, you want a general observable that holds your projects. I assume you already got that working (you didn't out that in your code though). Now, in your component, you want to get a subscription to it first. There are a few ways to work with the subscription, the way I see your trying to do is to subscribe to it, the problem with that approach is that it will take you a lot of work. First you will want to work against a copy of the projects array, because right now you said you change the projects in all of your code because you work vs a reference of the same projects array everywhere (There are number of ways to get a copy, one of them that comes to mind is using concat with an empty array for example).

After you did that, you will also need to know which client the user has picked, so when ever the subscription is emitted again (I assume from what your saying that the projects list can change outside of this component) you will have to get a new copy again and then filter it again. This could work and its closest to what your described.

I think you should also look for a better way to do it, like using ngFor with an async pipeline in the html to iterate over the projects in the subscription ( async pipe, can be used with ngfor . The next thing you could do is use a custom filter in the html to filter the results by the selected client.

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