Slide 1

Slide 1 text

Building and Publishing Angular Libraries Yadong @yadong_xie Alibaba Senior Front-end Engineer

Slide 2

Slide 2 text

Yadong @yadong_xie Alibaba Senior Front-end Engineer 63+ Components 35+ Languages 87K / month 300,000 users 5500+ dependents https://github.com/NG-ZORRO/ng-zorro-antd

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Should We Build Our Angular Library? • Meet all requirements • Full Control Pros • Hard to implement • Maintenance cost Cons

Slide 6

Slide 6 text

A Simple Component Progress

Slide 7

Slide 7 text

Simple Component [style.width.%]=“percent”
@Input() percent = 0; View ViewModel

Slide 8

Slide 8 text

[style.width.%]=“percent”
@Input() percent = 0; View ViewModel @Input() radio = 0; get percent() { return this.radio * 100 }; Model

Slide 9

Slide 9 text

Model View ViewModel Angular is a MVVM framework

Slide 10

Slide 10 text

Simple Component @Component({ selector: 'ngx-progress', template: `
` }) export class NgxProgressComponent { @Input() progress = 0; } ViewModel View

Slide 11

Slide 11 text

Component with Different View const collections = [ { name: 'Tab 1', selected: true }, { name: 'Tab 2', selected: false }, { name: 'Tab 3', selected: false } ]; const collections = [ { name: 'A', selected: true }, { name: 'B', selected: false }, { name: 'C', selected: false } ]; const collections = [ { name: 'lucy', selected: true }, { name: 'lily', selected: false }, { name: 'tom', selected: false } ];

Slide 12

Slide 12 text

Different View & Same ViewModel Single Selection

Slide 13

Slide 13 text

Correct ViewModel is the key to component

Slide 14

Slide 14 text

Complex View Problem

Slide 15

Slide 15 text

Clipping Detection

Slide 16

Slide 16 text

Component Dev Kit @angular/cdk a11y bidi corecion collections keycodes layout observers overlay platform portal scrolling stepper table text-field tree

Slide 17

Slide 17 text

Clipping Detection overlay constructor( public elementRef: ElementRef, private overlay: Overlay, private platform: Platform ) { this.overlay.create( new OverlayConfig({ positionStrategy: this.overlay .position() .flexibleConnectedTo(this.elementRef.nativeElement), scrollStrategy: this.overlay.scrollStrategies.reposition() }) ); } Fix

Slide 18

Slide 18 text

Screen Breakpoint layout @Component({ ... }) export class MyWidget { isHandset: Observable; constructor(bm: BreakpointObserver) { bm.observe(Handset).subscribe((state: BreakpointState) => { if (state.matches) { this.makeEverythingFitOnSmallScreen(); } else { this.expandEverythingToFillTheScreen(); } }); } } Fix

Slide 19

Slide 19 text

Extremely Complex View Problem

Slide 20

Slide 20 text

With Third-Party View Lib Make ngx-anything Lib Yourself

Slide 21

Slide 21 text

Make ngx-markdown Yourself 1. markdown parser

Slide 22

Slide 22 text

2. Read the Document html = marked.parse(string) Make ngx-markdown Yourself

Slide 23

Slide 23 text

import { Directive, Input, OnChanges, ElementRef } from "@angular/core"; import * as marked from "marked"; @Directive({ selector: “[markdown]" }) export class MarkdownDirective implements OnChanges { @Input() text: string; constructor(private elementRef: ElementRef) {} ngOnChanges() { this.elementRef.nativeElement.innerHTML = marked.parse(this.text); } } Make ngx-markdown Yourself 3. Source Code ViewModel View

Slide 24

Slide 24 text

Make ngx-markdown Yourself import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `
` }) export class AppComponent { text = ` # Markdown Editor This is a demo of \`ngx-markdown\` with [marked](https:// marked.js.org/) `; } 4. Demo https://stackblitz.com/edit/ngx-markdown-demo

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Template-Driven Model-Driven VS

Slide 27

Slide 27 text

--Please choose an option-- Dog Cat Hamster Parrot Spider Goldfish Template Driven

Slide 28

Slide 28 text

@Component({ template: ` ` }) export class NgxSelectComponent { options = [ { value: '', label: '--Please choose an option--' }, { value: 'dog', label: 'Dog' }, { value: 'cat', label: 'Cat' }, { value: 'hamster', label: 'Hamster' }, { value: 'parrot', label: 'Parrot' }, { value: 'spider', label: 'Spider' }, { value: 'goldfish', label: 'Goldfish', disabled: true } ]; } Model-Driven

Slide 29

Slide 29 text

