April 25, 2022

Angular - Can't bind to 'x' since it isn't a known property of 'y'

In Angular 11, I got this error:

error NG8002: Can't bind to 'x' since it isn't a known property of 'my-component'.
  1. If 'my-component' is an Angular component and it has 'x' input, then verify 
       that it is part of this module.
  2. If 'my-component' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the 
       '@NgModule.schemas' of this component to suppress this message.
  3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.

Apparently it is telling the reasons behind it, but still it did not inform about which specific module is missing if you do not know it yourself.

While searching for my problem, I have found the following possible scenarios, which may cause this error.

  1. When you have defined a custom input field 'x' in your component, first thing to check is if the property 'x' is defined in your component with an proper Input() decorator.

    In Html:

    <my-component [someInputProperty]="someValue">
    	

    In .ts file:

    export class MyComponent {
    
      @Input()
      someInputProperty: string;
      ...
    }
    	
  2. Also check the spelling for property names (case-sensitive) to make sure its free from typo.

    In .ts file

    export class MyComponent {
    
      @Input()
      someInputProperty: string;
      ...
    }
    	

    In Html (wrong):

    <my-component [someinputproperty]="someValue">
    	

    Which should be (correct):

    <my-component [someInputProperty]="someValue">
    	
  3. If you have defined the component in a custom module, make sure that you have registered this component (or directive or pipe) class in NgModule's declarations array:

    @NgModule({
      ...
      declarations: [
    	...,
    	MyComponent
      ],
      ...
    })
    export class AModule { }
    	
  4. If you have defined the component in a custom module (AModule), and want to use it in another module (BModule). Make sure that you have registered this component (or directive or pipe) class in source (AModule) module's declarations array as well as exports array:

    @NgModule({
      ...
      declarations: [
    	...,
    	MyComponent
      ],
      ...
      exports: [
    	...,
    	MyComponent
      ],
    
    })
    export class AModule { }
    	
  5. For common Angular default attributes, we need to memorize the module needs to be imported. For example:

    • For ngFor attribute, we need to import CommonModule.
    • In a similar fashion, for ngModel we need to import FormsModule.

April 20, 2022

Angular CLI - Generate components in a specific folder

In Visual Studio Code, following are the methods that can be used to create a component in a specific directory.

Method 1: Open in Integrated Terminal

You want to create a component in a app/components folder as shown in the image below:

  • Right click on the folder in which you want to create component.
  • Select option Open in Integrated Terminal

  • It will open a new terminal pane with the selected path.

  • In new terminal, type the command to create new component:

       ng g c component1
    

Method 2: Copy Relative Path and Paste on Terminal

  • Right click on folder in which you want to create component
  • From context menu, select Copy Relative Path

  • On termincal, type cd, press space and then paste the copied path. It will change the current working directory.
  • Then you can run the command to create new component.

       ng g c component1
    

Method 3: Manually Type complete path on terminal

In create component command, append the full folder path before compnent name.

   ng g c path/from/app/folder/component1

In our exmaple, to create a component in components folder (which is a sub-folder inside app), the command will be:

   ng g c components/component1

If your application have multiple modules, you may want to specify the existing module in which you need to create new component.

   ng g c employee/component1 --module=employee/employee.module.ts

This command will create component in employee module.

March 21, 2022

Using JavaScript Object.assign() method

In this post, we will see how to use the JavaScript Object.assign() method to copy and merge objects.

The syntax of the Object.assign() method is:

Object.assign(target, ...sources)

The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.

The Object.assign() invokes the getters on the source objects and setters on the target.

Clone an object

The following example uses the Object.assign() method to clone an object.

let book = {
    title: 'My Title'
};

let clonedBook = Object.assign({}, book);

console.log(clonedBook);
Output will contain the properties copied from source object.
{ title: 'My Title' }

Note that the Object.assign() only creates a shallow clone, not a deep clone.

Merge objects

The Object.assign() can merge two or more source objects into a target object which will contain properties consisting of all the properties of the source objects. For example:

let book = {
    title: 'My Title'
};

let bookTemplate = {
    noOfPage: 200,
    author: 'Author'
};

let bookPublishTemplate = {
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
};

let newBook = Object.assign({}, book, bookTemplate, bookPublishTemplate);

console.log(newBook);
Output will contain the properties merged from all the source objects.
{
    title: 'My Title',
    noOfPage: 200,
    author: 'Author'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
}

If two or more source objects have the property with same name, the property of the later object overwrites the earlier one:

let book = {
    title: 'My Title'
};

let bookTemplate = {
    title: 'My Title from bookTemplate'
    noOfPage: 200,
    author: 'Author'
};

let bookPublishTemplate = {
    author: 'Author from bookPublishTemplate'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
};

let newBook = Object.assign({}, book, bookTemplate, bookPublishTemplate);

console.log(newBook);
Output will contain the properties merged from all the source objects, also the duplicate properties will be overwritten by the later source objects.
{
    title: 'My Title from bookTemplate'
    noOfPage: 200,
    author: 'Author from bookPublishTemplate'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
}

Angular - Add conditional css class

Angular provides multiple ways to add css classes based on conditions.

Lets say we have a div and we want to apply the classes myclass1, myclass2, myclass3 based on different conditions using a variable counter defined in the component class.

Following are the different snytax methods we can use to add css class with conditions.

Method # 1

Add myclass1, if the counter is equal to 1

    <div [class.myclass1]="counter === 1">

Method # 2

