Sunday, 30 December 2018

How to write your own attribute directive in Angular?

Background

In the last post, we saw how to use structural directives like *ngIf and *ngFor that can be used to modify DOM elements. 
Attribute directives can change the appearance or behavior of an element, component, or another directive. In this post, I am going to show you how to write your own attribute directives in Angular.

For this, we are going to create a directive which on single click will enlarge our text and on double click get it back to original size. 

How to write your own attribute directive in Angular?

Let's start by writing our directive class file. Under the app folder create a new file called enlarge.directive.ts.  In this file add following code -


import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';
@Directive({
  selector: '[appEnlarge]'
})
export class EnlargeDirective {


  constructor(private el: ElementRef, private renderer: Renderer2) { }


  @Input('appEnlarge') appEnlargeSize: string;


  @HostListener('click') onclick() {
    this.enlarge( this.appEnlargeSize || '100px');
  }


  @HostListener('dblclick') ondblclick() {
    this.enlarge('50px');
  }


  private enlarge(enlargeSize: string) {
    this.renderer.setStyle(this.el.nativeElement, 'font-size', enlargeSize);
  }
}


Let's go over this code and try to understand what we are doing here.  1st line has a couple of imports that we will understand as we go ahead. Next line has an annotation called @Directive which states that this class is a directive and its selector is appEnlarge. So you could use this directive as follows -
  • <p appEnlarge>Directive applied for this content!</p>
We will see this in action in some time. Next, we have a constructor where we have access to the native element and the renderer. We can use APIs from renderer or Native element to actually make changes to the element.

Next, we have an input called appEnlargeSize. Here the user can specify how much the context needs to be enlarged. Notice how we have used an alias here in the bracket. This is so that we could specify input and directive in the same binding as below -
  • <p [appEnlarge]="'120px'">Directive applied for this content!</p>
Then we are using HostListener APIs for single and double click to actually do our transformation - which is increasing the font size. You can see all HostListener APIs here -
 Notice how in enlarge function we have used renderer API of setStyle to apply our desired style. You can see all renderer2 APIs here -

Now that your directive is done let's import it and declare it in our main module. Go to app.module.ts  and import it. Also, add it in the declaration section. Your app module should look like below -

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {EnlargeDirective} from './enlarge.directive'

@NgModule({
  declarations: [
    AppComponent,
    EnlargeDirective
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule { }

And that's it. Save all your changes and start your angular app using -
  • ng serve
Go to http://localhost:4200/ and see your changes.

Single click on the text to enlarge it and double click it to bring it to its original size.






This is not a great example with perfect CSS and animation but I hope this gives you an idea of how you can write custom directives in angular.  These are very powerful techniques to change the appearance of your components. Let me know if you have any questions. Thanks.


This is the last post of the year 2018 :). So wishing all a very happy new year! Hoping coming year would be more awesome with a lot of more learning and sharing!

Related Links

Thursday, 27 December 2018

Using structural directives in Angular

Background

There are 3 kinds of directives in 
  1. Components—directives with a template.
  2. Structural directives—change the DOM layout by adding and removing DOM elements.
  3.  Attribute directives—change the appearance or behavior of an element, component, or another directive.
We have already seen how to create components, how components interact with each other. Structural Directives change the structure of the view whereas Attribute directives are used as attributes of elements. You can use build in directives provide by angular or build your own.

In this post, I will explain structural directives such as *ngIf and *ngFor.


Using structural directives in Angular

As mentioned before structural directives manipulate the DOM structure by adding or removing elements from the DOM.  An asterisk (*) precedes the directive attribute name. Let's see an example by using *mgIf. This structural directive is used to decide if the element on which it is applied (including its children) are added in DOM or not.

So in your AppComponent code make changes so that the HTML template looks like below -

<div class="hello-world">

  <h1 *ngIf="showMessage">Hello World!</h1>
  <br/>

  <button (click)="toggleVisibility()">Toggle Visibility</button>
</div>


Notice the way we are using *ngIf. It says if showMessage variable evaluates to true then show this h1 element and if not remove it from the DOM. Then we have a button which says "Toggle Visibility" that essentially toggles the showMessage variable flag. Let's see the component typescript code to understand this better. Your component code would look like following -

import { Component } from '@angular/core';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  showMessage: boolean = false;

  toggleVisibility = () => {
    this.showMessage = !this.showMessage;
  }

}


Notice the showMessage variable of type boolean. It is set to false at the beginning and as you click the button this flag is toggles which change the visibility of our H1 element. Please note that *ngIf removes the element from the DOM and does not just change the visibility. So when I say changes visibility I mean it being part of the DOM. So let's give this a try. Start your app with -
  • ng serve





Notice how the h1 elements take part of DOM and when it is not visible it gets removed. Your button position will get adjusted based on that.

NOTE: When the condition is false, NgIf removes its host element from the DOM, detaches it from DOM events (the attachments that it made), detaches the component from Angular change detection, and destroys it. The component and DOM nodes can be garbage-collected and free up memory.

The asterisk is "syntactic sugar" for something a bit more complicated. Internally, Angular translates the *ngIf attribute into a <ng-template> element, wrapped around the host element.


Now let's take a look at *ngFor directive.

Change the component code to have an array of cities.


import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  cities: string[] = ["Mumbai", "Bangalore","Delhi","Chennai","Hyderabad"];

}


