Angular 4: View is not updating after model change?
Solution 1
I believe the reason you are struggling is because it seems like you have sibling components trying to manipulate data in which neither one is truly the owner of the data. Angular tries to enforce Unidirectional Data Flow which simply means data flows from the parent to the child instead of the child to the parent. The simplest way to solve your issue would be to have parent component that "owns" the userData and the child components bind to the data to display it and emit events to manipulate it.
Using your scenario as an example:
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public userData: any = {
score: 2000
};
updateScore(score: number): void {
this.userData.score += score;
}
}
app.component.html
<app-header [score]="userData.score"></app-header>
<app-test (onScoreChange)="updateScore($event)"></app-test>
header.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
@Input('score') score: number;
constructor() { }
ngOnInit() {
}
}
header.component.html
<span class="topbar-details">
Scrore : {{score}}
</span>
test.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
@Output('onScoreChange') onScoreChange = new EventEmitter<number>();
constructor() { }
ngOnInit() {
}
ansSubmit() {
this.onScoreChange.emit(54321);
}
}
test.component.html
<div class="col-2">
<button (click)="ansSubmit()" >
<div>Submit Answer</div>
</button>
</div>
Note: As far as loading the user information from local storage, I would use a service to handle that. Where and how the user data gets loaded is not really a concern of the components that are displaying and manipulating the data. Moving that logic to a service with the proper interface (likely returning observables if the data could ever possibly come from a remote location) will allow you to seamlessly swap out a local storage based service for an HTTP based service to force all scores to be transmitted to/from a server without changing a line of code in your component. It would also allow you to swap in a mock service for testing so local storage does not need to be seeded in order for the test to pass (i.e. - your tests have to know less about the implementation of your component to pass, so they will not be as fragile upon implementation changes)
Solution 2
You are adding HeaderComponent as a provider, so it's probably creating a new instance of the component. If you have an existing HeaderComponent, you will need to get ahold of that instance, so using a service would be helpful to keep a reference to the component instance.
It would be best to move the score to the service altogether and use a Subject to update its value. Check out their documentation: https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service. Really any strategies listed in that documentation should be useful for your situation.
Related videos on Youtube
Shubham Verma
I am a tech junky and a blogger. Worked on NodeJS, ExpressJS, AngularJS, Angular2/4/5/6, ReactJS, MySql, MongoDB, PostgreSQL etc Click here for my blogs Click here for my Youtube Channel: GuitarAnAddiction Other Blogs
Updated on June 04, 2022Comments
-
Shubham Verma almost 2 years
I have two
component
in my app ie:HeaderComponent
&TestComponent
. InHeaderComponent
, There is a method name setUserData(). I am calling this setUserDate() from TestComponent's methodansSubmit()
which is in filetest.component.ts
.Now
setUserDate()
is calling and value is coming insetUserDate()
method.The problem is : when
setUserDate()
calling , I am setting score value into this.userData.score, And this.userData.score value is binding in view ( HTML ) but the coming value from TestComponent's method ansSubmit() is not going to update on the view but the value is present in thets
file ( I am printing in console).Here is the code:
test.component.ts:
import { HeaderComponent } from '../../components/header/header.component'; @Component({ selector: 'test-page', templateUrl: './test.component.html', styleUrls: ['./test.component.css'], providers: [HeaderComponent] }) export class TestComponent implements OnInit { private userData: any = {}; constructor(private _router: Router, private headerComponent: HeaderComponent) { } ansSubmit() { const logged_in_user = new LocalStorage(environment.localStorageKeys.ADMIN); this.userData = logged_in_user.value; this.userData['score'] = parseInt(this.userData.score) + 54321 this.headerComponent.getUserData(this.userData['score']); // Calling HeaderComponent's method value is 54321. } }
test.component.html:
<div class="col-2"> <button (click)="ansSubmit()" > <div>Submit Answer</div> </button> </div>
header.component.ts:
import { Component, OnInit, ChangeDetectorRef, NgZone, ApplicationRef, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'], }) export class HeaderComponent implements OnInit { public userData : any = { score:2000, }; getUserData(score) { // score= 54321 ( value is coming ) this.userData.score = score ; console.log(this.userData.score); // in this.userData.score = 54321 ( Value is assigning ) } } }
header.component.html:
<span class="topbar-details"> Scrore : {{userData.score }} // actual problem is here, Its not updating value. Its only showing 2000. </span>
Please suggest me how to resolve this issue. Thanks in advance.
-
Sajeetharan almost 6 yearsdifferent component?
-
Shubham Verma almost 6 yearsyes, I am calling this method from a different component. By using : this.headerComponent.getUserData(this.userData['score']);
-
Shashank Vivek almost 6 yearscan u add some code in stackblitz.com and share it
-
-
Shubham Verma almost 6 yearsI tried the same, But it's not updating the old value on the view.
-
Shubham Verma almost 6 yearsThis is not a parent & child component.
-
Hyuck Kang almost 6 yearsOh, did you also try to set new value to old value in callback function in
this._ngZone.run
? -
dAxx_ almost 6 yearsSo please provide more information ... its not clear what you are trying to do and how.
-
Shubham Verma almost 6 yearsyes, I tried this._ngZone.run, But its also not updating the model on the view.
-
Hyuck Kang almost 6 yearsThen, I need more information to solve it. Could you share more details?
-
Shubham Verma over 5 yearsHi @dAxx I have updated my question have a look and response accordingly.
-
Shubham Verma over 5 yearsHi @Hyuck Kang I have updated my question please have a look and response accordingly.
-
Shubham Verma over 5 yearsI tried with ngOnChanges() but it's not working. And I don't need parent-child component, So I don't want to implement parent-child component. Both are the different component.
-
Damodharan J over 5 yearsIf they are different components, you can use a angular service, inject it in your class and use it. This simplifies so many things. Have a common data in the service and use it to render in the component. Will work for even unrelated components.
-
Shubham Verma over 5 yearsHi @ dAxx_ It's not emitting the setUserData() when this.userDataEvent.emit(this.score); called.
-
Hyuck Kang over 5 years@Shubham Verma I updated my anwser. Please read and try it. I will wait for your feedback.
-
David over 5 yearsI agree with moving the functionality to a service. Another solution would be to move the functionality to a parent component and utilize event @Output to get the render engine detection the changes properly.