'How to detect change from one component into other
Angular 4. Github source
I have a menu which is filled by a web service. The web service is in taskService, but is not necessary now.
ngOnInit() {
this.getTasks();
}
getTasks(): void {
this.taskService.getTasks()
.subscribe(Tasks => this.tasks = Tasks);
}
When you click on a task it loads a page, a different component, with a form ready to update the data. It is also made by a web service and works fine. The problem is that after update the task, it is not reflected in the task menu
I am importing this:
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
and adding this to the constructor:
private cdRef: ChangeDetectorRef
And this is my best approach to the detectChanges() function, after update the data with the save() function
this.taskService.updateTask(task, id)
.subscribe(
this.Ref.detach();
setInterval(() => {
this.Ref.detectChanges();
}, 5000);
);
This is the html from the menu to print the tasks:
<li *ngFor="let task of tasks" class="d-inline-block col-md-12">
<a routerLink="/task/{{task.id}}" > {{task.title}}</a>
<!-- <span class="close big"></span> -->
<button class="close big" title="delete task"
(click)="delete(task)">x</button>
</li>
And this is the form that updates the task
<form (ngSubmit)="save(taskName.value, taskBody.value)" #taskForm="ngForm" class="example-form">
<mat-form-field class="example-full-width">
<label>Task Name</label>
<input matInput [(ngModel)]="task.name" #taskName name="name">
</mat-form-field>
<mat-form-field class="example-full-width">
<textarea matInput [(ngModel)]="task.body" #taskBody name="body"></textarea>
</mat-form-field>
<button type="submit" class="btn btn-success" >Save</button>
</form>
Both are in different components.
I have tried to follow this tutorial, but I'm stuck, I don't know how to use the ChangeDetectorRef.
Solution 1:[1]
I've looked at your code. The problem is that the view-task.component
updates your tasks, but the navigation.component
is not notified about this transaction. I think BehaviorSubject
might be just the thing for you.
You can read more about it here
I assume you would have single array of tasks
throughout your application and you would display them on your navigation
component.
Task.service.ts
export class TaskService {
// behaviorSubject needs an initial value.
private tasks: BehaviorSubject = new BehaviorSubject([]);
private taskList: Task[];
getTasks() {
if (!this.taskList || this.taskList.length === 0) {
this.initializeTasks();
}
return this.tasks.asObservable();
}
initializeTasks() {
this.http.get('api/tasks')
.subscribe(tasks => {
// next method will notify all of the subscribers
this.tasks.next(tasks);
}, error => {
// proper error handling here
});
}
updateTasks(task: Task) {
this.http.post('api/updateTask')
.subscribe(resp => {
// update your tasks array
this.tasks = ...
// and call next method of your behaviorSubject with updated list
this.tasks.next(this.tasks);
}, error => {
// proper error handling here
});
}
}
Navigation.component.ts
export class NavigationComponent implements OnInit{
tasks: Task[];
constructor(private taskService: TaskService) {}
ngOnInit() {
// this method will be called every time behaviorSubject
// emits a next value.
this.taskService.getTasks()
.subscribe(tasks => this.tasks = tasks);
}
}
View-task.component.ts
export class ViewTaskComponent {
constructor(private taskService: TaskService) {}
updateTask(task: Task) {
this.taskService.updateTask(task);
}
}
I haven't tried this code myself. However, I have implemented something similar on my application before. So when you try it and have a problem, let me know.
Solution 2:[2]
Angular isn't running changeDetection
because you aren't asking it to. None of the instance variables in your ViewTaskComponent
are being updated inside your save
method.
In your code, after updateTask()
finishes, it returns to save()
in your ViewTaskComponent
. But there is no link to this.task
in the subscription callback to this.taskService.updateTask()
.
Since updateTask
uses a PATCH request, I'm assuming you're not getting the whole Task
object back. So you won't be able to just say this.task = valuePassedToSubscribeCallback
.
Instead you can invoke this.viewTask()
in that subscribe callback and fetch the entire updated Task
object.
For example:
this.taskService.updateTask(task, id)
.subscribe(
// new line here to fetch the updated Task object
this.viewTask()
);
Note: I'd second @Bunyamin Coskuner's suggestion to utilize BehaviorSubject
's (or even a standard Subject
). If you're up for a refactor, they are a clean way to manage state within your services.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | bazzells |