Angular 4 - Custom two way binding - html

I am trying to implement a custom two way binding between two of my components.
I did read about the naming convention saying that you have to define a #Input() like "test" and then define a #Output() named "testChange".
I couldn't find anything about whether this is still up-to-date or not and I can't get my binding to work.
Some code within parentComponent:
<my-comp [(userGroupIds)]="userGroups"></my-comp>
MyComponent (child):
export class MyComponent implements OnInit, OnDestroy{
#Input() userGroupIds: number[];
#Output() userGroupIdsChange = new EventEmitter<number[]>();
updateValues(){
//here I am iterating through the rows of a table within my component
this.userGroupIds = this.tableRows.map(item => {return item['id']});
this.userGroupdIdsChange.emit(this.userGroupIds);
}
}
parentComponent:
export class parentComponent implements OnInit, OnChanges, OnDestry{
userGroups: number[];
constructor(){
this.userGroups = [];
}
ngOnChanges(changes: SimpleChanges){
if(changes['userGroups']){
// this is never show, ngOnChanges doesn't get fired;
console.log(this.userGroups);
}
}
}
Is there something I am missing? Did the Angular-Team change anything?
Afaik the binding does something like
[userGroupsIds]="userGroups" (userGroupsIdsChange)="userGroupIds=$event"
So I tried setting this myself, but no update either. Only thing that work was passing a function to the eventEmitter.

Your binding works like a charm, it does not trigger the ngOnchanges method, which is the expected behavior.
from the Angular docs :
OnChanges
Lifecycle hook that is called when any data-bound property of a directive changes.
as userGroups is not an #Input() it cannot be a "data-bound property" , its value changing internally will not run the ngOnChanges hook.

Related

How can I get reference of child component and emit a value from child component

I have a parent component ValidateSessionComponentwhich has a child component LoginFormComponent. In my unit test, I want to emit a value from LoginFormComponent but I am unable to figure out how I can do so.
The HTML of the ValidateSessionComponent has reference of the LoginFormComponent.
<app-login-form #loginForm (formOutputEvent)="handleFormValues($event)" [userId]="username"></app-login-form>
The LoginFormComponent looks like
export class LoginFormComponent implements OnInit {
loginForm:FormGroup;
formData:LoginFormValues;
#Input() userId;
#Output() formOutputEvent: EventEmitter<LoginFormValues>;
existingUser:UserSigninInfo;
..
}
In the test, i want to emit formOutputEvent by calling formOutputEvent.emit(formData); But I can't figure out how to access formOutputEvent of LoginFormComponent in ValidateSessionComponent's spec.
fit('should send signin request on receiving form values ',(done)=>{
//let loginComponent:LoginFormComponent = TestBed.get(LoginFormComponent); //IF I UNCOMMENT THIS THEN I GET ERROR NO PROVIDER OF LOGINFORMCOMPONENT
let userService:UserManagementService = TestBed.get(UserManagementService);
component.loginForm.formOutputEvent.emit(new LoginFormValues('test#test.com','somepassword'));
spyOn(userService,'signinUser');
setTimeout(()=>{
expect(userService.signinUser).toHaveBeenCalledWith(new UserSigninInfo('test#test.com','somepassword'));
done();
},1000);
});
I thought I could use the ViewChild directive #ViewChild(loginForm) loginFormRef; but I suppose I'll get an ElementRef. I can't figure out how to access the formOutputEvent.

Angular: How to call function after the component got the Input data

I have a parent and child component. In the parent I am searching for a user and give the userId via #Input to my child component. This is working.
After my child component is getting the value I want to call a function so that I am getting the card from the user back. How do I do this? I always getting a card is undefined error.
<p (show)="serachForCard()">
User: {{userId}}
Card: {{card.id}}
</p>
My Idea was to call a function but it is still not working.
Function in my ts file:
serachForCard() {
this.cardService.getCardByUserId(this.userId)
.subscribe(
(data: Card) => this.card= data
)
}
Implement OnChanges interface inside your child component and call your function from ngOnChanges method.
#Component({
...
})
export class MyComponent implements OnChanges {
#Input() someInput: string;
ngOnChanges(): void {
// do something with this.someInput
}
}
Documentation: OnChanges
A lifecycle hook that is called when any data-bound property of a directive changes. Define an ngOnChanges() method to handle the changes.

Cannot open form in child from parent component - 'cannot read property xxx'