Now you can change this components HTML code as follows -

<div class="hello-world">
  <ul>
      <li *ngFor="let city of cities">{{city}}</li>
  </ul>
</div>


This essentially creates an unordered list element and inside it, iterates over each of the cities in the array and creates a list element. This would look like below -




NOTE: Angular desugars this notation into a marked-up <ng-template> that surrounds the host element and its descendants. Each structural directive does something different with that template.

As mentioned before you could also create your own structural directives. To read more on these go to the angular documentation - https://angular.io/guide/structural-directives

Related Links

Wednesday, 26 December 2018

How to dynamically create a component in Angular?

Background

In the last few posts, we have been seeing how to write applications in Angular. If you wish to revisit some of them take a look at the Related Links section at the bottom of this post. In this post, I will show you how to dynamically create components on Angular. I am assuming you already have the boilerplate code we have written so far. This would be an extension to it.


How to dynamically create a component in Angular?

Let us start making changes from the AppComponent. If you remember this is the component we have added in the bootstrap property of our root module. Go to the HTML content of this component, remove all the code we had added before for routing and replace it with the following code -

<div class="hello-world">
  <h1>{{title}}</h1>


  <button (click)="genComponent('Botton 1 Clicked!')">Button 1</button>
  <button (click)="genComponent('Botton 2 Clicked!')">Button 2</button>


  <template #messagecontainer>
  </template>


</div>

It is a simple HTML content. Let me explain this first. 

  • We have a simple h1 tag which has a bound value called title. title value would be provided by the corresponding component definition in the typescript file. We will come to the changes in typescript file in a moment. 
  • Next, we have 2 buttons that call the function genComponent on the click event. This function definition would again go in the typescript file which we will see shortly.
  • Lastly, we have a template tag where we will dynamically load our component. This new component as we will see has a bound value called msg which is what you see as the argument of genComponent function.
Now let's see how the typescript file for this looks like -

I am going to create a new component in the same AppComponent file. If you wish you can create a new file for it. The component is as follows -

@Component({
  selector: 'app-child',
  template: `
<h2>Message : {{msg}}</h2>
`
})
export class ChildComponent {
  @Input() msg: string;
}

As you can see this ChildComponent which we intend to create dynamically take an argument msg as input. We will see how we can pass this input when we dynamically create this. You can also see it's HTML content - It simply has an H2 tag with the bound msg input.

Now, let's take a look at AppComponents definition -

import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, OnDestroy } from '@angular/core';




@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnDestroy {
  title = 'angulardemo';
  componentRef: any;


  @ViewChild('messagecontainer', { read: ViewContainerRef }) entry: ViewContainerRef;


  constructor(private resolver: ComponentFactoryResolver) { }


  genComponent(msg) {
    this.entry.clear();
    const factory = this.resolver.resolveComponentFactory(ChildComponent);
    this.componentRef = this.entry.createComponent(factory);
    this.componentRef.instance.msg = msg;
  }
  destroyComponent() {
    this.componentRef.destroy();
  }


  ngOnDestroy() {
    this.destroyComponent();
  }
}

