How to create (and use) custom components with Angular 2

We get this question ALL the time. It’s time to address it through this article.

By the end of this article, you’ll know a lot more about –

Custom Components

How they are different from Regular Components

How you can create your own Custom Components in Angular

Best practices for writing Custom Components

If you read till the end of the article, we also have a little something for you. This special something changed the way I structured my applications.

Custom Components in Angular?

If you’re new to Angular or you want to learn more about components, not just custom components, check out this article. It talks about the three kinds of directives in Angular (secret… a Component is a directive.)

What are custom components? What makes them special?

Custom components are equal to components. They are the same thing. There is no such thing as a custom component! That’s exactly why we wanted to take the time to write this article and clear the air.

Let’s give it some Angular 1.x context. Angular 1.x people may be familiar with custom directives.

In Angular 1.x you had two options with directives –

Use an existing directive (part of Angular or a 3rd party library).

Create your own (custom) directive.

But things have changed in Angular. Components are building blocks of modern Angular applications. No custom components, no application.

Does your Angular application use 0* custom components? That means you’re either using a bunch of third-party libraries, or you NEED to look at your code again.*

How to create a Component in Angular

Time to create our first component!

1. Import Component Decorator

1

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

This imports the component decorator (described below).

2. Use Decorator and Add Metadata

Now we’re going to use the @Component decorator, pass it some data and then export our component class.

1

2

3

4

5

@Component({

selector:'my-custom-component',

template:`

<div>My Custom Component</div>

`})exportclassMyCustomComponent{}

Decorators are more than the people who come in and beautify your home.

In the context of programming, decorators are a design pattern. They are an alternative to subclassing.

We’ve informed Angular that the class MyCustomComponent is an Angular Component. Now, Angular will treat it like a component. It will use all the metadata we pass to augment the MyCustomComponent class. Our class is now an Angular component.

In a nutshell, decorators augment something. The @Component decorator augments a class.

3. Specify Template

An Angular component is a directive wth a template. No template, no component.

There are two ways you can add a template to your component –

1. Inline Template

Here you add the template in the metadata. Use this when the template code is less than (or equal to) 3 lines (see best practices below for more information).

1

2

3

4

5

@Component({

selector:'my-inline-template',

template:`

<h1>My Inline Template Component</h1>

Hello World`})exportclassMyInlineTemplateComponent{}

2. External Template File

Use this when your template code is more than 3 lines. We prefer to use this anyway. It keeps separates the logic from the presentation making the code more maintainable.

1

2

3

4

5

// Folder Structure

--freezer

|--freezer.component.ts

|--freezer.component.html

Doesn’t this folder structure look so much better? Extracting the template also makes it easier to find the files you’re looking for.

Note: There’s a reason both the files have the .component suffix. More on that in the best practices section below.

Now, let’s create a simple template file.

It’s freezing in here!

Increase the temperature

16°C

25°C

35°C

Finally, we’ll include the template file in the component.

1

2

3

4

5

6

7

// Including your template file in your component

@Component({

selector:'freezer',

templateUrl:'./freezer.component.html'

})

exportclassFreezerComponent{}

Notice that we aren’t using the template property. Instead, we’re using the templateUrl property to include the external template file

This looks a lot cleaner and pays off as the size of the template files and project continues to grow.

4. Add Styles

This is optional. You don’t need styles for your component to work.

Styles work a lot like templates. There are two ways you can include them in the template –

1. Inline Styles

You can inline styles using the styles property.

1

2

3

4

5

6

7

8

9

10

11

@Component({

...

styles:[`

h1{

margin-top:50px,

border:2pxsolid black;

}

`]

...

})

exportclassFreezerComponent{}

Just like templates, this doesn’t really look too pretty. It’s much cleaner (and more maintainable) when we extract these styles to an external file.

You could also inline the styles in the template markup.

2. External Style File(s)

Using the example above, our new folder structure would look something likes this –

1

2

3

4

--freezer

|--freezer.component.ts

|--freezer.component.html

|--freezer.component.css

Now let’s pull in the style file.

1

2

3

4

5

