What are directives? Directives are instructions to the DOM.
Components themselves within angular are directives (we are asking angular to put them inside the DOM).
Components are examples of directives with a template, but there are directives without a template.
We can actually create our own custom directives:
<p appTurnGreen>Receives a green bacgkroud!</p>
In the above, appTurnGreen
is a non existent directive, but we can create it.
Notice that when using a directive, we use it as a property to an html element (we can also use it as a class or tag as we learned before, but property is the default usage).
We we learn how to create custom directives in the section of the course which is focused on directives.
But currently, we will be looking at the built-in directives inside angular that we usually use a lot.
ngIf is a built-in angular directive which works as a conditional (if) directive. So say you want to output a message: 'server was created' when the server was created, but you don't want to output 'server was not created' when it wasn't.
Meaning you want to conditially display a message (or a DOM element).
ngIf is a structural directive (it changes the structure of our DOM) - because it either adds the element to the DOM or it doesn't.
We have to add a star before structural directives:
<p *ngIf="serverCreated" >Server was created</p>
ngIf should receive a statement that resolves into true or false (just like [disabled] in a previous example). If it receives a string or whatever, it won't work.
serverCreated
should be a variable found in your .ts file that is either true or false.
You could have also used a method that returns true or false, or directly written an expressions that resolves into true of false, it doesn't matter.
Sometimes you'd like to use an else condition with the ngIf.
We will go back to the previous example: now you also want to show 'No server was created' if no server was created yet (a bit redundant but okay).
Here is how you do it:
<p *ngIf="serverCreated; else noServer" >Server was created</p>
<ng-template #noServer>
<p>No server was created<p>
</ng-template>
ng-template
is a component directive that ships with angular, that can help us mark places in the DOM.
'#' is called a local reference, we will be learning more about it later. For now you can think of it as a marker, you can name it whatever you want.
ngIf was a structural directive. Other directives (such as ngStyle) are attribute directives, and we will be using them just like we use normal attributes (properties -?) inside html elements [no star here].
Unlike structural directives, attribute directives don't add or remove elements; they only change the element they were placed in.
Not to elongate this much: we can add normal styling for any html component we have, as such:
<img src='/assets/img/parms-group-large.png' class='w-100' style='opacity: 0.3' />
However, ngStyle is useful for conditional styling.
Remember, in react, you'd have done conditional styling as such:
style={{marginBottom: mainPrice ? "10px" : "0"}}
However, in angular we're not writing JSX. We can't have double {{}} brackets for normal javascript. This is where the ngStyle comes in handy.
Here is how to use it: we will be using it as a property binding (if we were using it normally we wouldn't have the square brackets, but we're using it as property binding - the most usual use case for this directive - so that's why we have square brackets):
<p [ngStyle]="{backgroundColor: getColor()}" >Hello, I am colored conditionally</p>
The [ngStyle] property binding expects to get a javascript object. So inside the {} brackets, we're actually writing javascript. Which allows us to call functions or do whatever.
Here is the getColor() function code in .ts:
getColor(){
return this.serverStatus === 'online' ? 'green' : 'red';
}
You could've also written it like this (we can either write the css property name inside a string or use camel case as we did above):
<p [ngStyle]="{'background-color' : getColor() }" >Hello, I am colored conditionally</p.
Also, you [maybe, I am just speculating] could have skipped using the method altogehter:
<p [ngStyle]="{backgroundColor: serverStatus === 'online' ? 'green' : 'red'}" >Hello, I am colored conditionally</p>
ngClass is also an attribute directive. Just like ngStyle allowed us to conditionally add styles, ngClass will allow us to conditionally add classes.
And it also needs to be used as a property binding [ngClass] to work as intented.
And like ngStyle, is also expects to receive a javascript object (so just open the {} brackets and start writing js). The javascript object should contain pair values, as such:
"{className : condition}"
So consider the previous example: now we want to make the text inside the green background to become white - but we will do it using ngClass:
<p
[ngStyle]="{backgroundColor: getColor()}"
[ngClass]="{online: serverStatus === 'online'}"
> Hello, I am colored conditionally </p>
online
is the name of the class we want to conditionally add. If it had a dash inside the name, like for example, online-text
we would need to put it inside single brackets: 'online-text'
.
ngFor is a structural directive (it adds stuff to the DOM), so we should use it with a star.
Quick overview:
Assume you want to output a component twice or more. Instead of doing this:
<app-server></app-server>
<app-server></app-server>
We can create an array of servers inside our servers .ts file
export class ServersComponent implements OnInit{
allowNewServer = false;
serverCreationStatus = 'No server created';
serverName = 'testServer'
serverCreated = false;
//here:
servers = ['Testserver', 'Testserver 2'];
onCreateServer(){
this.serverCreated = true;
//here
this.servers.push(this.serverName);
this.serverCreationStatus = 'server was created'
}
}
So now we can display the initial 2 servers (now found within the array), and display additional servers as they get added. So remove this:
<app-server></app-server>
<app-server></app-server>
And put this instead:
<app-server *ngFor="let srvr of servers"></app-server>
Now the above will loop through all elements of the server
array that was defined in the .ts file and be passed to the outer element app-server
to output each one.
srvr
is just a variable we defined now, it can be named anything. And we can use it from now on anywhere inside this element (only this element I guess). We can string interpolate it inside the element content, we can use it inside other directives, etc [see assigment below].
However, this is not the ideal way to this (loop through stuff and output them). There are better ways to achieve this, and we just gave an example here to better understand ngFor.
The assignment asked for the following:
*Note from instructor: for the last task, it might be easier to have used numbers instead of timestamps for logging clicks
My solution (which worked :D)
.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
display: boolean = false;
i: number = 1;
clicks = [];
onButtonClick(){
this.display = !this.display;
this.clicks.push(this.i ++);
console.log(this.clicks);
}
}
.html
<button (click)="onButtonClick()">Display Details</button>
<p *ngIf="display">Lorem ipsum dolor sit amet.</p>
<ul style="padding-top: 40px;">
<li
*ngFor="let item of clicks"
[ngStyle]="{backgroundColor: item >= 5 ? 'blue' : ''}"
[ngClass]="{'white-text': item >= 5}">{{item}}</li>
</ul>
.css
.white-text{
color: white;
}
Note how we defined
item
inside*ngFor="let item of clicks"
and then used it inside the element content, and also in other directives.
Notes:
The instructor used the array length instead of creating i
to keep track of click logs:
.ts
export class AppComponent {
display: boolean = false;
clicks = [];
onButtonClick(){
this.display = !this.display;
this.clicks.push(this.clicks.length + 1);
}
}
Another note on how to solve it when using timestamps (solve the 'how to dectect item 5 and above' issue) is in the next section.
So based on the previous assigment, we will now use timestamps and see how we can detect the 5th item.
We can actually have an additional statement inside *ngFor
to detect the index of the current item:
<button (click)="onButtonClick()">Display Details</button>
<p *ngIf="display">Lorem ipsum dolor sit amet.</p>
<ul style="padding-top: 40px;">
<li
*ngFor="let item of clicks; let i = index"
[ngStyle]="{backgroundColor: i >= 4 ? 'blue' : ''}"
[ngClass]="{'white-text': i >= 4}">{{item}}</li>
</ul>
Note how now we started from 4, since the index starts from 0.
.js (just updated it to get a timestamp):
export class AppComponent {
display: boolean = false;
clicks = [];
onButtonClick(){
this.display = !this.display;
this.clicks.push(new Date()); //or just Date.now() if you want the number of seconds since bla bla
console.log(this.clicks);
}
}
This is it for section 2 of the course. Good luck!