Reputation: 46
In angular, i tried to make a sample service to pass an array, which would be used commonly by all components, and then one component adds new element to that array. But, that new element is not getting rendered in the app.component.html after i push it. I am kinda new to angular. Please help on this. The app.component.html is:
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<app-new-account></app-new-account>
<hr>
<app-account
*ngFor="let acc of myAccounts; let i = index"
[account]="acc"
[id]="i"></app-account>
</div>
</div>
</div>
The app.component.ts is :
import { Component, OnInit } from '@angular/core';
import { AccountsService } from './accounts.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [AccountsService]
})
export class AppComponent implements OnInit {
myAccounts: {name: string, status: string}[] = [];
constructor(private accountsService: AccountsService) {}
ngOnInit() {
this.myAccounts = this.accountsService.accounts;
}
}
The service(accounts.service.ts) is:
import { Injectable } from '@angular/core';
@Injectable()
export class AccountsService {
accounts = [
{
name: 'Master Account',
status: 'active'
},
{
name: 'Testaccount',
status: 'inactive'
},
{
name: 'Hidden Account',
status: 'unknown'
}
];
addAccount(acctName: string, acctStatus: string) {
console.log("inside add accnt");
this.accounts.push({name: acctName, status: acctStatus});
console.log(this.accounts);
}
updateStatus(id: number, status: string) {
this.accounts[id].status = status;
}
}
The sub-component account.component.html is:
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<h5>{{ account.name }}</h5>
<hr>
<p>This account is {{ account.status }}</p>
<button class="btn btn-default" (click)="onSetTo('active')">Set to 'active'</button>
<button class="btn btn-default" (click)="onSetTo('inactive')">Set to 'inactive'</button>
<button class="btn btn-default" (click)="onSetTo('unknown')">Set to 'unknown'</button>
</div>
</div>
The sub-component account.component.ts is:
import { Component, Input } from '@angular/core';
import { LoggingService } from '../logging.service';
import { AccountsService } from '../accounts.service';
@Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css'],
providers: [LoggingService]
})
export class AccountComponent {
@Input() account: {name: string, status: string};
@Input() id: number;
constructor(private loggingService: LoggingService, private accountsService: AccountsService) {}
onSetTo(status: string) {
this.accountsService.updateStatus(this.id, status);
this.loggingService.logStatusChange(this.account.status);
}
}
The sub-component new-account.component.html is:
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div class="form-group">
<label>Account Name</label>
<input
type="text"
class="form-control"
#accountName>
</div>
<div class="form-group">
<select class="form-control" #status>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="hidden">Hidden</option>
</select>
</div>
<button
class="btn btn-primary"
(click)="onCreateAccount(accountName.value, status.value)">
Add Account
</button>
</div>
</div>
The sub-component new-account.component.ts is:
import { Component } from '@angular/core';
import { LoggingService } from '../logging.service';
import { AccountsService } from '../accounts.service';
@Component({
selector: 'app-new-account',
templateUrl: './new-account.component.html',
styleUrls: ['./new-account.component.css'],
providers: [LoggingService]
})
export class NewAccountComponent {
constructor(private loggingService: LoggingService, private accountsService: AccountsService) {}
onCreateAccount(accountName: string, accountStatus: string) {
this.accountsService.addAccount(accountName, accountStatus);
this.loggingService.logStatusChange(accountStatus);
}
}
Upvotes: 0
Views: 2419
Reputation: 1783
as long as you don't use OnPush
Change-Detection-Strategy it should work as you described. I copied your code into stackblitz and I think its working:
https://stackblitz.com/edit/angular-3dxw8d
Have a nice weekend
Upvotes: 0
Reputation: 16847
It's a change detection problem. You must change the reference to an array to trigger the change detection.
In service file:
addAccount(acctName: string, acctStatus: string) {
this.accounts = [...this.accounts, {name: acctName, status: acctStatus}]
}
updateStatus(id: number, status: string) {
this.accounts = this.accounts.map((acc, i) => i === id ? { ...acc, status } : acc))
}
In component file:
get myAccounts(): {name: string, status: string}[] {
return this.accountsService.accounts;
}
constructor(private accountsService: AccountsService) {}
You should get accustomed to working with immutable data (always returning new copy, instead of mutating the original value), or you will end up with a log of weird bugs with angular change detection.
The good rule of thumb is: If your console.log shows the correct value, but the template is not updated, it's a change detection problem.
Upvotes: 1