Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building and Publishing Angular Libraries

Yadong Xie
December 17, 2019

Building and Publishing Angular Libraries

In the early days of Angular, the development of the Angular component library was a rather complicated task, but the threshold for building component libraries has been significantly reduced now. This sharing will show you how to build a qualified Angular component library from a different perspective.

Yadong Xie

December 17, 2019
Tweet

More Decks by Yadong Xie

Other Decks in Programming

Transcript

  1. 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
  2. Should We Build Our Angular Library? • Meet all requirements

    • Full Control Pros • Hard to implement • Maintenance cost Cons
  3. [style.width.%]=“percent” <div class=“fill”> <div class=“background”> @Input() percent = 0; View

    ViewModel @Input() radio = 0; get percent() { return this.radio * 100 }; Model
  4. Simple Component @Component({ selector: 'ngx-progress', template: ` <div class="fill" [style.width.%]="progress"></div>

    ` }) export class NgxProgressComponent { @Input() progress = 0; } ViewModel View
  5. 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 } ];
  6. Component Dev Kit @angular/cdk a11y bidi corecion collections keycodes layout

    observers overlay platform portal scrolling stepper table text-field tree
  7. 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
  8. Screen Breakpoint layout @Component({ ... }) export class MyWidget {

    isHandset: Observable<BreakpointState>; constructor(bm: BreakpointObserver) { bm.observe(Handset).subscribe((state: BreakpointState) => { if (state.matches) { this.makeEverythingFitOnSmallScreen(); } else { this.expandEverythingToFillTheScreen(); } }); } } Fix
  9. 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
  10. Make ngx-markdown Yourself import { Component } from '@angular/core'; @Component({

    selector: 'app-root', template: `<div markdown [text]="text"></div>` }) 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
  11. <select name="pets"> <option value="">--Please choose an option--</option> <option value="dog">Dog</option> <option

    value="cat">Cat</option> <option value="hamster">Hamster</option> <option value="parrot">Parrot</option> <option value="spider">Spider</option> <option value="goldfish" disabled>Goldfish</option> </select> Template Driven
  12. @Component({ template: ` <ngx-select [options]="options"></ngx-select> ` }) 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
  13. Template Driven Model-Driven @Component({ selector: 'app-root', template: ` <ngx-select [options]="options"></ngx-select>

    ` }) 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: ` <select> <option *ngFor="let o of options" [value]="o.value" [disabled]=“o.disabled”> {{o.label}} </option> </select> ` }) export class AppComponent { options = [ { value: 'jack', label: 'Jack', disabled: false }, { value: 'lucy', label: 'Lucy', disabled: false }, { value: 'disabled', label: 'Disabled', disabled: true } ]; } VS VS
  14. @Component({ selector: 'app-root', template: ` <select> <option *ngFor="let o of

    options" [attr.title]="o.label" [class.gray]="o.disabled" [value]="o.value" [disabled]=“o.disabled”> {{o.label}} </option> </select> ` }) 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
  15. Theme "options": { "styles": [ "src/styles.less" ] } angular.json sass

    | less | stylus | … Global Style CSS preprocessor : @Component({ … styleUrls : [ "./style.less" ] }) component Component Style VS
  16. 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
  17. 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;
  18. 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:
  19. 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
  20. Maintenance Mono Repo Multiple Repo • Better Testing • Code

    Reviews • Share Common Modules • Easy Refactoring • Clear Ownership • Better Scale • Narrow Clones VS