Learning Angular: Directives - Custom
I am continuing from my last Learning Angular
article. This
time, I write a custom directive using Angular’s documentation to
write a custom appUnless directive to write my own: appFor2 - a
specialized for directive.
By reading this article, you will understand how to create your own
directive and change an item in Angular’s TemplateRef.
This article will take you less than five minutes to read.
Introduction
As I used ngFor in my ngFor
article,
it annoyed me that I couldn’t not write the loop in this form:
<ul *ngFor="let item in Object.keys(items)">
<li> {\{ item }\} </li>
</ul>Angular does not execute code past one level in the template,
(i.e. Object.keys(items) is a string in the template) there needs to
be a temporary variable to hold the keys of items.
In the Angular documentation, the document how to create your own directive, I thought: why not create a directive so it would be:
<ul *appFor2="items">
<li>{\{ item }\}</li>
</ul>This should be easy, right?!
Starting with unless
Angular
documentation
shows how to make custom directive for the unless function.
Cool, this is a good starting point as:
- it has the setup to create a new directive
- it’s similar to what I want to make
- not part of the ‘standard’ library
I’m basically halfway there!
Following Along
If you would like to follow along with this code, you can use the Github link to get a final copy of the code or view the project in your browser using this StackBlitz link.
Port unless into my project
To get a better feel of whats going on, I translate the unless
function into my application along with its infrastructure. Everywhere
there is an unless, I replace with For2 or its equivalent.
The app.module.ts file:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { For2Directive } from './for2.directive';
@NgModule({
declarations: [
AppComponent,
For2Directive,
],
imports: [
BrowserModule,
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }The for2.directive.ts file:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appFor2]'})
export class For2Directive {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
@Input() set appFor2(items: Array<any>) {
console.log("hi from appFor2");
}
}Without this example in the documentation, I would not have such an easy time setting up a custom directive in Angular.
appFor2 v.0
Instead of keeping the appFor2 function body the same as the
original unless, I change the appFor2 function to the following:
@Input() set appFor2(items: Array<any>) {
console.log("hi from appFor2");
}- changing
condition: <boolean>to:items: Array<any>sets my function to accept an array of<any>item instead of a boolean. - using
console.log("hi from appFor2");validates the configuration through a simple message in the console.
Using appFor2
To use appFor2, the app.component.html file needs an entry, let’s
keep it simple and reuse the words list from my ngFor article:
<ul *appFor2="words">
<li> </li>
</ul>Let’s run the application, open our browser and make sure things are working.
Nothing on the page, which in this case, is a good sign.
Let’s open the browser’s console and see if there’s anything:

Great - appFor2 sends a message to the console. This means we can
execute any code in appFor2.
Let’s get a sweet for function going!
Next Step
In the documentation, unless had this code in it:
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
}The this.viewContainer.createEmbeddedView(this.templateRef); looks
important. Let’s add this to our appFor2 function in the following
manner:
@Input() set appFor2(items: Array<any>) {
for(var item of Object.keys(items)) {
let view = this.viewContainer.createEmbeddedView(this.templateRef);
}
}Let’s run it and see what we get in our browser:

Interesting, there’s four bullets with nothing in them.
The fact that there are bullet points and not just “empty space” is
important. That means the viewContainer has <li> element details
and Angular passes them in through templateRef.
How can the appFor2 function put the contents of the items array
into the viewContainer to populate the bullet points?
Digging around
Honestly, I was looking around the Angular documentation with a hope I would find an article on working with TemplateRef. The documentation on TemplateRef did not have the details I wanted. I did find another article that pointed me the right direction!
The TemplateRef is an abstraction and designed so Angular can render into anything: different web browsers, mobile devices, native applications.
Digging through TemplateRef by outputting the object onto the console
using console.log(view) let me interactively poke it.
I kept poking around until I found the textContent entry, which the
above article mentions.
Let’s see if setting this textContent value as: item will do
anything using the following code:
@Input() set appFor2(items: Array<any>) {
for(var item of Object.keys(items)) {
let view = this.viewContainer.createEmbeddedView(this.templateRef);
console.log(view)
view.rootNodes[0].children[0].textContent = item;
}
}
Wow, that works!!!
Wait a minute…
That worked for a list, what if we use appFor2 in a paragraph? Like so:
<p *appFor2="words">
<b>{\{ item }\}</b>
</p>Looking at the page, the result is:

Wow, that’s works too. I basically have a new implementation for for
the way I want, in a <ul>, <p>, and more.
Conclusion
Using the Angular documentation for creating a custom directive as a
basis, I created my own custom directive that is a for loop, done
the way I want.
Finding the way to manage the TemplateRef was the hardest part and took trial and error.
I won’t go making my own custom for loop every time I want to work
with a list of items. This exercise definitely gives me a little
understanding of what’s going on in TemplateRef and how display items.