Add myclass1, if the counter is equal to 1

	<div [ngClass]="{'myclass1' : counter === 1}">

Add myclass2, if the counter is greater than 1

	<div [ngClass]="{'myclass2' : counter > 1}">

You can use the same syntax to add multiple classes with their own conditions, each class-condition pair will be spearated by a comma.

For example, here the myclass1 will be added if the counter's value is 1, myclass2 will be added if the counter's value is 2, and myclass3 if the counter has a value greater than 2

   <div [ngClass]="{'myclass1': counter === 1, 'myclass2' : counter === 2 }, 
        'myclass3' : counter > 2 }">

Method # 3

We can use the ternary operator to pick one of the two classes.

If the counter's value is 1, then myclass1 will be added, else myclass2 will be applied.

    <div [ngClass]="counter == 1 ? 'myclass1' : 'myclass2'">

Method # 4

You can write a function inside the component which will return the the class(es) to be added based on some complex condition.

    <div [class]="getClass(6)">

The function will return class names like this:

  getClass(count: number){
    var classList='';

    if(count === 1){
       classList = 'myclass1'; 
    }else if (count > 1 && count < 5){
        classList = 'myclass2';
    }else if(count > 5){
        classList = 'myclass2 myclass3';
    }
    return classList;
  }
}

February 24, 2022

Angular - Receive event notification from child component

Angular enables the components based development which helps you create small cohesive components to design the application in a manageable approach. This may also lead to a complex heirarchy among the components. Then you need a mechanism to communicate between these components which may be in two directions top-down and bottom-up.

In this post we will see an example of bottom-up approach to allow communication from the child component to the parent component.

Lets say we have two components, ParentComponent and ChildComponent, with structure like this.

<parent-component>
    <child-component />
</parent-component>

The child-component has a button called btnSearch, which needs to invoke the parent-component's function searchCalledByChild();

Lets walk through this example to see how we can achieve this behavior.

This is the child component's ts file:

// child.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-child-component',
  templateUrl: './child-component.component.html',
  styleUrls: ['./child-component.component.css']
})
export class ChildComponentComponent implements OnInit {
  count: number = 0;
  child_msg : string = "";

  constructor() { }

  ngOnInit(): void {
  }
  
  btnSearchClicked() {
    this.child_msg = "Clicked Counter: " + this.count++;
  }
}

This will display a string child_msg in html template, showing the counter for button clicks.

This is the parent component's ts file:

// parent.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent-component',
  templateUrl: './parent-component.component.html',
  styleUrls: ['./parent-component.component.css']
})
export class ParentComponentComponent implements OnInit {
	
  parent_msg : string = "";
  chil_msg_inside_parent : string = "";

 constructor() { }

  ngOnInit(): void {
  }
	
  //method in parent class, this needs to be invoked 
  //when button is clicked in child component
  searchCalledByChild(child_msg_received: string)
  {	
    this.parent_msg = "Message from parent component";
		
    //child_msg_received is the data passed from child component.
    this.chil_msg_inside_parent = child_msg_received;
  }
}

To enable the parent component to receive notification of child-component's event, we have to makes these changes in child-component's ts file:

First import Output and EventEmitter from '@angular/core'

import { Output, EventEmitter } from '@angular/core';

Decorate a property with @Output(). Here searchButtonClickedEvent is the name of the property declared as EventEmitter, which means it's an event.

@Output() searchButtonClickedEvent = new EventEmitter<string>();

The type parameter we passed to EventEmitter<> tells angular that this event will emit the string data.

Raise this event from child-component's local method btnSearchClicked() using the emit() function.

btnSearchClicked() 
{
   this.child_msg = "Clicked Counter: " + this.count++;

   this.searchButtonClickedEvent.emit(this.child_msg);
}

Here is the html template for child-component:

<div style="background-color:grey;margin:10px;padding:10px;">
   <button id="btnSearch" (click)="btnSearchClicked()">Click child</button>
   <p>Child Content: {child_msg}}</p>
</div>

Now the child component is ready to emit events whenever the btnSearchClicked() function is called. In this example we are calling this function from child-component's button click.

To enable the parent-component to receive this event, we will bind the parent' local method to the child's event. To bind the event we will use the same event property of EventEmitter we have defined in the child-component.

<div style="background-color:tan;margin:10px; padding:10px;">
   
   <app-child-component 
         (searchButtonClickedEvent)="searchCalledByChild($event)">
   </app-child-component>
   
   <p>{{parent_msg}}</p>
   <p>Parent Content: {{chil_msg_inside_parent}}</p>
   
</div>

This event binding, (searchButtonClickedEvent)="searchCalledByChild($event), connects the event in the child (searchButtonClickedEvent) to the function in the parent(searchCalledByChild).

The $event parameter contains the data that we have passed from the child-compoment in the emit() function.

The complete listing of the parent-component's ts file will be look like this:

// parent.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent-component',
  templateUrl: './parent-component.component.html',
  styleUrls: ['./parent-component.component.css']
})
export class ParentComponentComponent implements OnInit {
	
  parent_msg : string = "";
  chil_msg_inside_parent : string = "";

 constructor() { }

  ngOnInit(): void {
  }
	
  //method in parent class, this needs to be invoked 
  //when button is clicked in child component
  searchCalledByChild(child_msg_received: string)
  {	
    this.parent_msg = "Message from parent component";
		
    //child_msg_received is the data passed from child component.
    this.chil_msg_inside_parent = child_msg_received;
  }
}

References:

Related Post(s):