Template Driven Model-Driven @Component({ selector: 'app-root', template: ` ` }) export class AppComponent { options = [ { value: 'jack', label: 'Jack', disabled: false }, { value: 'lucy', label: 'Lucy', disabled: false }, { value: 'disabled', label: 'Disabled', disabled: true } ]; } @Component({ selector: 'app-root', template: ` {{o.label}} ` }) export class AppComponent { options = [ { value: 'jack', label: 'Jack', disabled: false }, { value: 'lucy', label: 'Lucy', disabled: false }, { value: 'disabled', label: 'Disabled', disabled: true } ]; } VS VS

Slide 30

Slide 30 text

@Component({ selector: 'app-root', template: ` {{o.label}} ` }) export class AppComponent { options = [ { value: 'jack', label: 'Jack', disabled: false }, { value: 'lucy', label: 'Lucy', disabled: false }, { value: 'disabled', label: 'Disabled', disabled: true } ]; } Angular Blog MDN web docs Event Reference MDN web docs Attributes Reference MDN web docs CSS Reference Event Reference: 196+ Attributes Reference: 122+ Building on Existing Technologies and Communities

Slide 31

Slide 31 text

Theme

Slide 32

Slide 32 text

Theme "options": { "styles": [ "src/styles.less" ] } angular.json sass | less | stylus | … Global Style CSS preprocessor : @Component({ … styleUrls : [ "./style.less" ] }) component Component Style VS

Slide 33

Slide 33 text

styles : [ ` ngx-progress { display: inline-block; width: 100%; background-color: #f5f5f5; } .fill { height: 8px; background-color: #1890ff; } ` ] { encapsulation: 0, styles : [ 'ngx-progress {\n display: inline-block;\n width: 100%;\n background-color: #f5f5f5;\n } \n\n .fill[_ngcontent-%COMP%] {\n height: 8px;\n background-color: #1890ff;\n }' ], data : {} } Tree Shakeable Can’t Change Parameter at Runtime Component Style Runtime

Slide 34

Slide 34 text

Theme progress.less progress-theme.less Progress Component styleUrls : [ './progress.less' ] @import "../theme.less"; ngx-progress { color: @background-color-base; } .progress-inner { color: @primary-color; } ngx-progress { display: inline-block; width: 100%; } .progress-inner { height: 8px; } progress.less progress-theme.less Progress Component styleUrls : [ './progress.less' ] @import "../theme.less"; ngx-progress { background: @background-color-base; } .fill { background: @primary-color; } ngx-progress { display: inline-block; width: 100%; } .fill { height: 8px; } theme.less less rewrite pre-built @primary-color : #1890FF; @background-color-base : #F5F5F5;

Slide 35

Slide 35 text

Optimization • Track By Function • runOutsideAngular • Avoid Function Calls in Views • Pipes • OnPush Performance Package Size Angular Guru Mgechev Blog Links: • Less Dependency • provideIn • No export default Angular Package Format CK’s Notepad Neufund Blog Links:

Slide 36

Slide 36 text

Schematics & Snippets https://github.com/ng-alain/ng-zorro-vscode Links: Angular Blog ng g ng-zorro-antd:layout-top

Slide 37

Slide 37 text

Internationalization Date @angular/common/locales date-fns LTR/RTL @angular/cdk/bidi Local Text pipes

Slide 38

Slide 38 text