Let us see what this is doing -
  • We already say the content of app.component.html above where we removed the navigator outlet and added 2 buttons and a template for dynamically loading our CHildComponent.
  • Inside the component definition, we have a variable called title which we say in the HTML template inside h1 tag.
  • Then we have an entry variable which points to the template tag we have created in AppCpmponents HTML file.
  • We can access template as the ViewChild inside the Component class. The template is a container in which, we want to load the ChildComponent dynamically. Therefore, we have to access template as ViewConatinerRef.ViewContainerRef represents container where one or more view can be attached.
  • Next, we have defined a constructor where we have injected ComponentFactoryResolver dependency. We need this to create the component dynamically.
  • Then we have 2 methods  - genComponent and destroyComponent that is used to create and destroy our ChildComponent dynamically. Notice how we are storing the reference of the new component in componentRef and using it.
  • In genCompinent we are doing the following -
    • Clearing the container
    • Creating a factory for ChildComponent
    • Creating component using above factory
    • Passing the value msg input using component reference instance method
  • Also, notice we are calling destroyComponent in ngOnDestroy lifecycle hook of angular component. You could add this login inside an event of your own as well. 
  • You also saw how our ChildComponent has an input called msg and you can see in genComponent function we are passing this input as this.componentRef.instance.msg.
  • Also, take care of all the imports that are on the 1st line of the above code.
NOTE: The entry component which is a reference to the template tag has APIs to create and destroy components.

The final changes that we need would be in the AppModule itself. We need to define our new Child component in declaration and entry components. So import the new ChildComponent in the AppModule and put it under declaration and entryComponents 


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NewEmployeeComponent } from './employee/new-employee/new-employee.component';
import { SubempComponent } from './employee/subemp/subemp.component';