6

@Component({

...

styleUrls:['./freezer.component.css'],

...

})

exportclassFreezerComponent{}

Much better.

It doesn’t end there. Let’s just say you’ve got multiple themes for your component. You can extract the styles for each of themes in another file and then include them in the component.

Your new folder structure might look something like this.

1

2

3

4

5

6

7

--freezer

|--freezer.component.ts

|--freezer.component.html

|--freezer.component.css

|--themes

|--dark.theme.css

|--snow.theme.css

And here’s how you’d include all these files.

1

2

3

4

5

6

7

8

9

10

@Component({

...

styleUrls:[

'./freezer.component.css',

'./themes/dark.theme.css',

'./themes/snow.theme.css'

],

...

})

exportclassFreezerComponent{}

Note: You can also import external style files using @import. Have a look at Angular’s official docs for more info.

Wait! The styling goodness doesn’t end there.

Encapsulation

Your component can use three encapsulation strategies.

We can let the styles leak to other custom components (no encapsulation). Or we can restrict the styles to the current component

Shadow DOM

To understand the different strategies, you need to know how the Shadow DOM works. Here’s a quick overview.

Shadow DOM is the browser’s ability to render a DOM tree (subtree) without including it in the main DOM tree.

In the context of styles, it means that the global styles would not apply to the elements in the Shadow DOM.

Encapsulation Strategies

Here are the values you can pass to the custom components encapsulation property –

native – this uses the browser’s native Shadow DOM implementation. The styles will not leak out.

emulated – the current component restricts the CSS without the use of a Shadow DOM (it’s emulated). If you want to read more about this, you can look at the official docs.

none – this adds the styles to the global scope. The component uses the styles throughout your application.

5. Export the Component

We’ve implied this step in the examples above, but it’s important to make it explicit. You cannot use your component without exporting it.

1

2

@Component({...})

exportclassMyComponent{}

The component also needs to be a part of a module. So make sure that you add it as a declaration of a NgModule.

Other Things You Can Do With Your Component

The 4 steps we covered above are the bare bone essentials. Angular’s Component is capable of a lot more than displaying some HTML with styles.

1. Component Lifecycle Hooks

Angular calls lifecycle methods as it creates, updates and destroys the component.

The different lifecycle methods are available as interfaces. Implementing these interfaces is optional. You don’t need to use them, but it’s better if you do – it allows strong type checking and better editor tooling.

So let’s look at an example with the OnInit lifecycle hook. Angular calls the ngOnInit method after it creates the component. We can use this method to initialize a component and set things up.

1

2

3

4

5

6

7

8

9

10

11

// Required imports

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

// Configure component

@Component({})

exportclassMyComponentimplementsOnInit{

// Method called by Angular once it has created the component

ngOnInit(){

console.log('component initialized');

}

}

In the example above, Angular will call the ngOnInit method once it has created the component.

Note: The method name for all lifecycle hooks is the name of the lifecycle hook prefixed with ng. So to access the OnDestroy lifecycle hook, we would add the ngOnDestroy method.

2. Working with data/services in Angular

Let’s create an IceCreamList component and use it to display a list of the ice cream flavors we have.

Here’s what the folder structure is going to look like for our small app. We are going to keep the services in one folder for this small application.

1

2

3

4

5

6

--services

|--ice-cream.service.ts

--ice-cream-list

|--ice-cream-list.component.ts

|--ice-cream-list.component.html

|--ice-cream-list.component.css

Note: We are only talking about the IceCreamList component and it’s data. We aren’t going to cover bootstrapping the application here. You might know how to do that already. If not, you can read about setting up the root module.

Now we’re going to create a simple template that renders a list of ice cream flavors.

{{flavor}}

Next, we’re going to create the service that returns the list of ice cream flavors.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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

@Injectable()

