Last updated: November 15, 2020 8:44 PM
Angular 8 Demo Project Log and Experience Part 3.
Decorator(Annotation)
Annotation or Decorator is a function, which returns a function.
Create a customer decorator @Emoji
export function Emoji() {
return (target: object, key: string) => {
let val = target[key]; // let val get the target's value. key is the index.
const getter = () => {
return val;
};
// user setter to change the value
const setter = (value: string) => {
val = `😂 ${value} 😂`;
};
// append the property to the target
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
Thus, this decorator @Emoji
can be import to other component and use it.
@Emoji() result = 'Hello';
The result output is 😂 Hello 😂
;
Create a customer decorator @Confirmable(’…’)
This decorator contains parameter.
export function Confirmable(message: string) {
return (target: object, key: string, descriptor: PropertyDescriptor) => {
const original = descriptor.value;
descriptor.value = function(...args: any) {
const allow = window.confirm(message);
if (allow) {
const result = original.apply(args);
return result;
}
return null;
};
return descriptor;
};
}
To use this decorator.
@Confirmable('Are you sure to do something?')
handleClick() {
console.log('handle click works');
}
When clicking the button, we want to pop up a window to warn our user.
Directive
Kinds
There are three kinds of directives in Angular:
- Components: directives with a template.
- Structural directives: change the DOM layout by adding and removing DOM elements. Change the structure of the view,
ngFor
,ngIf
,ngSwitch
. - Attribute directives: change the appearance or behavior of an element, component, or another directive.Used as attributes of elements,
ngClass
,ngStyle
,ngModel
.
Create Custom Directives
Create a file gird-item.directive.ts
, in this file use a shortcut ng-directive
to generate a directive. Then a template can be generated as below.
import { Directive } from '@angular/core';
@Directive({
selector: 'appGridItem'
})
export class GridItemDirective {}
The selector name is wrong, it need to be an Attribute. Fix the problem to change the name to attribute:
@Directive({
selector: '[appGridItem]',
})
Also, we need to add its deceleration in shared.component.ts
and also export it.
The code to implement the directive:
import { Directive } from '@angular/core';
@Directive({
selector: '[appGridItem]'
})
export class GridItemDirective {
constructor(private elr: ElementRef, private rd2: Renderer2) {
this.rd2.setStyle(this.elr.nativeElement, 'display', 'grid');
this.rd2.setStyle(
this.elr.nativeElement,
'grid-template',
`'image' 'title'`
);
this.rd2.setStyle(this.elr.nativeElement, 'place-itmes', 'center');
this.rd2.setStyle(this.elr.nativeElement, 'width', '4rem');
}
}
The best practice is to set styles in the ngOnInit
, not in the constructor
.
constructor (
private elr: ElementRef,
private rd2: Renderer2
) {}
ngOnInit() {
this.rd2.setStyle(this.elr.nativeElement, 'display', 'grid');
this.rd2.setStyle(this.elr.nativeElement, 'grid-template-areas', `'image' 'title'`);
this.rd2.setStyle(this.elr.nativeElement, 'place-items', 'center');
this.rd2.setStyle(this.elr.nativeElement, 'width', '4rem');
}
Use Directives
We can create appGridItemImage
and appGridItemTitle
directives.
Directives are like properties. Now we can use these directives in the template of HorizontalGridComponent
.
<div appGridItem *ngFor="let item of channels">
<img [src]="item.imgUrl" alt="" appGridItemImage />
<span appGridItemTitle></span>
</div>
Summary
Directives like properties, which can be applied to HTML tags.
Style Binding / Event Binding use Directive
There is no template in directive. Directive needs a host(an element). In Angular, @HostBinding
can be used to bind attributes and styles to host, @HostListener
can be used to bind events to host.
@HostBinding
We use rd2.setStyle
to set styles of host, now we can use @HostBinding
to refactor.
export class GridItemDirective {
@HostBinding('style.display') display = 'grid';
@HostBinding('style.grid-template-areas') template = `'image' 'title'`;
@HostBinding('style.place-items') align = 'center';
@HostBinding('style.width') width = '4rem';
}
@HostListener
It accepts two params, one is eventName(The CSS event to listen for), another is event data (args, A set of arguments to pass to the handler method when the event occurs).
@HostListener('click', ['$event.target'])
onClick(ev) {
console.log('event: ', ev);
}
We don’t need to add additional event binding in template. What we do is just click the image, the @HostListener
is listing to the click event.
<div appGridItem *ngFor="let item of channels">
<img [src]="item.icon" alt=""
[appGridItemImage]="'4rem'"
[fitMode]="'cover'">
<span appGridItemTitle="0.6rem" class="title"></span>
</div>
:host
:host is used to design component styles in .component.css. It is a pseudo-classes selector. For example,
:host {
display: flex;
justify-content: center;
}
The pattern defined by :host is applied to the component itself.