KEMBAR78
Build Your Own Angular Component Library | PDF
So you want to build your (Angular)
Component Library? We can help
Carlo Bonamico - Sonia Pini
Milan | November 29 - 30, 2018
Components are the core of Web Apps
Self-contained set of UI and logic
○ encapsulate a specific behaviour
○ provide an explicit API
better than
Advantages of Components
Stronger Encapsulation
● explicit @Input and @Output bindings
● changing the component internal implementation has less
impact on the rest of the application
● more decoupling, less regressions
Reusability
● same component used in different contexts
● with different parameters or inputs
Advantages of Components
Better Collaboration
● less conflicts when team grows
● easier to test for regressions
More Clarity and Readability
● I can effectively use a component knowing only its API (the
bindings section)
● the link to other components is very clear and explict in the
HTML
Also, the component tree is very good for performance
<mail-view>
<message-list>
<nav-actions><common-star>
<message-view>
<common-star>
Why build a
Component Library?
Most of the concepts in this talk apply to any framework
although example syntax is based on Angular
Why a Component Library?
Starting point:
Provide a Uniform Visual Style
Why a Component Library?
● Higher productivity
○ Higher abstraction
○ Re-use across projects
○ Faster app build time
● Focus Documentation / Test effort
● Promote Cleaner Design and Best Practices
○ Separation of Responsibility
● Apps benefit from improvements in the Library
What kind of Component Library?
● Basic Components / Atoms
○ form labels
○ input controls
○ buttons <mat-button>
○ abstract elements: fonts, color palettes, animations, ...
http://atomicdesign.bradfrost.com/table-of-contents/
What kind of Component Library?
● Composed Components / Molecules
○ combining Base Components together
○ e.g. <lib-search-filter>
What kind of Component Library?
● Complex Components / Organisms
○ combining Basic and/or Composed Components together
○ Domain-independent es. <ht-table>, <shopping-cart>
○ Domain-specific es. <doc-sign-documents>, <contact-address-book>
Composability + Consistency = Great LIB
Functional Consistency
● Make your product more predictable
● Better UX
Visual Consistency
● Includes the colors, fonts, sizes, positions
Modularity / Composability
● the set is greater than the parts
API Consistency & Development Experience
● including Documentation and Packaging
● make developer more productive
Visual Consistency
Adopt or Provide Style Guide
Well defined colors, fonts, spacing
Davide Casali - Gestalt Design Principles for Devs
https://www.slideshare.net/folletto/gestalt-design-principles-for-developers
Adopt a naming convention for styles
● your own vs Block-Element-Modifier http://getbem.com
Use a CSS Pre-Processor (SCSS - LESS) to
● simplify uniform style definitions with @variables
● semi/automatically create themes or accessibility features
Library-level CSS should concentrate on
● variables for color palette, base font sizes, margin, padding
● page-level layout (e.g. Grid)
Component-level CSS
● encapsulation for easier maintenance
Organize Library Stylesheets
Separate Containers from content
Container Components focus on
● position/layout within the page or parent
○ <ht-sidebar-layout>, flex-based
● decorating the content
○ <ht-card>, <ht-accordion>, <ht-panel>
Component Design:
How to Configure a Component
<component>inputs outputs
Data
Config
Event
● Attributes
○ width, height, orientation
● Bindable @Inputs
○ mode, disabled <ht-button [disabled]=”!editable”>
● Library-Level Options
○ global defaults, from ConfigurationService or Injection Token
export const CONFIG = new InjectionToken<string>('config');
class DatePickerComponent {
constructor( @Inject(CONFIG) private configuration: Configuration) { }
ngOnInit() {
if (!this.dateFormat) this.dateFormat = this.configuration.defaultDateFormat;
}
Component Configuration
Component Design:
Passing Data to Components
<component>inputs outputs
Config
Data Event
Pattern: Data Flow “down”
Component bind a subset of their model to children
<mail-view>
<folder-list> <message-list>
<nav-actions><common-star>
<message-view>
<common-star>
folders
messages
currentMessage
Using @Input()
Using plain variables/arrays for @Input
● data owned by parent
● better for stateless / “dumb” components
<user-details [user]=”currentUser”>
Using Observable for @Input
● if you want to listen to changes continuously or
asynchronously
<realtime-chart [dataStream]=”stockValueStream” >
Parent Component
todoList: string[] = [ ]
Output → Push string into todoList
@Input() todos: string[]
Parent Component
todoList: Observable<string[]>
Output → Push string into todoList
@Input() todos: Observable<string[]>
Parent Component
todoList: Observable<string[]>
Output → Push string into todoList
@Input() todos: string[]
Use “| async”
Using DataSource in @Input
More complex components might need data refresh / drill-down
○ think <ht-table> with pagination, sorting, ...
Define a DataSource Interface
○ with all methods your component want use during its life
Provide an Implementation for the easier/common use-cases
○ ArrayDataSource, PagedDataSource
Document the DataSource Interface to guide developers
○ provide example of custom implementation
An Example from Angular Material
abstract DataSource<T>
○ connect(...): Observable<T[]>
○ disconnect(...): void
TableDataSource<T> extends DataSource<T>
○ page(...): Observable<T[]>
○ sort(...): Observable<T[]>
○ filter(...): Observable<T[]>
https://material.angular.io/components/table/api#MatTableDataSource
Multi-mode @Inputs
The component auto-adapts to the provided input
For example, to populate a mat-table we can use
○ a simple T[] if our data are static
○ an Observable<T[]>
○ a DataSource<T>
Component Design:
Interaction Patterns
<component>inputs outputs
Config
Data Event
Pattern: Events Flow “up”
Component pass relevant events/changes to parent
<mail-view>
<folder-list> <message-list>
<nav-actions><common-star>
<message-view>
<common-star>
onSelected()
onCurrentMsg()
onStar()
onReply()
onNext()
Never communicate directly: Publish events! key for low coupling
● towards Parent Component
@Output() selected = new EventEmitter<string>();
select(item) { this.selected.emit(item); }
● towards shared Observable Streams in a Service
○ e.g. ErrorService, AlertService
private errorSubject = new Subject<any>();
notifyError(code, message) {
this.errorSubject.next({
code: code,
message: message
});
}
Events & Common Services
In both cases, events
should express
● what's happened
● à la DDD Events
● intent, not what to
do
Separation of Responsibilities +
Component Interaction
Complex behaviour achieved through collaboration
<mail-view>
<folder-list> <message-list> <message-view>
onSelected()
onCurrentMsg()messages
currentMessage
Example: Error Modal Component
Modal
Component ErrorView
Component
ErrorModal
Component
Implement the Decorator Pattern to add cross-cutting
behaviour to a set of components
● <ht-modal>
● <ht-input-container>
○ provides label, required,
validation errors to an input
<ht-input-container>
<ht-input [(ngModel)]=...
</ht-input-container>
○ uses @ContentChild
to access the control
Decorator Component Pattern
API Design
API Design Challenges and Principles
● Your Library has two “Users”
○ End Users and Developers
○ think from both perspectives
● Joshua Block: Principle of Least Astonishment
○ How to Design Good APIs
https://www.youtube.com/watch?v=aAb7hSCtvGw
Names Matter
● Functionality should be easy to explain
○ express Intent
○ X is hard to name => symptom that X design needs refinement
■ too many responsibilities
■ unclear intent
○ Good names drive development
● Adopt Naming Conventions
○ library prefix in components name (e.g. ht-panel vs app-view)
○ prefix in property names vs HTML5 attributes (disabled, width...)
Beyond Names: Conceptual Consistency
● Same/standard APIs based on Types
○ LabelAndIcon interface
○ ControlValueAccessor for all input fields
● Same Abstractions
○ DataSources for <ht-table>, <ht-tree>, <ht-document-download>
○ conceptually similar event if cannot share same Type
Limit the amount of Mental Models that developers must learn
● Kathy Sierra on learning for effective products
https://www.youtube.com/watch?v=FKTxC9pl-WM
API Should Be As Small As Possible
● But no Smaller
○ focus on the most important use-cases
● Constant trade-off between Under and Over-Engineering
○ if in doubt, leave it out: you can add it later
● often adding an Extension Point is easier
○ less risky than a Feature
○ e.g. <ng-content> or publishing an Event on a Stream
Gather & Refine Requirements
● Extract true requirements from ‘proposed solutions’
■ Table Columns should be configured to display a Button / Link
○ becomes
■ “Table Columns supports a template to customize cell rendering”
● Think more general
● Keep the Spec short
Plan for the Future
● Expect to make mistakes
● Prepare to evolve API
○ Expand-Contract pattern
○ Deprecation / console.warn()
○ Design for Refactoring: Typescript helps!
○ Ease upgrade with tools (tslint for rxjs 5-> 6, schematics)
○ automatic tests
Documenting your
Library
Documentation Matters
Document every public component, interface, method, ...
● if it is not documented, “it does not exists” for developers
● Component, method, variable names should be as
self-explanatory as possible
● for all the rest, there is...
Compodoc
● Rich documentation with compodoc
○ https://compodoc.app/
○ compodoc -p ./tsconfig.json
-d docs
● Automatically generate docs for
○ Component, Classes
○ Routing, Modules
● MarkDown for (quick) guides
○ project setup / library import
○ Architecture - Conceptual model
○ Component LifeCycle
Document by Example
● Examples are key
○ even better if executable
● Plnkr
○ http://next.plnkr.co/
● Stackblitz
○ https://stackblitz.com/
● StoryBook
○ https://storybook.js.org/
● Angular Playground
○ http://www.angularplayground.it/
Packaging and
Distribution
A good Angular Library
● Is platform independent
○ can be consumend as UMD, FESM, ...
● Is bundled and distributed
● Works with AOT (Ahead of Time Compiler)
● Plays well with TypeScript and IDEs
○ autocompletion, compile-time checking
See Angular Package Format
Minko Gechev
@mgechev
screenshot
structure of the build
package in node
modules
Angular 6.x integrates David Herges’ ng-packagr
● workspace = apps + libs
ng generate library basiclib
ng generate app basiclib-demo
ng build basiclib
cd dist/basiclib
npm pack
npm publish basiclib.y.z.tgz
even live reload across lib & app
https://mereommig.dk/en/blog/adding-livereload-to-the-angular-cli-libraries
Creating and publishing
The Future of
(Component) Libraries
Future directions
● Angular Ivy Renderer
○ reusing a library does not require metadata processing
■ https://blog.angularindepth.com/inside-ivy-exploring-the-new-angular-compiler-ebf85141cee1
● Angular Elements
○ package Components as HTML5
Custom Elements (a.k.a. Web Components)
○ https://angular.io/guide/elements
● See also StencilJs Component Compiler
○ https://stenciljs.com/
References on Components
Codemotion 2016 Talk
https://www.slideshare.net/carlo.bonamico/angular-rebooted-components-every
where
Components in Angular 2
https://angular.io/docs/ts/latest/cookbook/component-communication.html
More on Angular
https://angularconnect.com/talks
Thank you!
● Other presentations
− http://www.slideshare.net/carlo.bonamico/presentations
● Follow us on Twitter
− @carlobonamico @nis_srl @Sonietta
● updates on Angular, Clean Code, Continuous Delivery
● Contact us
− carlo.bonamico@gmail.com / carlo.bonamico@nispro.it
− Sonia.pini@nispro.it
● Our company
− http://www.nispro.it

Build Your Own Angular Component Library

