microfrontends
microservices
core library
Run ng test my-app
to execute the unit tests via Jest.
Run nx affected:test
to execute the unit tests affected by a change.
Run ng e2e my-app
to execute the end-to-end tests via Cypress.
Run nx affected:e2e
to execute the end-to-end tests affected by a change.
$ git commit
$ git commit -m '[upd] version ...;'
$ git tag v...
$ git commit -m '[upd] version ...;'
$ git push --tag
$ git push
Pure
it means that pipe don't mutation input date (without side effects)
Strategy to OnPush. This means that change detection inside the Component will now be
triggered only when its @Input properties change.
We create apure custom pipe
to perform the required calculation only when the
pipe’s input changes. Uses the pipe instead of calling the method into templates of components.
If pipe is pure we can cache the calculations instead of recalculating them
@memo()
использование переменной для из директивы для вызова различного функционала
<div *carousel="let url from images; let ctrl = controller">
<button (click)="ctrl.next()">
const context = {
$implicit: null ,
controller: {
next: () => this.next() // next: this.next.bind(this, param);
}
}
получение host component в директиве
// дочерняя директива/компонент совершенно спокойно может получить инстанс родителя через DI
import { Directive } from "@angular/core"; import { CountComponent } from "./count.component";
@Directive({ selector: "[increment]" })
export class IncrementDirective {
constructor(private countComponent: CountComponent) {
this.countComponent.count += 1;
}
}
использование ngTemplateOutlet для проекции контента
<div class="content">
<ng-container
[ngTemplateOutlet]="itemTemplateRef"
[ngTemplateOutletContext]="{ $implicit: item, method1: myMethod }" ><!-- переменная, которая передается в шаблон -->
</ng-container>
</div>
<!-- использование с передачей компонента внутри ng-template -->
<app-container>
<ng-template #itemTemplate let-item let-method="method1"> <!-- переменная полученная шаблоном -->
<app-other-component [inputProp]="item"></app-other-component>
</ng-template>
</app-container>
import { ContentChild, TemplateRef } from '@angular/core'
// selector: 'app-container'
// ...
@ContentChild('itemTemplate', { static: false }) item: TemplateRef<any>;
<div class="container" *context="let method = method">
<ngn [context]="{method}"> </ngn>
<button
fireDirective
(outputEvent)="method($event)">
<!-- outputEvent: EventEmitter<Facade>
fireDirective.HostListener.clickOrAnyEvent() => outputEvent.emit(facade);
...
contextDirective.method(event) => event is Facade; // true
-->
Ok
</button>
</div>
// InjectToken
import { InjectionToken, ContentChild, Inject, NgModule, Component } from '@angular/core';
type OrderAction;
export const ORDER_ACTION = new InjectionToken<OrderAction>('OrderAction');
class Service {
public constructor(@Inject(ORDER_ACTION) private orderAction: OrderAction) {}
}
export const OTHER_ACTION: OrderAction = {/*... */};
@NgModule({
providers: [{ provide: ORDER_ACTION, useValue: OTHER_ACTION }]
})
export class AppModule {}
@Component({
providers: [
{
provide: ORDER_ACTION,
useExisting: CComponent,
},
],
})
class CComponent implements OrderAction {
@ContentChild(ORDER_ACTION, { static: true }) orderAction: "OrderAction"; // this OTHER_ACTION for AppModule
}
import { Directive, Inject} from "@angular/core";
abstract class AbstractTuiTableFilter<T, K> {}
///..
@Directive({
selector: `[tuiGenericFilter]`,
providers: [
{
provide: AbstractTuiTableFilter,
useExisting: TuiGenericFilterDirective,
},
],
})
class TuiGenericFilterDirective<T> {}
///...
class ComponentWithDirective<T> {
constructor(@Inject(AbstractTuiTableFilter)private delegate: AbstractTuiTableFilter<T[keyof T], any>) { }
}
Extends Model and templated transform method of pipe
import { inject,Pipe,PipeTransform } from '@angular/core'; import { Observable} from 'rxjs';
@Pipe({ name: 'filterUsers', pure: true, standalone: true })
export class FilterUsersPipe implements PipeTransform {
private filter = inject(FILTER_USERS_PIPE);
transform<T extends User>(value: Observable<T[]>): Observable<T[]> {
return this.filter(value) as Observable<T[]>;
}
}
`<ng-container *http="let hotTabs; get: url; typeof: ExUser">
<ng-container *ngFor="let tabContent of hotTabs | filterUsers | async">` // tabContent is ExUser
`<ng-template [ngTemplateContextGuard]="guardFn" let-article>`
export class FooComponent {
public readonly guardFn = (ctx: any): ctx is Context<Article> => true;
}
async next observable
import { ReplaySubject} from 'rxjs';
const editor$ = new ReplaySubject<typeof Editor>(1);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
import(`@tiptap/core`).then(m => editor$.next(m.Editor));
using (runOutsideAngular)
- addEventListener
- removeEventListener
-
HttpInterceptor
gives you more freedom regarding mocking data from external libraries -
JSON server
is a JavaScript library for mocking REST APIs.$ npm install json-server
-
Cookies
write a script for your middleware (e.g. APIC) that checks for specific cookies e.g.
errorapi, errorcode and errorbody. -
JSON generators
- auto mocker-data-generator, json-generator.com
- manual faker.js
- [rx] don't use *ngIf (don't create inside variable | async as variable)
- [naming] use 'create' prefix for method except for only methods using inside inject
create...() { let value = { prop: inject(TOKEN)}}
- [naming] don't use redundant 'postfix' entity name:
StrategyScroll -> dispatcherScroll (incorrect)
StrategyScroll -> dispatcher (correct) - [naming] method interactions:
internal methods have short name without postfix of main type
external methods have this is postfix - [dci] create roles(methods):
if method takes parameters it must returns UnaryFunction for pipe
- [dci] data:
all utilities instead of the returned result should set value to same named property of
ChangesType<T>
- [dci] encapsulation:
add data for encapsulate in object that pass to
tube(...)({ overlay, encapsulateObject})
this avoids enriching the object whose data must be returned from the method