Skip to content

Angular Directives

Vincent Kruger edited this page Sep 28, 2022 · 7 revisions

Project Setup

We will continue to use the existing Angular project you've already generated.

Be sure to commit it as is, so that you can go back and look at older examples if needed, since we will be changing app.component.ts and app.component.html

Go ahead and generate a new component called comment-section and use it within app.component.html.

You should see something like comment-section works! once you start your app.

We'll be building out a comment section as we go through the different directives.

What are directives

Think of directives as supercharging regular HTML.
For example, we've already been creating our own components, and components are a type of directive in angular.
All that means is, whereas we regularly used to only be able to use specific tags like <div>, <p>, <head> etc., we can now create our own component and use it's tag (like <app-comment-section>) in our HTML. That is a directive, it supercharged regular HTML and added a new type of tag we can use.

Similarly directives can be used not just to add new tags, but also to add new attributes we can set on a tag.

The above is a rough explanation of how you can think of directives, below is a more technical explanation of the same thing.


At the core, a directive is a function that executes whenever the Angular compiler finds it in the DOM.

Angular directives are used to extend the power of the HTML by giving it new syntax.
Each directive has a name — either one predefined by Angular like ngFor, or a custom one which can be called anything. Each directive also determines where it can be used: in an element, attribute, class etc.

Above copied from this guide to directives

See the official Angular documentation for more information and for other built in directives: https://angular.io/guide/built-in-directives

Structural Directives

Structural directives are responsible for HTML layout. They shape or reshape the DOM's structure, typically by adding, removing, and manipulating the host elements to which they are attached.

ngIf

The ngIf directive can be used to conditionally show or hide parts of your UI.
You can do this by applying the directive to any DOM element and binding it's value to a boolean property (or expression or function) in your Typescript code.

Suppose we wanted to only show comments to the user once they click on a button that says View Comments. This is a good use case for using ngFor.

Edit comment-section.component.ts as follows:

  // default to not show the comments
  showComments = false;

  // function that changes the variable to true
  onViewComments() {
    this.showComments = true;
  }

Edit comment-section.component.html as follows:

<!-- Below shows an event binding that will call our onViewComments function when we click the button.
     This was discussed in a previous chapter -->
<button (click)="onViewComments()">View Comments</button>

<!-- Below we see our first structural directive
     ngIf will ensure our <p> tag is only shown when the showComments variable is set to true
     Which currently will only happen once we click on the button -->
<p *ngIf="showComments">comment-section works!</p>

When you look at your app now you should see something like the following:

ngIfBasic

This is great, but right now we can't hide the comments again, and the button keeps saying Show Comments even once they're already visible.
Try to use what we've learned about javascript and Angular bindings to achieve the below functionality.

ngIfToggle

Hint: You will need to use the {{ }} binding syntax as well as a ternary expression to dynamically set the button's text. You will also need to update our showComments() function to instead toggle our boolean variable between true and false.

ngFor

The ngFor directive can be used to repeat a specific DOM element/tag as many times as needed, whilst allowing you to still customize each item if needed. This means we don't have to write javascript to repeatedly insert a new element, we can just use the ngFor directive and Angular will work it's magic for us.

Right now instead of a list of comments, we've got just a single <p> tag, but let's change that to more closely resemble a real comment section.

First we need a list (array) of comments that we want to show to the user, so let's create that first.

Edit comment-section.component.ts and let's add an array of comments:

// Three comments we want to show to the user.
  comments = [
    'Wow this is so cool!',
    'Honestly amazing 💯',
    'Probably one of the things of all time',
  ];

Okay so now that we have the data we want to show, let's show it in our app.
Edit comment-section.component.html as follows:

<!-- ngIf as before to only show this div once the user clicks to view the comments -->
<div *ngIf="showComments">
  <!-- use the ngFor directive to create as many <p> tags as we need, one for each comment -->
  <!-- This is similar to a regular for (each) loop. For each comment we will create a <p> tag -->
  <!-- We are creating a variable called `comment` that will reference the current comment as we loop over our array -->
  <!-- We can then use the value of each actual comment using bindings -->
  <p *ngFor="let comment of comments">{{ comment }}</p>
</div>

ngFor

Attribute Directives

Attribute directives listen to and modify the behavior of other HTML elements, attributes, properties, and components.

ngClass

The ngClass directive can be used to dynamically add or remove classes from an element based on our data/javascript code, again without us needing to write javascript to do this. The directive takes care of all of it for us.