Scenario: I have a list of companies that each have an array of projects as one of their variables. I will display the list of companies in the parent component/html, and only when clicking on their corresponding 'open' does a child component open to display the list of projects for that company. This list is a FormArray that is editable.
I created this FormArray example as the standalone projects component to interact and perform CRUD operations with example data.
My goal now is to open the form as a child component when I click the 'open' button on each individual company as in THIS stackblitz.
In the example it appears that this.setData(); within the constructor is causing the upset.
I have found through experimentation that, by commenting this line out causes the app not to crash, but of course the FormArray will not be loaded when I click the company 'open' button. However, I have also found that writing {{company.name}} in the child component DOES output the company details in the child, so it shows data is going through correctly.
I just cannot understand what is going wrong?
Try ngDoCheck() lifecyle hook
A lifecycle hook that invokes a custom change-detection function for a
directive, in addition to the check performed by the default
change-detector.
ngDoCheck() {
this.setData();
}
The problem lies in this.setData() call inside SubForm class constructor.
You never checked for null/undefined data. So, initially when the component loads, this.company is undefined. Hence, name variable will be assigned undefined value.
So, when program tries to access the value of this.company in the below line, it gives error:
setData() {
let control = <FormArray>this.myForm.controls.data;
control.push(this.fb.group({
name: this.company.name,
...
}));
}
Solution:
Add null/undefined check for this.company before calling this.setData():
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
data: this.fb.array([])
})
if(!isNullOrUndefined(this.company)){
this.setData();
}
console.log(this.company)
}
As I tried on the StackBlitz, few changes to be done to get it working:
You have to access the #Input variable in ngOnInit() by implementing an OnInit interface.
Parent Component .TS file:
isOpened : boolean = false; // one local variable of type boolean to open child compo
openInfo(company) {
this.isOpened = !this.isOpened;
this.openCompany = company;
this.open=true;
}
Parent Component HTML Code:
<mat-card *ngFor="let data of DATA">
{{data.name}}
<button mat-raised-button (click)="openInfo(data)">open</button>
</mat-card>
<div *ngIf="isOpened">
<subform [company]="openCompany"></subform>
</div>
Child Component .TS Code:
Import this:
import {
Component, OnInit, Input
} from '#angular/core';
and Component class:
export class SubForm implements OnInit {
#Input() company: any;
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
data: this.fb.array([])
})
console.log(this.company);
this.setData();
}
}
A working example

How do I call a function/execute code automatically in Angular?

I have code in a function that I need for initialization of other variables. However, this function doesn't get called unless i call it through another tag in html. Is there any way that i can initialize this function or write the code in a way in which the code gets executed automatically as soon as the project starts executing the the website loads?
You should have a look at lifecycle hooks that are used in Angular, here is the link to the documents related.
lifecycle hooks
In here you can read about the OnInit() lifecycle hook which is triggered when a component is loaded ( after constructor ) and is an ideal place to look at initialising variables / calling functions.
public ngOnInit(): void {
this.exampleText = 'Hello Component';
}
just make sure to implement it on your class like so
export class youClassHere implements OnInit {
public exampleText: string;
public ngOnInit(): void {
//executing logic on component load
this.exampleText = 'Hello Component';
}
}
You can implement OnInit event and do it there. Take a look here OnInit. Check here if you want to now more about Lifecycle Hooks. Alternative option is to use constructor. But that's executed on class initialization.
class MyComponent implements OnInit {
ngOnInit() {
// ...
}
}
You can implement OnInit lifecycle in your class and call your function inside OnInit so that it gets called whenever your component gets mounted.

Angular2 binding throws an error "Expression has changed after it was checked"

As i having parent and child component as follows,
Child Component (RWTaxComponent)
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'rw-tax',
templateUrl: 'rw.tax.component.html'
})
export class RWTaxComponent implements OnInit {
#Input() hsn: string = '';
#Input() srno: string = '';
#Input() taxPercent: number;
#Output() taxPercentChange: any = new EventEmitter();
constructor(
) { }
ngOnInit() {
}
ngOnChanges(event) {
console.log('HSN:: '+this.hsn);
console.log('SRNO:: '+this.srno);
if (this.hsn && this.srno) {
// Doing my logic here to find taxPercent
this.taxPercentChange.emit(this.taxPercent);
}
}}
Child component template (rw.tax.component.html) is,
<p>{{taxPercent | number:'1.2-2'}}</p>
And i invoked the above child component in my parent component as follows,
<rw-tax [(hsn)]="lineItem.hsn" [(srno)]="lineItem.srno" [(taxPercent)]="lineItem.taxPercent"></rw-tax>
I want to change taxpercent whenever hsn/srno is changed, these hsn/srno is not changed in RWTaxComponent itself, it is changed by parent component.
So i used ngOnChanges() event to detect that hsn/srno is changed and it getting invoked when ever i change hsn and srno in my parent component.
But the problem is after doing my logic to find taxpercent am trying to update the value in parent component by this.taxPercentChange.emit(this.taxPercent); and getting error "Expression has changed after it was checked"
Am i wrongly understood the Angular2 lifecycle ?
Please suggest a right way to do it...
This issue occurs when there is a change in input of the component and on that change if we are trying to change or emit any property.
Solution 1:
try promises resolve the changes and in then block emit the output event.
Solution 2 : (may not work)
Try setTimeout method on emitting the value