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

Building and Publishing Angular Libraries

Avatar for Yadong Xie 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.

Avatar for Yadong Xie

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