I have two component
in my app ie: HeaderComponent
& TestComponent
. In HeaderComponent
, There is a method name setUserData().
I am calling this setUserDate() from TestComponent's method ansSubmit()
which is in file test.component.ts
.
Now setUserDate()
is calling and value is coming in setUserDate()
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 the ts
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.
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)