This is one of several repos that I created for the course "Angular - The Complete Guide (2022 Edition)". For a complete list of repos created for this course: https://gist.github.com/christophervigliotti/92e5b3b93cbe9d630d8e9d81b7eb6636 .
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656188#overview
- it's switch/case for your templates
<div [ngSwitch]="value">
<p *ngSwitchCase="5">Value is 5</p>
<p *ngSwitchCase="31">Value is 31</p>
<p *ngSwitchDefault>Value is default</p>
</div>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656186#overview
-
Get the condition as an input with
@Input
. Bind to a property namedappUnless
, whenever it changes execute a method (using a setter with theset
keyword). This is still a property, it's just a setter of the property which is a method that gets executed whenever the condition changes phew. -
We expose two things in the constructor:
TemplateRef
andViewContainerRef
are injected into the template. TemplateRef gives us access to the template and ViewContainerRef gives us access to the place in the DOM where want to render it. Template is the what and the viewContainer is the where (is that statement accurate?). -
Method
createEmbeddedView()
does. This creates a view in this view container. the view is our templateRef (argumentthis.templateRef
). -
Method
clear()
removes everything from this place in the DOM.
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]'
})
export class UnlessDirective {
@Input() set appUnless(condition: boolean){
if(!condition){
this.viewContainerRef.createEmbeddedView(this.templateRef);
}else{
this.viewContainerRef.clear(); // remove everything from this place in the dom
}
}
constructor
(
private templateRef: TemplateRef<any>,
private viewContainerRef: ViewContainerRef
) { }
}
Added line import { UnlessDirective } from './unless.directive';
and added UnlessDirective
to the declarations
array.
import { AppComponent } from './app.component';
import { BasicHighlightDirective } from './basic-highlight/basic-highlight.directive';
import { BetterHighlightDirective } from './better-highlight/better-highlight.directive';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { UnlessDirective } from './unless.directive';
@NgModule({
declarations: [
AppComponent,
BasicHighlightDirective,
BetterHighlightDirective,
UnlessDirective
],
imports: [
BrowserModule,
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In this implmenetion we pass in onlyOdd
, which fires the logic that we put in the "method inside of a property" @Input
declaration in unless.directive.ts
<!-- even -->
<div *appUnless="onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 == 0}"
[ngStyle]="{backgroundColor: odd % 2 == 0?'yellow':'transparent'}"
*ngFor="let even of evenNumbers"
>{{ even }}</li>
</div>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656182#overview
- the star * is the required prefix for a structural directive.
- angular transforms *ngSomething into a structural directive
without structural directive (using ng-template
)...
<ng-template [ngIf]="!onlyOdd">
<div>
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 == 0}"
[ngStyle]="{backgroundColor: odd % 2 == 0?'yellow':'transparent'}"
*ngFor="let even of evenNumbers"
>ng-template style! {{ even }}</li>
</div>
</ng-template>
with an *ngIf
structural directive...
<div *ngIf="!onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 == 0}"
[ngStyle]="{backgroundColor: odd % 2 == 0?'yellow':'transparent'}"
*ngFor="let even of evenNumbers"
>{{ even }}</li>
</div>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656180#overview
better-highlight.directive.ts
// added...
@Input() defaultColor: string = 'transparent';
@Input() highlightColor: string = 'blue';
// ...modified this line to remove the default color...
@HostBinding('style.backgroundColor') backgroundColor: string;
/*
...and changed ngOnInit to set the color here. this allows us to
set the color after it may be overridden/redefined outside of this directive (see app.component.html snippet below)
*/
ngOnInit() {
this.backgroundColor = this.defaultColor;
}
// ...and modified these methods to do the same
@HostListener('mouseenter') mouseEnters(eventData: Event){
this.backgroundColor = this.highlightColor;
}
@HostListener('mouseleave') mouseLeaves(eventData: Event){
this.backgroundColor = this.defaultColor;
}
we can bind to defaultColor and highlightColor, passing strings in that override the default values of 'transparent' and 'blue'
app.component.html
<p
appBetterHighlight
[defaultColor]="'yellow'"
[highlightColor]="'red'"
>Style me with a better directive</p>
if you have one property to bind you can provide an alias (here we added 'appBetterHighlight'
to @Input('appBetterHighlight') highlightColor: string = 'blue';
). This would change the implementation from this...
<p
appBetterHighlight
[defaultColor]="'yellow'"
[highlightColor]="'red'"
>Style me with a better directive</p>
...to this...
<p
[appBetterHighlight]="'red'"
[defaultColor]="'yellow'"
>Style me with a better directive</p>
Note that this is only an optional thing.
Another note about roperty binding...if you pass down a string using square brackets and single quotes...you can take a shortcut, removing square brackets and single quotes (remember kids, strings only). So [defaultColor]="'yellow'"
becomes defaultColor="yellow"
, making the final code as such...
<p
[appBetterHighlight]="'red'"
defaultColor="yellow"
>Style me with a better directive</p>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656178#overview
@HostBinding
decorator replaces renderer
code implemented in 97.
better-highlight.directive.ts
@HostBinding('style.backgroundColor') backgroundColor: string = 'transparent';
// ...and ...
@HostListener('mouseenter') mouseEnters(eventData: Event){
this.backgroundColor = 'blue';
}
@HostListener('mouseleave') mouseLeaves(eventData: Event){
this.backgroundColor = 'transparent';
}
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656176#overview
Imported HostListener
and implemented it. Note that mouseenter
and mouseleave
are event names that HostListener recogizes, and that mouseEnters
and mouseLeaves
are unique method names and can be anything
@HostListener('mouseenter') mouseEnters(eventData: Event){
this.renderer.setStyle(
this.elRef.nativeElement,
'background-color',
'blue');
}
@HostListener('mouseleave') mouseLeaves(eventData: Event){
this.renderer.setStyle(
this.elRef.nativeElement,
'background-color',
'transparent');
}
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6730946#overview
In the last lecture, we used the Angular Renderer class to change the style of a HTML element. As explained in that lecture, you should use the Renderer for any DOM manipulations.
Of course, you can do more than simply change the styling of an element via setStyle(). Learn more about the available Renderer methods here >> https://angular.io/api/core/Renderer2
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656172#overview
- Command
ng g d better-highlight
generates the new directive (and a test that I deleted). File was moved into subfolderbetter-highlight
. - Renderer2 is injected into the constructor method...as with the previous method, scoping the var to
private
allows us to access it in other methods in this object/directive. - setStyle() method is used to change the background-color to blue
- why is this a better approach? angular is not limited to web...as such some apps might not have access to the DOM.
import { Directive, OnInit, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {
constructor(
private elRef: ElementRef,
private renderer: Renderer2
) { }
ngOnInit() {
this.renderer.setStyle(
this.elRef.nativeElement,
'background-color',
'blue');
}
}
Import BetterHighlightDirective using import { BetterHighlightDirective } from './better-highlight/better-highlight.directive';
and also add it to the declarations array
import { AppComponent } from './app.component';
import { BasicHighlightDirective } from './basic-highlight/basic-highlight.directive';
import { BetterHighlightDirective } from './better-highlight/better-highlight.directive';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
@NgModule({
declarations: [
AppComponent,
BasicHighlightDirective,
BetterHighlightDirective
],
imports: [
BrowserModule,
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Implement the directive
<p appBetterHighlight>Style me with a better directive</p>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/13193718#overview
- ngClass and ngStyle are attribute directives
- build a directive that highlights an element that we hover over
- created new subfolder of app
basic-highlight
- created new file
basic-highlight.directive.ts
This defines our custom attribute directive. selector: '[appBasicHighlight]'
defines our selector. In this example we could implement this directive in a template using <p appBasicHighlight>I respect your mother</p>
where appBasicHighlight
is a unique selector. The square brackets around the selector name allows us to apply the directive to any tag that contains the directive name. If the brackets were not present TODO: define what the behavior is and note sample usage when square brackets are not used.
We inject the element that the directive sits on by passing it into the constructor function constructor(private elementRef: ElementRef){}
where private elementRef: ElementRef
is the element reference argument. To use the data everywhere in the class (outside of the constructor) we specify that the argument is private
. We are doing just that in function ngOnInit
.
import { Directive, ElementRef, OnInit } from "@angular/core";
@Directive({
selector: '[appBasicHighlight]'
})
export class BasicHighlightDirective implements OnInit {
constructor(private elementRef: ElementRef){}
ngOnInit() {
this.elementRef.nativeElement.style.backgroundColor = 'orange';
}
}
Inform Angular that we have this directive. We must import it and declare it. We import it via import { BasicHighlightDirective } from './basic-highlight/basic-highlight.directive';
and declare it in the declarations
array in the @NgModule
call.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BasicHighlightDirective } from './basic-highlight/basic-highlight.directive';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
BasicHighlightDirective
],
imports: [
BrowserModule,
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
To implement this new directive, simply add it as an attribute of the tag where you want to apply it..
<p appBasicHighlight>Style me with basic directive</p>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656166#overview
Using [ngClass]="{odd: odd % 2 == 0}"
in app.template...
<div *ngIf="onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 !== 0}"
*ngFor="let odd of oddNumbers"
>{{ odd }}</li>
</div>
<div *ngIf="!onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 == 0}"
*ngFor="let even of evenNumbers"
>{{ even }}</li>
</div>
using [ngStyle]="{backgroundColor: odd % 2 !== 0?'yellow':'transparent'}"
<div *ngIf="onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 !== 0}"
[ngStyle]="{backgroundColor: odd % 2 !== 0?'yellow':'transparent'}"
*ngFor="let odd of oddNumbers"
>{{ odd }}</li>
</div>
<div *ngIf="!onlyOdd">
<li
class="list-group-item"
[ngClass]="{odd: odd % 2 == 0}"
[ngStyle]="{backgroundColor: odd % 2 == 0?'yellow':'transparent'}"
*ngFor="let even of evenNumbers"
>{{ even }}</li>
</div>
https://www.udemy.com/course/the-complete-guide-to-angular-2/learn/lecture/6656164#overview
app template - loops through the numbers array and displays the current array element
<ul class="list-group">
<li
class="list-group-item"
*ngFor="let number of numbers"
>{{ number }}</li>
</ul>
app component - declare the array of numbers
numbers = [1, 2, 3, 4, 5];
app template - conditional display of one array or another based on the value of boolean variable onlyOdd
...
<div class="container">
<div class="row">
<div class="col-xs-12">
<button
class="btn btn-primary"
(click)="onlyOdd = !onlyOdd"
>toggle odd/even numbers</button>
<br><br>
<ul class="list-group">
<div *ngIf="onlyOdd">
<li
class="list-group-item"
*ngFor="let odd of oddNumbers"
>{{ odd }}</li>
</div>
<div *ngIf="!onlyOdd">
<li
class="list-group-item"
*ngFor="let even of evenNumbers"
>{{ even }}</li>
</div>
</ul>
</div>
</div>
app component, declare the odd and even arrays...
oddNumbers = [1, 3, 5, 7, 9, 11];
evenNumbers = [2, 4, 6, 8];
onlyOdd = false;
Attribute Directives vs Structural Directives
- attribute: because they sit on elements
- structural: same, but they change the structure of the DOM
sudo npm i -g npm@6
sudo npm install
ng serve