Internationalization export default [ 'ja', [['午前', '午後'], u, u], u, [ ['⽇', '⽉', '⽕', '⽔', '⽊', '⾦', '⼟'], u, ['⽇曜⽇', '⽉曜⽇', '⽕曜⽇', '⽔曜⽇', '⽊曜⽇', '⾦曜⽇', '⼟曜⽇'], ['⽇', '⽉', '⽕', '⽔', '⽊', '⾦', '⼟'] ], u, [ ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], [ '1⽉', '2⽉', '3⽉', '4⽉', '5⽉', '6⽉', '7⽉', '8⽉', '9⽉', '10⽉', '11⽉', '12⽉' ], u ], u, [['BC', 'AD'], ['紀元前', '⻄暦'], u], 0, [6, 0], ['y/MM/dd', u, 'y年M⽉d⽇', 'y年M⽉d⽇EEEE'], ['H:mm', 'H:mm:ss', 'H:mm:ss z', 'H時mm分ss秒 zzzz'], ['{1} {0}', u, u, u], ['.', ',', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'], ['#,##0.###', '#,##0%', '¤#,##0.00', '#E0'], '¥', '⽇本円', {'CNY': ['元', '¥'], 'JPY': ['¥'], 'RON': [u, 'レイ']}, plural ]; export default [ 'ko-KP', [['AM', 'PM'], u, ['য়੹', 'য়റ']], u, [ ['ੌ', 'ਘ', 'ച', 'ࣻ', 'ݾ', 'Ә', 'ష'], u, ['ੌਃੌ', 'ਘਃੌ', 'ചਃੌ', 'ࣻਃੌ', 'ݾਃੌ', 'Әਃੌ', 'షਃੌ'], ['ੌ', 'ਘ', 'ച', 'ࣻ', 'ݾ', 'Ә', 'ష'] ], u, [ [ '1ਘ', '2ਘ', '3ਘ', '4ਘ', '5ਘ', '6ਘ', '7ਘ', '8ਘ', '9ਘ', '10ਘ', '11ਘ', '12ਘ' ], u, u ], u, [['BC', 'AD'], u, ['ӝਗ੹', 'ࢲӝ']], 1, [6, 0], ['yy. M. d.', 'y. M. d.', 'y֙ Mਘ dੌ', 'y֙ Mਘ dੌ EEEE'], ['a h:mm', 'a h:mm:ss', 'a hद m࠙ sୡ z', 'a hद m࠙ sୡ zzzz'], ['{1} {0}', u, u, u], ['.', ',', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'], ['#,##0.###', '#,##0%', '¤#,##0.00', '#E0'], 'KPW', 'ઑࢶ ޹઱઱੄ ੋ޹ ҕചҴ ਗ', { 'AUD': ['AU$', '$'], 'JPY': ['JP¥', '¥'], 'RON': [u, 'L'], 'TWD': ['NT$'], 'USD': ['US$', '$'] }, plural ]; export default [ 'ar-EG', [['م' ,'ص'], u, u], [['م' ,'ص'], u, ['ً ءﺎﺴﻣ' ,'ﺎً ﺣﺎﺒﺻ']], [ ['س' ,'ج' ,'خ' ,'ر' ,'ث' ,'ن' ,'ح'], [ 'ﺲﯿﻤﺨﻟا' ,'ءﺎﻌﺑرﻷا' ,'ءﺎﺛﻼﺜﻟا' ,'ﻦﯿﻨﺛﻻا' ,'ﺪﺣﻷا', 'ﺖﺒﺴﻟا' ,'ﺔﻌﻤﺠﻟا' ], u, ['ﺖﺒﺳ' ,'ﺔﻌﻤﺟ' ,'ﺲﯿﻤﺧ' ,'ءﺎﻌﺑرأ' ,'ءﺎﺛﻼﺛ' ,'ﻦﯿﻨﺛإ' ,'ﺪﺣأ'] ], u, [ ['د' ,'ب' ,'ك' ,'س' ,'غ' ,'ل' ,'ن' ,'و' ,'أ' ,'م' ,'ف' ,'ي'], [ 'ﻮﯿﻧﻮﯾ' ,'ﻮﯾﺎﻣ' ,'ﻞﯾﺮﺑأ' ,'سرﺎﻣ' ,'ﺮﯾاﺮﺒﻓ' ,'ﺮﯾﺎﻨﯾ', 'ﺮﺒﻤﺴﯾد' ,'ﺮﺒﻤﻓﻮﻧ' ,'ﺮﺑﻮﺘﻛأ' ,'ﺮﺒﻤﺘﺒﺳ' ,'ﺲﻄﺴﻏأ' ,'ﻮﯿﻟﻮﯾ' ], u ], u, [['م' ,'م.ق'], u, ['يدﻼﯿﻣ' ,'دﻼﯿﻤﻟا ﻞﺒﻗ']], [6 ,5] ,6, ['d\u200f/M\u200f/y', 'dd\u200f/MM\u200f/y', 'd MMMM y', 'EEEE، d MMMM y'], ['h:mm a', 'h:mm:ss a', 'h:mm:ss a z', 'h:mm:ss a zzzz'], ['{1} {0}', u, u, u], [ '.', ',', ';', '\u200e%\u200e', '\u200e+', '\u200e-', 'E', '×', '‰', '∞', @angular/common/locales Don't Forget

Slide 39

Slide 39 text

Doc Generator aio Deploy Document

Slide 40

Slide 40 text

• Angular Package Format • ng-packagr • Semantic Versioning • Generate Release Note Release

Slide 41

Slide 41 text

Maintenance Mono Repo Multiple Repo • Better Testing • Code Reviews • Share Common Modules • Easy Refactoring • Clear Ownership • Better Scale • Narrow Clones VS

Slide 42

Slide 42 text

Lint Husky & angular commit message codelyzer & tslint

Slide 43

Slide 43 text

CI & Test Travis AzurePipeline Codecov

Slide 44

Slide 44 text

Thank you