JPT JPT - 3 years ago 298
TypeScript Question

How to bind an input parameter of an Angular4 component

I have a small web site I am developing in Angular4 (my first attempt in Angular) and have come across an issue I just can seem to figure out. Simplified down my scenario is:

I have a component (account-list) which using a html5 select/option control which is populated from a rest api and displays a list of accounts.

I have a second component which displays the details of the account (account-detail) and take accountId as an input parameter.

When an account is selected in the account-list component I want the account-detail to auto update to the newly selected account. I know that my account-list component is working fine and that when I select an account the selectAccountId variable in the ts is being updated.

However I just can't seem to get the update to the selectAccountId variable to trigger an update to the account-detail component. I know this component works fine on it own as a default account is displayed and the id of this default is set in the ngOnInit method of the account-list component.

The relevant html5 in the account-list component:

<select id="Id" #Id="ngModel" class="hideLabel form-control" [(ngModel)]="selectedAccountId" name="Id">
<option [ngValue]="account.Id" *ngFor="let account of accounts">  
{{account.Name}}  
</option>  
</select>  

<!-- this div just to prove that selectedAccountId changes when a new account is selected, which it does -->
<div *ngIf="selectedAccountId">
{{selectedAccountId}}
</div>

<!-- this is the line which does not seem to work, changes to selectedAccountId are not triggering the component to refresh -->
<account-detail *ngIf="selectedAccountId" [account]="selectedAccountId"></account-detail>


The ts code of the account-list component:

export class AccountListComponent implements OnInit {
selectedAccountId: number;

title: string;
accounts: AccountHeader[];
errorMessage: string;

constructor(private accountService: AccountService, private router: Router) { }

ngOnInit() {
var s = this.accountService.getLatest();

s.subscribe(
accounts => {
this.accounts = accounts; this.selectedAccountId = this.accounts[0].Id;
},
error => this.errorMessage = <any>error
);
}
}


The ts code of the account-detail component:

export class AccountDetailComponent {
@Input("account") selectedAccountId: number;
account: Account;

selectedLicense: License;

constructor(
private authService: AuthService,
private accountService: AccountService,
private router: Router,
private activatedRoute: ActivatedRoute) {
}

ngOnInit() {
if (this.selectedAccountId) {
this.accountService.get(this.selectedAccountId).subscribe(
account => this.account = account
);
}

else {
this.router.navigate([""]);
}
}
}


In all honesty I've lost track of the things I've tried to make this work, most of the blogs, guides etc I've read talk about how to bind the other way and I have all that working just fine. But I can't find how to get the binding to trigger an update of the account-detail component which should be accomplished by this line:

<!-- this is the line which does not seem to work, changes to selectedAccountId are not triggering the component to refresh -->
<account-detail *ngIf="selectedAccountId" [account]="selectedAccountId"></account-detail>


I've been following a book in which this was working fine, but originally this was working in AngularJS and then migrated to Angular2 (then 4) and somewhere along the way this has stopped working.

Any help would be much appreciated, thanks!

Answer Source

So in your account-detail component you are using the ngOnInit method to fetch some data depending on your selected account id. ngOnInit is a lifecycle hook that is called once when a component is created. When you change the selected id, angular is not recreating the component and the method does not fire.

What you need is a method that fire when you change the variable. There are several approaches you can use, check out this comprehensive post for more information.

Here is a simple example using a property setter to trigger a method call when the input is changed:

export class AccountDetailComponent {
    selectedAccount: Account;

    private _selectedAccountId: number;
    @Input("account") set selectedAccountId(value: number) {
       this._selectedAccountId = value;
       this.getAccount();
    }

    getAccount() {
        if (this._selectedAccountId) {
            this.accountService.get(this._selectedAccountId).subscribe(
                account => this.selectedAccount = account
            );
        } 
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download