Setting the scene

Let's say our comments page will be used on a social networking site like YouTube, we want comments made by the video creator to show up differently from other users, so that they stand out.

We can do this using CSS, any comment made by the user who uploaded the video should have a gold background.
Let's do that below.

Firstly, we need to know who made what comment, so let's change our comments array to instead of being an array of strings, to be an array of objects. This way we can include information on who left the comment.

// array of comment objects. Each object has a `user` property as well as a `comment` property.
comments = [
    { user: 'CodeSpace', comment: 'Thanks for all the feedback everyone!' },
    { user: 'Josh', comment: 'Wow this is so cool!' },
    { user: 'Tim', comment: 'Honestly amazing 💯' },
    { user: 'Alex', comment: 'Probably one of the things of all time' },
  ];

Because our array now contains objects not strings, if you looked at your site you'd see something like this:

image

That's because in our HTML file, we are using the following

  <p *ngFor="let comment of comments">
    {{ comment }}
  </p>

However each comment is now an object, and inside of the object is a comment property which actually holds the value of what the user commented.

So let's change our HTML to show who made the comment as well as fix the above issue. We'll do this by accessing the comment and user properties that are available within each comment object.

<p *ngFor="let commentObj of comments">
    {{ commentObj.comment }} - {{ commentObj.user }}
</p>

image

Okay now let's say this comment section is for a video CodeSpace uploaded, we can use ngClass to style it differently than the other comments.

Using ngClass

Edit comment-section.component.css and let's add the following styling:

p {
  padding: 8px;
}

/* we will need to apply this class to any comment made by whoever uploaded the video */
.creator-comment {
  background-color: cornsilk;
}

Now that the CSS is ready, we need to use it. Let's edit our HTML one last time.

<p
    [ngClass]="commentObj.user == 'CodeSpace' ? 'creator-comment' : ''"
    *ngFor="let commentObj of comments"
  >
    {{ commentObj.comment }} - {{ commentObj.user }}
</p>

As you can see, we're using the ngClass directive and a ternary expression to determine what css class should be applied to the <p> tag.
If the user who made the comment is (==) CodeSpace then we want to add the creator-comment class to that <p> tag, else we don't apply any class ('').

Your site should now look as follows:

image

Further reading on ngClass

ngClass can do a lot more than just apply one class, you can apply/remove as many classes as you'd like if you instead binded to an object.
I'd highly recommend reading further about it, as well as ngStyle and it's usage.

ngModel

Use the NgModel directive to display a data property and update that property when the user makes changes.

To follow the below examples, you will first need to visit the above link to see how you can import the FormsModule into our app.

This directive essentially allows us to synchronize changes that happen on an HTML input element to our code and back again. Meaning we don't have to write code to synchronize the two.

Let's add the ability for our users to add a new comment.

Edit the comment-section.component.ts and add the following:

  newComment = '';

  // ...
  // some other variables and functions
  // ...

  onClickAddComment() {
    this.comments.push({ user: 'unknown user', comment: this.newComment });
  }

The above should look familiar, we are simply going to add a new comment object to our array of comments when the user clicks a button. We are using unknown user for now, as we've got no way at the moment for the user to enter their own name.

Next let's add some UI for the above functionality.

Edit the html file and add the following at the bottom of the file.

<hr />
<label>New Comment:</label>
<!-- Use the ngModel directive to ensure that when a user types into the input, our variable `newComment` will get updated. -->
<!-- Similarly if we were to update `newComment` in our `.ts` file, the input in the UI will reflect that change -->
<input [(ngModel)]="newComment" />
<!-- Bind to the disabled attribute of the button to block submitting when the comment field is empty -->
<!-- Also binding to the click event to call our custom function when the user clicks the button -->
<button [disabled]="!newComment" (click)="onClickAddComment()">
  Add comment
</button>

If you see an error about Can't bind to 'ngModel' since it isn't a known property of 'input'. please follow the first link in this sub-section on importing the FormsModule

You should now have a page similar to the following:

ngModel

Right now after submitting a comment, the comment box stays filled in. See if you can update the onClickAddComment function so that the comment box gets cleared once the user clicks on a button.

Hint: This is a simple one line change, thanks to the ngModel directive!

As an extra exercise you can try adding another input field with another ngModel directive where the user can type in their own name before submitting a comment. You would then need to disable the Add Comment button until both the user name and comment fields are filled in.

It should look something like this:

ngModelExtended

Clone this wiki locally