exportclassIceCreamService{

flavors:Array;

// Populate 'mock' flavors. In real-world applications, you'd get these through a backend service

The only thing special about this bit of code is the providers property. In it, we tell Angular to inject the IceCreamService into our component. The providers are available to the children custom components too.

Time to get the data from the service.

1

2

3

4

5

exportclassIceCreamListComponent{

constructor(private_iceCreamService:IceCreamService){

this.flavors=_iceCreamService.getFlavors();

}

}

We injected the IceCreamService into our constructor and retrieved the flavors. We can now display these flavors in our template.

3. Component @Input and @Output

Your component isn’t going to be a hermit living far away from other custom components. The most common interaction is with parent components.

Let’s look at the ice cream example once again. This time we’ll use two components to display the data – a parent list component and a child ice cream component.

This is a common use case in real world applications – use a parent component to render child components.

Here’s what our folder structure will look like.

1

2

3

4

5

6

7

8

9

10

--services

|--ice-cream.service.ts

--ice-cream-list

|--ice-cream-list.component.ts

|--ice-cream-list.component.html

|--ice-cream-list.component.css

--ice-cream-flavor

|--ice-cream-flavor.component.ts

|--ice-cream-flavor.component.html

|--ice-cream-flavor.component.css

We will use the IceCreamListComponent to get the data using the IceCreamService. Then, we will pass that data to the IceCreamFlavorComponent. When the user clicks on a flavor we will log the name of that flavor.

The IceCreamServiceremains unchanged.

Let’s start by updating the template used by IceCreamListComponent.

Pick a flavor

Click on a flavor to select it

Here, we pass the flavor data to IceCreamFlavorComponent. The IceCreamFlavorComponent handles the presentation.

Let’s create the IceCreamFlavorComponent.

1

2

3

4

5

6

7

8

9

10

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

@Component({

selector:'ice-cream-flavor',

templateUrl:'./ice-cream-flavor.component.html',

styleUrls:['./ice-cream-flavor.component.css']

})

exportclassIceCreamFlavorComponent{

@Input()flavor:string;

}

We added another import for the Input decorator. Then, we used that decorator to declare flavor as an Input property.

Now, template time.

{{flavor}}

The template displays the flavor along with a button. Let’s create the function to handle this click event.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// Two new imports

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

@Component({...})

exportclassIceCreamFlavorComponent{

@Input()flavor:string;

// The parent can bind to this event

@Output()flavorSelected=newEventEmitter();

onSelectFlavor(){

this.flavorSelected.emit(this.flavor)

}

}

When the user clicks a flavor button, the component emits the flavor to its parent.

The last thing we need to do is log the name of the flavor. Time to update IceCreamListComponent.

1

2

3

4

5

6

7

8

9

10

11

...// imports

@Component({...})

exportclassIceCreamListComponent{

...// methods to fetch the ice cream data

// Method that logs the ice cream flavor

onFlavorSelected(flavor:string){

console.log(flavor);

}

}

We’re done! Now you know how to –

Create a component.

Fetch data using a service.

Send data back and forth between components.

Best Practices for Creating Custom Components in Angular

Now let’s look at some best practices for you custom components. These are from Angular’s official style guide.

Please note that this is an opinionated guide. By no means is this the best way to do things. As long as your application follows some sort of convention and it’s consistent, it doesn’t matter what style guide you use.

Your code will work without the following pointers. These are only opinions on how you should structure your code.

1. Naming Your Selector

Use the dashed-case or kebab-case for your component’s selector.

1

2

3

4

5

6

7

8

9

// Suggested

@Component({

selector:'my-component',

})

// Not

@Component({

selector:'myComponent'

})

Use an element selector instead of an attribute or class selector.

1

2

3

4

5

6

7

8

9

// Suggested

@Component({

selector:'my-component',

})

// Not

@Component({

selector:'[myComponent]'

})

2. Extract Template and Styles

We’ve already covered this above. But let’s do a run through.

If your template or styles take up more than 3 lines extract them into another file.

Name your files using kebab-case followed by .component followed by .<file-type>.

1

2

3

4

5

6

7

8

9

10

11

// Suggested

--my-component

|--my-component.component.ts

|--my-component.component.html

|--my-component.component.css

// Not

--my-component

|--myComponent.component.ts// no kebab-case

|--template.html// incorrect name

|--my-component.css// does not have .component.css suffix

Finally, use relative URLs for specifying the path to the external file.

1

2

3

4

5

6

7

8

9

10

11

// Suggested

@Component({

...

templateUrl:'./my-component.component.html'

})

// Not

@Component({

...

templateUrl:'/src/components/my-component.component.html'

})

3. Input and Output Properties

Use the @Input and @Output decorators instead of using the input and output property.s

This keeps the inputs and outputs more maintainable.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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

// Suggested

@Component({

selector:'my-component',

templateUrl:'...'

})

exportclassMyComponent{

@Input()firstName:string;

@Input()lastName:string;

}

// Not

@Component({

selector:'my-component',

templateUrl:'...',

inputs:['firstName','lastName']

})

exportclassMyComponent{}

It’s also much neater when you place @Input and @Output on the same line as the property.

1

2

3

4

5

6

// Suggested

@Input()property:string;

// Not

@Input()

property:string;

Avoid using aliases (other than when you need to). It’s easier to keep track of one name. You only need to consider using aliases if there are clashes (eg. same component name and the input property name).

1

2

3

4

5

// Suggested

@Input()firstName:string;

// Not

@Input('firstName')name:string;

5. Naming Events and Event Handlers

Name events without the on prefix. This keeps your event names consistent with Angular’s event names.

1

2

3

4

5

6

7

8

// Suggested

&lt;/...&gt;

// Not

&lt;/...&gt;

// Angular doesn't use the on prefix for events

<button>Click</button>

Name event handlers with the on prefix. This is useful if you’re using Angular’s alternative syntax. For now, look at this as consistent naming for your event handlers.

1

2

3

4

5

6

7

exportclassMyComponent{

// Suggested

onFlavorSelected(...){...}

// Not

flavorSelected(){}

}

6. Class Member Order

Think public first, private after. Properties/fields first, methods after. This order helps us locate members and identify their purpose with ease.

So your class would look something like this.

1

2

3

4

5

6

7

8

9

10

@Component({...})

exportclassMyComponent{

// public properties

// private fields

// public methods

// private methods

}

You can also go the extra mile by organizing each of the sections alphabetically.

7. Extracting Complex Logic

We want to keep our components as clean and decoupled as possible.

We already did this above in the ice cream example. We extracted the logic to get the ice cream flavors in the IceCreamService. Our component takes care of the UI and the service takes care of the logic.

Now, if we want to fetch ice cream flavors from a remote API, we only need to change the code in the IceCreamService.

Always extract complex logic into services. Reusable services make the code more maintainable.

8. Presentation Logic in Component

Your component’s template should be DUMB. Handle all the logic in the component. Once again, this makes your code more maintainable. You’ll have all your component’s logic in one place.

1

// Suggested

{{f}}

Total cost of all flavors {{totalCost}} Average cost of one scoop {{averageCost}}

// Not

{{f}}

Total cost of all flavors {{totalCost}} Average cost of one scoop {{totalCost / flavors.length}}

Atomic Design

Alright here’s something extra. This is what separate the good developers from the great developers.

You might have worked with something like Bootstrap or Angular Material in the past.

What if your product could have its own library? Wouldn’t it be easier to create other products using the same UI elements?

That’s where atomic design comes in. Atomic Design principles have been around for quite some time now. Very few people actually use them.

Atomic design is a methodology for creating design systems.

The goal is to break down your complex UI. What’s left is buttons, inputs, labels, etcetera. Now, you can use these atomic components to build larger components.

By reusing components, you create a design system that is maintainable and flexible. The next time you need to change a button on a website, you only need to change one component!

That’s it!

This was a sneak peek into the magic of the Atomic Design methodology. You can read more about Atomic Design here.

Conclusion

Now you have all the knowledge you need to create your own Angular custom components.

We’ve covered –

The basic syntax for creating custom components.

Adding a template and styles to your component (inline and external).

Working with lifecycle hooks.

Using a service to get data for your components.

Best practices that you can use while creating your custom components.

I hope this article has been helpful for you. Please post any questions, comments, and feedback you have below. We look forward to it!