  • 1.
    So you wantto build your (Angular) Component Library? We can help Carlo Bonamico - Sonia Pini Milan | November 29 - 30, 2018
  • 2.
    Components are thecore of Web Apps Self-contained set of UI and logic ○ encapsulate a specific behaviour ○ provide an explicit API better than
  • 3.
    Advantages of Components StrongerEncapsulation ● explicit @Input and @Output bindings ● changing the component internal implementation has less impact on the rest of the application ● more decoupling, less regressions Reusability ● same component used in different contexts ● with different parameters or inputs
  • 4.
    Advantages of Components BetterCollaboration ● less conflicts when team grows ● easier to test for regressions More Clarity and Readability ● I can effectively use a component knowing only its API (the bindings section) ● the link to other components is very clear and explict in the HTML Also, the component tree is very good for performance <mail-view> <message-list> <nav-actions><common-star> <message-view> <common-star>
  • 5.
    Why build a ComponentLibrary? Most of the concepts in this talk apply to any framework although example syntax is based on Angular
  • 6.
    Why a ComponentLibrary? Starting point: Provide a Uniform Visual Style
  • 7.
    Why a ComponentLibrary? ● Higher productivity ○ Higher abstraction ○ Re-use across projects ○ Faster app build time ● Focus Documentation / Test effort ● Promote Cleaner Design and Best Practices ○ Separation of Responsibility ● Apps benefit from improvements in the Library
  • 8.
    What kind ofComponent Library? ● Basic Components / Atoms ○ form labels ○ input controls ○ buttons <mat-button> ○ abstract elements: fonts, color palettes, animations, ... http://atomicdesign.bradfrost.com/table-of-contents/
  • 9.
    What kind ofComponent Library? ● Composed Components / Molecules ○ combining Base Components together ○ e.g. <lib-search-filter>
  • 10.
    What kind ofComponent Library? ● Complex Components / Organisms ○ combining Basic and/or Composed Components together ○ Domain-independent es. <ht-table>, <shopping-cart> ○ Domain-specific es. <doc-sign-documents>, <contact-address-book>
  • 11.
    Composability + Consistency= Great LIB Functional Consistency ● Make your product more predictable ● Better UX Visual Consistency ● Includes the colors, fonts, sizes, positions Modularity / Composability ● the set is greater than the parts API Consistency & Development Experience ● including Documentation and Packaging ● make developer more productive
  • 12.
  • 13.
    Adopt or ProvideStyle Guide Well defined colors, fonts, spacing Davide Casali - Gestalt Design Principles for Devs https://www.slideshare.net/folletto/gestalt-design-principles-for-developers
  • 14.
    Adopt a namingconvention for styles ● your own vs Block-Element-Modifier http://getbem.com Use a CSS Pre-Processor (SCSS - LESS) to ● simplify uniform style definitions with @variables ● semi/automatically create themes or accessibility features Library-level CSS should concentrate on ● variables for color palette, base font sizes, margin, padding ● page-level layout (e.g. Grid) Component-level CSS ● encapsulation for easier maintenance Organize Library Stylesheets
  • 15.
    Separate Containers fromcontent Container Components focus on ● position/layout within the page or parent ○ <ht-sidebar-layout>, flex-based ● decorating the content ○ <ht-card>, <ht-accordion>, <ht-panel>
  • 16.
    Component Design: How toConfigure a Component <component>inputs outputs Data Config Event
  • 17.
    ● Attributes ○ width,height, orientation ● Bindable @Inputs ○ mode, disabled <ht-button [disabled]=”!editable”> ● Library-Level Options ○ global defaults, from ConfigurationService or Injection Token export const CONFIG = new InjectionToken<string>('config'); class DatePickerComponent { constructor( @Inject(CONFIG) private configuration: Configuration) { } ngOnInit() { if (!this.dateFormat) this.dateFormat = this.configuration.defaultDateFormat; } Component Configuration
  • 18.
    Component Design: Passing Datato Components <component>inputs outputs Config Data Event
  • 19.
    Pattern: Data Flow“down” Component bind a subset of their model to children <mail-view> <folder-list> <message-list> <nav-actions><common-star> <message-view> <common-star> folders messages currentMessage
  • 20.
    Using @Input() Using plainvariables/arrays for @Input ● data owned by parent ● better for stateless / “dumb” components <user-details [user]=”currentUser”> Using Observable for @Input ● if you want to listen to changes continuously or asynchronously <realtime-chart [dataStream]=”stockValueStream” >
  • 21.
    Parent Component todoList: string[]= [ ] Output → Push string into todoList @Input() todos: string[]
  • 22.
    Parent Component todoList: Observable<string[]> Output→ Push string into todoList @Input() todos: Observable<string[]>
  • 23.
    Parent Component todoList: Observable<string[]> Output→ Push string into todoList @Input() todos: string[] Use “| async”
  • 24.
    Using DataSource in@Input More complex components might need data refresh / drill-down ○ think <ht-table> with pagination, sorting, ... Define a DataSource Interface ○ with all methods your component want use during its life Provide an Implementation for the easier/common use-cases ○ ArrayDataSource, PagedDataSource Document the DataSource Interface to guide developers ○ provide example of custom implementation
  • 25.
    An Example fromAngular Material abstract DataSource<T> ○ connect(...): Observable<T[]> ○ disconnect(...): void TableDataSource<T> extends DataSource<T> ○ page(...): Observable<T[]> ○ sort(...): Observable<T[]> ○ filter(...): Observable<T[]> https://material.angular.io/components/table/api#MatTableDataSource
  • 26.
    Multi-mode @Inputs The componentauto-adapts to the provided input For example, to populate a mat-table we can use ○ a simple T[] if our data are static ○ an Observable<T[]> ○ a DataSource<T>
  • 27.
  • 28.
    Pattern: Events Flow“up” Component pass relevant events/changes to parent <mail-view> <folder-list> <message-list> <nav-actions><common-star> <message-view> <common-star> onSelected() onCurrentMsg() onStar() onReply() onNext()
  • 29.
    Never communicate directly:Publish events! key for low coupling ● towards Parent Component @Output() selected = new EventEmitter<string>(); select(item) { this.selected.emit(item); } ● towards shared Observable Streams in a Service ○ e.g. ErrorService, AlertService private errorSubject = new Subject<any>(); notifyError(code, message) { this.errorSubject.next({ code: code, message: message }); } Events & Common Services In both cases, events should express ● what's happened ● à la DDD Events ● intent, not what to do
  • 30.
    Separation of Responsibilities+ Component Interaction Complex behaviour achieved through collaboration <mail-view> <folder-list> <message-list> <message-view> onSelected() onCurrentMsg()messages currentMessage
  • 31.
    Example: Error ModalComponent Modal Component ErrorView Component ErrorModal Component
  • 32.
    Implement the DecoratorPattern to add cross-cutting behaviour to a set of components ● <ht-modal> ● <ht-input-container> ○ provides label, required, validation errors to an input <ht-input-container> <ht-input [(ngModel)]=... </ht-input-container> ○ uses @ContentChild to access the control Decorator Component Pattern
  • 33.
  • 34.
    API Design Challengesand Principles ● Your Library has two “Users” ○ End Users and Developers ○ think from both perspectives ● Joshua Block: Principle of Least Astonishment ○ How to Design Good APIs https://www.youtube.com/watch?v=aAb7hSCtvGw
  • 35.
    Names Matter ● Functionalityshould be easy to explain ○ express Intent ○ X is hard to name => symptom that X design needs refinement ■ too many responsibilities ■ unclear intent ○ Good names drive development ● Adopt Naming Conventions ○ library prefix in components name (e.g. ht-panel vs app-view) ○ prefix in property names vs HTML5 attributes (disabled, width...)
  • 36.
    Beyond Names: ConceptualConsistency ● Same/standard APIs based on Types ○ LabelAndIcon interface ○ ControlValueAccessor for all input fields ● Same Abstractions ○ DataSources for <ht-table>, <ht-tree>, <ht-document-download> ○ conceptually similar event if cannot share same Type Limit the amount of Mental Models that developers must learn ● Kathy Sierra on learning for effective products https://www.youtube.com/watch?v=FKTxC9pl-WM
  • 37.
    API Should BeAs Small As Possible ● But no Smaller ○ focus on the most important use-cases ● Constant trade-off between Under and Over-Engineering ○ if in doubt, leave it out: you can add it later ● often adding an Extension Point is easier ○ less risky than a Feature ○ e.g. <ng-content> or publishing an Event on a Stream
  • 38.
    Gather & RefineRequirements ● Extract true requirements from ‘proposed solutions’ ■ Table Columns should be configured to display a Button / Link ○ becomes ■ “Table Columns supports a template to customize cell rendering” ● Think more general ● Keep the Spec short
  • 39.
    Plan for theFuture ● Expect to make mistakes ● Prepare to evolve API ○ Expand-Contract pattern ○ Deprecation / console.warn() ○ Design for Refactoring: Typescript helps! ○ Ease upgrade with tools (tslint for rxjs 5-> 6, schematics) ○ automatic tests
  • 40.
  • 41.
    Documentation Matters Document everypublic component, interface, method, ... ● if it is not documented, “it does not exists” for developers ● Component, method, variable names should be as self-explanatory as possible ● for all the rest, there is...
  • 42.
    Compodoc ● Rich documentationwith compodoc ○ https://compodoc.app/ ○ compodoc -p ./tsconfig.json -d docs ● Automatically generate docs for ○ Component, Classes ○ Routing, Modules ● MarkDown for (quick) guides ○ project setup / library import ○ Architecture - Conceptual model ○ Component LifeCycle
  • 43.
    Document by Example ●Examples are key ○ even better if executable ● Plnkr ○ http://next.plnkr.co/ ● Stackblitz ○ https://stackblitz.com/ ● StoryBook ○ https://storybook.js.org/ ● Angular Playground ○ http://www.angularplayground.it/
  • 44.
  • 45.
    A good AngularLibrary ● Is platform independent ○ can be consumend as UMD, FESM, ... ● Is bundled and distributed ● Works with AOT (Ahead of Time Compiler) ● Plays well with TypeScript and IDEs ○ autocompletion, compile-time checking See Angular Package Format Minko Gechev @mgechev screenshot structure of the build package in node modules
  • 46.
    Angular 6.x integratesDavid Herges’ ng-packagr ● workspace = apps + libs ng generate library basiclib ng generate app basiclib-demo ng build basiclib cd dist/basiclib npm pack npm publish basiclib.y.z.tgz even live reload across lib & app https://mereommig.dk/en/blog/adding-livereload-to-the-angular-cli-libraries Creating and publishing
  • 47.
  • 48.
    Future directions ● AngularIvy Renderer ○ reusing a library does not require metadata processing ■ https://blog.angularindepth.com/inside-ivy-exploring-the-new-angular-compiler-ebf85141cee1 ● Angular Elements ○ package Components as HTML5 Custom Elements (a.k.a. Web Components) ○ https://angular.io/guide/elements ● See also StencilJs Component Compiler ○ https://stenciljs.com/
  • 49.
    References on Components Codemotion2016 Talk https://www.slideshare.net/carlo.bonamico/angular-rebooted-components-every where Components in Angular 2 https://angular.io/docs/ts/latest/cookbook/component-communication.html More on Angular https://angularconnect.com/talks
  • 50.
    Thank you! ● Otherpresentations − http://www.slideshare.net/carlo.bonamico/presentations ● Follow us on Twitter − @carlobonamico @nis_srl @Sonietta ● updates on Angular, Clean Code, Continuous Delivery ● Contact us − carlo.bonamico@gmail.com / carlo.bonamico@nispro.it − Sonia.pini@nispro.it ● Our company − http://www.nispro.it