create angular components
Summary: In this article you will learn how to extend the browser and build your own custom HTML Input tag, as a means of learning the main functionality provided by Angular Components and Directives.
Now that we have a development environment in place, and that we are familiar with the benefits of the MVC approach, let's start looking into the main functionality available in Angular Core.
This is the part of the platform from which all other modules are built upon, and you are probably already familiar with many of the features of Angular Core as well as many of its most well-known decorators like @Component, but:
Did you ever wondered what is the overarching theme or goal that all those combined features are trying to achieve ? What is the main functionality of Angular Core ?
While looking into the features of Angular Core one by one and in isolation, we might end up loosing sight of the larger picture of what those features are trying to make possible.
All together, the view layer features of Angular Core provide us with all the tools necessary to essentially become the closest possible thing to a browser developer:
With Angular Core we can essentially extend the browser native functionality and create our own HTML tags and attributes, and associate behavior to them.
That in a nutshell is what Angular Core allows us to do, and most of the view layer features of Angular Core revolve around that single idea.
We will see that Angular Core (the view layer part) can be compared to essentially a missing browser extensibility toolkit - we are going to learn why and we will use it in a moment.
Let's start first by learning a core framework notion: let's learn what a Directive is.
The notion of a directive is not something unique to Angular, although AngularJs did both name the concept and popularized the term.
But something similar to the concept of directive has actually always been there since the beginning of browsers and the web !
To understand the notion of Directive, let's have a look at this plain input field:
This is actually a good example of a browser built-in directive. It does look on first sight like a leaf element of our HTML document:
<input placeholder="Type Your Search">
As we can see it has an XML-like API that allows to configure the element. Its not valid XML because there is no closing tag, but its close.
But there is more to this simple input box than meets the eye, at least in the case of the Chrome browser. To see that, let's go to the Chrome settings panel (Ctrl + Shift + I, and then click Settings), and then let's turn on the "Show User Agent Shadow DOM".
We will get back to what these terms mean, right now all we need to know is that this option allows us to see further inside our web page, including inside a plain HTML input.
That's right, at least in this case an HTML input is really not a leaf element ! Lets have a look at its internal implementation using the Dev Tools:
<input placeholder="Type Your Search">
# shadow-root
<div pseudo="-webkit-input-placeholder" id="placeholder"
style="display: block !important; text-overflow: clip;">
Type Your Search
</div>
<div id="inner-editor"></div>
So what do we have here, even more HTML ? HTML inside a leaf HTML tag like the input tag ? How does this work ?
It looks like the input component is not a native operating system input: instead it looks like its internally composed of some HTML, and even some CSS too ! There is this internal stylesheet applied inside this element:
input::-webkit-input-placeholder {
line-height: initial;
white-space: pre;
word-wrap: normal;
-webkit-user-modify: read-only;
overflow: hidden;
}
user agent stylesheet ::-webkit-input-placeholder {
-webkit-text-security: none;
color: rgb(117, 117, 117);
pointer-events: none;
}
It looks like an HTML input with a placeholder is internally implemented in the browser as a Div with a border, and a special area that is detecting keyboard strokes and reflecting the changes on the input box.
The placeholder is literally just a div with some text inside it, that sits inside the border of the input.
It looks like the browser internally composes HTML tags of of other more primitive HTML tags and styles, like DIVs etc., until it reaches the very native rendering elements of the native operating system platform where its running.
These HTML tags where not even visible to the outside world until recently, because tools did not show these elements. These elements are said to be part of the Shadow DOM of the input component.
These HTML elements are a hidden document sub-tree that can exist inside of what it looks like a leaf component, such as an HTML input.
Notice that the styles that run inside this input are isolated: these styles are not visible in the main HTML of the page, they are only visible inside the component.
So it looks like the browser has this built-in feature that seems very useful for creating new HTML elements from existing ones. Using it we can:
This combined specification of a look and feel, an API and a behavior is a useful concept, so let's give it a name: let's call it a Directive.
Although the notion of directive is usually associated with the Angular framework, we can see it can also be seen as a generic browser functionality that has existed for a long time as an internal browser implementation mechanism.
Wouldn't it be great if we could use this browser mechanism to create our own custom designed HTML tags ?
Sure, but the problem is that browsers don't allow us to use directly this internal functionality !
But the good news is that we can still get the same equivalent functionality: Angular Core provides us with a Javascript based mechanism that essentially allows us to do almost anything that we could do with the internal browser Directive mechanism ourselves.
To show that that is indeed the case, let's implement our own HTML input directive with a placeholder, and see how it maps to the native one.
We are actually going to use a very similar implementation to what we saw above in the browser shadow DOM - its going to be just a div with a border, plus some text and some keyboard behavior.
So let's get started implementing our first Angular Directive.
Let's start by defining the API of our Directive. We would like it to look like this on the page:
<my-input placeholder="Type your search"></my-input>
As we can see, this looks just like an HTML input, except that it has a closing tag and a different name.
This new directive will be a very particular type of HTML Directive, because more than behavior, it has also an associated look and feel. We will call to that type of Directive a Component - its simply a Directive with a template.
Let's then start implementing our input box, we will see that the end result will be hard to tell apart visually from a normal browser input.
So let's implement this in one go, and then break it down step-by-step. This is our first Angular component:
@Component({
selector: 'my-input',
template: `
<div class="my-input" tabindex="0" (keyup)="onKeyUp($event)">
{{value ? value : placeholder}}
</div>
`,
styles: [`
.my-input {
border: 1px solid lightgray;
height: 25px;
line-height: 25px;
padding-left: 5px;
width: 170px;
color: lightgray;
vertical-align: middle;
}`]
})
export class MyInputComponent {
@Input()
placeholder;
value = '';
onKeyUp($event: KeyboardEvent) {
if (this.value.length > 0 && $event.key === 'Backspace') {
this.value = this.value.slice(0, this.value.length - 1);
}
else if (!$event.metaKey && !$event.ctrlKey &&
!$event.altKey && $event.key != 'Shift') {
console.log($event.key);
this.value += $event.key;
}
}
}
This small sample of code actually has all the elements of a browser Directive:
If you try this out you would be surprised that this is actually quite close to what a real input looks like, except for the blinking cursor, which could also be simulated with a "|" character and an animation.
If we now break down how the my-input component is implemented, we will see that its quite close that what we see in the browser shadow DOM for a normal input.
As we can see, the code above is simply a plain ES6 class with a decorator attached to it. Let's isolate the part of the code that defines the public API of this component:
@Component({
selector: 'my-input'
....
})
export class MyInputComponent {
@Input()
placeholder;
....
}
As we can see, what tells Angular that this class contains the functional specification for a component is the presence of the @Component() decorator.
One of the properties of the decorator is called selector, and it contains a CSS selector that describes to which custom HTML elements should this component be applied.
In this case, this means that to all the elements on page named my-input we will be applying both this look and feel and this behavior.
Also we can see that component has an input property named placeholder, which is a text string. And the combination of the name of the tag my-input and the name of its input properties defines the public API of this new HTML element that we have built.
Let's now have a look at how the look at the look and feel of our component, defined inside the template.
The component is implemented as a focusable DIV (doe to the presence of the tabindex property), with a my-input CSS class applied to it:
<div class="my-input" tabindex="0" (keyup)="onKeyUp($event)">
{{value ? value : placeholder}}
</div>
Notice the special syntax {{}} inside the template, this is a template expression, meaning that the content of the expression will be evaluated against the component.
So this mean that the expression {{placeholder}} is evaluated as this.placeholder, where this is pointing to a particular instance of my-input.
In our expression we are using the ternary operator ? to display the placeholder text, in case the value variable is not populated. Notice that the value variable is not part of the public API of the component, but instead its an internal implementation detail of the component.
We can see that besides the component template, we are also defining some styles associated to the component. These styles by default work in a way that is very close to the Shadom DOM mechanism.
The styles defined inside this component are also isolated from the main page, just like the webkit-input-placeholder that we saw while looking inside the shadow DOM of a plain HTML input.
This is a very useful feature that the browser provides, because it means that we can define a component and ensure that it will always look the same independently of the page where the component is being used.
Although by default Angular does not use the same exact mechanism as the one present in the Shadow DOM of the plain input component, it uses out of the box something close.
Angular will transparently increase the specificity of the styles so that they take precedence over external styles present on the page, by adding an attribute selector to all the component styles.
The way that this works is that Angular will start by adding a given property to all the HTML elements of the my-input template at runtime:
<div class="my-input" tabindex="0" _ngcontent-c1>
{{value ? value : placeholder}}
</div>
Notice the _ngcontent-c1
property, this was automatically added by Angular. This property was then used to transparently bump the specificity of the component styles at runtime:
.my-input[_ngcontent-c1] {
border: 1px solid lightgray;
height: 25px;
line-height: 25px;
padding-left: 5px;
width: 170px;
color: lightgray;
vertical-align: middle;
}
This means that these styles will most likely and for all practical purposes take precedence over external stylesheets of the page, ensuring that the component will look the same across different pages where its used.
But notice that this level of isolation is not 100% guaranteed, meaning that if for example there is an external style that for example is using !important the component style would still be overridden.
Now that we have seen how the component template is defined and bound to the class the specifies an API and behavior, let's have a look at the implementation at how we are capturing keyboard strokes and reflecting the changes on the input box.
In the template, we are detecting the keyup event inside the div by using the (keyup) event syntax, which is binding the occurrence of the native browser keyup event to a component method called onKeyUp.
So this is another goal of the component class: it will not only define the public API of our component and provide a place to store variables used in the components template, but it also contains a definition of how the component should react to the user input.
In our case, we defined the keyboard handling behavior of our component in this method:
onKeyUp($event: KeyboardEvent) {
if (this.value.length > 0 && $event.key === 'Backspace') {
this.value = this.value.slice(0, this.value.length - 1);
}
else if (!$event.metaKey && !$event.ctrlKey &&
!$event.altKey && $event.key != 'Shift') {
console.log($event.key);
this.value += $event.key;
}
}
Let's then break down how the keyboard behavior works:
Notice that we did not interact with the template directly, we just modified the value variable and Angular detected this change transparently and ensured that the new value was reflected on the screen.
This is of course a simplified implementation of the input behavior, to illustrate both how the Shadom DOM browser functionality works, and how Angular provides approximately the same functionality.
So in a Nutshell, this is what Angular Core (the view layer part at least) is all about! Its like a missing toolkit for extending the browser with our own HTML elements, with their own API, look and feel and behavior.
Maybe the key design philosophy of this toolkit is that its designed to extend HTML instead of replacing it, by providing a Javascript implementation of some functionality that until now was only available as an internal browser composition mechanism.
The goal is to define our own HTML elements and use them to compose our application, in a very similar way to what browsers are already doing under the hood.
I hope you enjoyed the post, I invite you to have a look at the list below for other similar posts and resources on Angular.