@NgModule({
  declarations: [
    AppComponent,
    NewEmployeeComponent,
    SubempComponent,
    ChildComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [ChildComponent]
})
export class AppModule { }

Now you should be all set to test out your code. Start your angular application with -

  • ng serve
and open your application on the set port (default 4200)-






Related Links

Tuesday, 25 December 2018

How to write your own webpack plugin?

Background

In the last post,


we saw how we can customize webpack build configuration without using "ng build" command with angular CLI 6. In this post, I will show you how we can build our webpack plugin and use it in our angular project.


Setup

We already created a custom webpack configuration file in the last post. So let's start from there. Our relevant angular.json webpack config has the following part - 

"architect": {
  "build": {
    "builder": "@angular-builders/custom-webpack:browser",
    "options": {
"customWebpackConfig": {"path": "./custom-webpack.config.js"},


So our custom webpack config file is -
  • ./custom-webpack.config.js
Let's go ahead and make changes to this file so that we can provide it our own plugin to work. Add the following code to it -


const MyWebPackPlugin = require("./webpack-plugin.js")
 
module.exports = {
    plugins: [
        new MyWebPackPlugin({
            message: 'Hellow World!'
        })
    ]
}

As you can see it tells webpack that it needs to use a custom webpack plugin supplied. Also if you remember from last post configurations in this file are merged with the default ones. So no need to provide all webpack configurations in this file. Just the ones to add/edit should suffice.


In the above code, we are saying we need a plugin wrote in a separate file called ./webpack-plugin.js. Once we import it we create an instance of the plugin class and pass a variable called message to it. We will see this plugin class in a moment. So whenever webpack runs it will use this plugin. 

For the actual plugin code create a file called custom-webpack.config.js in the same folder as webpack-plugin.js and add the following code to it.


class MyWebPackPlugin {
    constructor(optionInput) {
        this.options = optionInput;
    }
    apply(compiler) {
        compiler.hooks.done.tap("MyWebPackPlugin", () => {
            console.log("Message is : " + this.options.message);
        });
    }
}
module.exports = MyWebPackPlugin;



This is essentially creating a Plugin class called MyWebPackPlugin. It has a constructor which takes in options. If you revisit code in custom-webpack.config.js  written above you will notice we are passing options to the instance of MyWebPackPlugin which has a message variable. 

The directory structure for your reference is as follows -


Understanding the Plugin

A webpack plugin is a JavaScript object that has an apply method. This apply method is called by the webpack compiler, giving access to the entire compilation lifecycle.


So you see there is an apply method in the class we wrote above which takes an argument called compiler. This gives us access to complete compilation lifecycle. In the above example, we are looking for "done" event and getting a callback for that. To see a list of all hooks available visit - 
To see all plugin APIs refer -

Finally, in the callback, we are printing out the value of message we got from our angular app.

NOTE: We are using webpack 4. Plugin code would look slightly different for webpack 3.

Let's run ng build and see if our changes worked!




And you can see our message getting printed in the console.


Related Links




Monday, 24 December 2018

How to customize build configuration with custom webpack config in Angular CLI 6

Background

In Angular CLI 1.x (Angular 5) we had a command called "ng eject" which ejected underlying webpack configuration so that you could make changes based on your requirements. This has been disabled with Angular CLI 6.


As you can see it will be completely removed in Angular CLI 8.0 version. In this post, I will show you how you can customize your builds with custom webpack configuration without using ng eject. For this, we will use a library called angular-builders. This post assumes you have done necessary setup and have basic knowledge of Angular 6. I have written multiple posts previously on these. If you are not following it then take a look at the links in the related section at the end of this post.

This uses the concept of Builders. You can provide a builder of your own or can use one that is provided by the community.


How to customize build configuration with custom webpack config in Angular CLI 6

Let's start by adding new dependencies. Go to package.json and add following dependencies under devDependencies =

"@angular-builders/custom-webpack": "^7.0.0",
"@angular-builders/dev-server": "^7.0.0",
"@angular-devkit/build-angular": "~0.11.0", 

Make sure build-angular dependency is ~0.11.0. This is something that you may need to upgrade version for. We will see in a  moment how to use this. Once done go to your apps root folder and run -
  • npm install
This should install the required additional dependencies in the node_modules folder in the same directory.


Now go to angular.json file. Here you need to change the builder from "@angular-devkit/build-angular:browser" to "@angular-builders/custom-webpack:browser".

NOTE: If you are building an universal app and would like to change server build configuration use @angular-builders/custom-webpack:server instead of @angular-builders/custom-webpack:browser.


Next, you need to add customWebpackConfig property to your build target options to point it to your custom webback config file.

"customWebpackConfig": {"path": "./custom-webpack.config.js"},

Now your angular.json build configuration should look something like -

      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {"path": "./custom-webpack.config.js"},
            "outputPath": "dist/angular-demo",
            "index": "src/index.html",
            ....


Now you can create a file called custom-webpack.config.js in the root directory (Same place where the angular.json file exists).

In this file, you can add your custom webpack config. For example, if you want to add a custom loader for files with extension .example you can do something like below -


const path = require("path");

module.exports = {
  module: {
    rules: [
      {
        test: /\.example$/,
        use: ["example-loader"],
        include: path.resolve(__dirname, "./")
      }
    ]
  }
};



NOTE: Unlike ng eject command this configuration will be merged with default angular build configuration. So you need to add configuration to this file which you wish to change/add (and not the whole thing).


How to use ng serve with custom webpack configuration we set up above?

If you wish to use ng serve with above setup - i.e with custom webpack configuration make following additional changes -

Inside serve target in angular.json file change "builder": "@angular-devkit/build-angular:dev-server" to "builder": "@angular-builders/dev-server:generic".

Your serve target should look something like below -


"serve": {
  "builder": "@angular-builders/dev-server:generic",
  "options": {
    "browserTarget": "angulardemo:build"
  },
  "configurations": {
    "production": {
      "browserTarget": "angulardemo:build:production"
    }
  }
},


Now you can do an ng serve and custom webpack configuration changes we did will be used.

In the next post we will see how we can create a custom webpack plugin with setup in place. So stay tuned :)


Related Links

Wednesday, 5 December 2018

Passing data between the nested component in Angular

Background

In the last post, we saw how we can create nested components in Angular -
We also saw a few other angular things - links creating a new App, using Angular CLI, understanding project structure etc. All the links are in the related links section at the bottom. In this post, I will show you how to pass data from parent component to child component and listen for data to be passed from child to parent. 

I am going to continue using previous code example that we have built so far. So if you need additional guidance before we start with this code, look up the previous post that talks about building nested components

Passing data between the nested component in Angular

At this point, you should have a parent component called NewEmployeeComponent and a child component called SubempComponent. Child component just has a static HTML saying that subcomponent works. And it gets rendered as part of the parent component. All of this works and we saw it in the last post. If you do have a question till here I would recommend to go back and read my previous post -
Now let's add some input and output properties in the child component. Your child component code should look like below -

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
@Component({
  selector: 'app-subemp',
  templateUrl: './subemp.component.html',
  styleUrls: ['./subemp.component.css']
})
export class SubempComponent implements OnInit {


  @Input()
  department:string;


  @Input()
  designation:string;


  @Output()
  joined:EventEmitter<string>;


  constructor() {
    this.joined = new EventEmitter();
  }


  ngOnInit() {
    console.log("SubempComponent initialized");
  }


  onButtonClick = function(event) {
    console.log("Button clicked. Emitting now!")
    this.joined.emit("Joined department " + this.department + " with designation " + this.designation);
  }


}

Notice here that department and designation are two parameters which are input and are of type string. So we would expect the parent component to send it to this child component. Next, we have an output parameter called joined which is an EventEmitter. Parent component which creates this child component can catch and handle this output and we will see how in a moment. Finally, we have a function onButtonClick that actually emits a string saying that the button was clicked with the particular designation and department. We will now see when this function will get called. But essentially once this function is called we emit the string which the parent component can intercept and handle.

Now let's see child components HTML code -

<h1>This is sub components heading</h1>
<p>
  Yay! subemp works!
  Department : {{department}} <br/>
  Designation : {{designation}} <br/>


  <button (click)="onButtonClick($event)">Click to join!</button>
</p>


From the previous version of the code, I have just added lines to print department, designation and a button on click of which we call function onButtonClick which we just saw above. This function will, in turn, emit the event that will be caught and handled in the parent. We will now see changes in parent component and once done we will execute our code and see it in action.


Make changes to parent component typescript file so that it looks like below -

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-new-employee',
  templateUrl: './new-employee.component.html',
  styleUrls: ['./new-employee.component.css']
})
export class NewEmployeeComponent implements OnInit {

  name: string;
  age: number;

  constructor() { }

  ngOnInit() {
    this.name = "Aniket";
    this.age = 27;
  }

  childClickedButton = function(event) {
    console.log("Child component clicked. event : " + event);
  }

}



Notice that the only new change here is a new function called childClickedButton that we will use to catch and handle the output of child component we say above.

Now make changes to the parent's HTML file so that it looks like below -

<h1>Welcome to the Employee portal</h1>

<h3>Name : {{name}}</h3>

<h3>Age : {{age}}</h3>

<app-subemp

    [department]="'IT'"
    [designation]="'Software developer'"
    (joined)="childClickedButton($event)"
></app-subemp>

</pre>



We have changed some things in the way we add child component in the HTML file. This is how we send input to the child component and handle the output. It's called data binding. So we are essentially sending department and designation as inputs to the child component. We saw these as @input() variables in child component. Similarly, we are now catching joined which was a output and calling our user defined function childClickedButton function. So the string emitted by the child on the actual button clicked will be sent as the event to this function.

Now let's see this in action. Run the app with command -

  • ng serve -o
You should see the following screen -




Make sure you go to the correct URL path to render the components. Now you can click the "click to join" button in the parent and see the console logs. You should see the following -






You can see the button click event happened which emitted the event from the child. Parent captured it and printed another console log. That's how you can configure inputs and outputs for nested angular components and pass data.


Related Links



  • Angular Hello World example(OSFG)
  • Understanding angular application folder structure(OSFG)
  • Routing and navigation in Angular(OSFG)
  • Building nested components in Angular (OSFG)
  • t> UA-39527780-1 back to top