-
Notifications
You must be signed in to change notification settings - Fork 0
Angular Directives
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.
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 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.
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:
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.
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 ourshowComments()
function to instead toggle our boolean variable between true and false.
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>
Attribute directives listen to and modify the behavior of other HTML elements, attributes, properties, and components.
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.
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 object
s. 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:
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>
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.
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:
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.
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 theFormsModule
You should now have a page similar to the following:
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:
-
An introduction to Angular
- Installing and creating an Angular Project
- Interactivity and Bindings
- Angular Directives
- Looping with Angular Directives (Coming Soon)
-
Challenge
- Angular Classes (Coming Soon)
- Presentation and interactivity with Angular (Coming Soon)