Slide 1

Slide 1 text

NX WORKSPACES FOR ANGULAR

Slide 2

Slide 2 text

What is NX?

Slide 3

Slide 3 text

What is NX? Overview

Slide 4

Slide 4 text

What is NX? Overview Encapsulation

Slide 5

Slide 5 text

What is NX? Overview Encapsulation Speed

Slide 6

Slide 6 text

What is NX? Overview Encapsulation Speed Migration

Slide 7

Slide 7 text

What is NX? Overview Encapsulation Speed Migration Plugins

Slide 8

Slide 8 text

Why?

Slide 9

Slide 9 text

App

Slide 10

Slide 10 text

App Funktion Pipe Service Component / Directive Module

Slide 11

Slide 11 text

App Funktion Pipe Service Component / Directive Module

Slide 12

Slide 12 text

App Funktion Pipe Service Component / Directive Module

Slide 13

Slide 13 text

App Funktion Pipe Service Component / Directive Module

Slide 14

Slide 14 text

App Funktion Pipe Service Component / Directive Module

Slide 15

Slide 15 text

App Funktion Pipe Service Component / Directive Module

Slide 16

Slide 16 text

App Funktion Pipe Service Component / Directive Module

Slide 17

Slide 17 text

App Funktion Pipe Service Component / Directive Module

Slide 18

Slide 18 text

App Funktion Pipe Service Component / Directive Module

Slide 19

Slide 19 text

App Funktion Pipe Service Component / Directive Module

Slide 20

Slide 20 text

App

Slide 21

Slide 21 text

App

Slide 22

Slide 22 text

Bring Order

Slide 23

Slide 23 text

Have Flexibility

Slide 24

Slide 24 text

Gregor Woiwode

Slide 25

Slide 25 text

Fabian Gosebrink

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Fabian Gosebrink

Slide 28

Slide 28 text

Fabian Gosenbrink

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

What is

Slide 31

Slide 31 text

Tooling

Slide 32

Slide 32 text

Tooling

Slide 33

Slide 33 text

Tooling

Slide 34

Slide 34 text

Tooling

Slide 35

Slide 35 text

NG Workspace

Slide 36

Slide 36 text

NG Workspace

Slide 37

Slide 37 text

NG Workspace

Slide 38

Slide 38 text

NX Workspace

Slide 39

Slide 39 text

Workspace

Slide 40

Slide 40 text

Intelligent Build

Slide 41

Slide 41 text

Intelligent Build Cache

Slide 42

Slide 42 text

Encapsulation

Slide 43

Slide 43 text

Angular Modules

Slide 44

Slide 44 text

Feature Modules

Slide 45

Slide 45 text

import { NgModule } from '@angular/core'; @NgModule({ imports: [ ... ], declarations: [ AppComponent, HelloComponent, Hello1Component, Hello2Component, Hello3Component, Hello4Component, Hello5Component, Hello6Component, Hello7Component, Hello8Component, Hello9Component, Hello10Component, Hello11Component, Hello12Component, Hello13Component, Hello14Component, Hello15Component 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Slide 46

Slide 46 text

import { NgModule } from '@angular/core'; @NgModule({ imports: [ ... ], declarations: [ AppComponent, Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ... ], bootstrap: [AppComponent] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Slide 47

Slide 47 text

import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [ Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, ] }) export class Feature1Module { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Slide 48

Slide 48 text

import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [ Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ] }) export class Feature2Module { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Slide 49

Slide 49 text

import { NgModule } from '@angular/core'; @NgModule({ imports: [ ], declarations: [ AppComponent, Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ], }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Slide 50

Slide 50 text

import { NgModule } from '@angular/core'; import { Feature1Module } from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; @NgModule({ imports: [ BrowserModule, FormsModule, Feature1Module, Feature2Module ], declarations: [ AppComponent, HelloComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 51

Slide 51 text

import { NgModule } from '@angular/core'; import { Feature1Module } from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; @NgModule({ imports: [ BrowserModule, FormsModule, Feature1Module, Feature2Module ], declarations: [ AppComponent, HelloComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { Feature1Module } from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; Feature1Module, Feature2Module import { NgModule } from '@angular/core'; 1 2 3 4 @NgModule({ 5 imports: [ 6 BrowserModule, 7 FormsModule, 8 9 10 ], 11 declarations: [ 12 AppComponent, 13 HelloComponent 14 ], 15 bootstrap: [ AppComponent ] 16 }) 17 export class AppModule { } 18

Slide 52

Slide 52 text

Libraries

Slide 53

Slide 53 text

Libraries Share code between apps

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

import { /* ... */ } from 'lib-to-configure'; import { NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8

Slide 59

Slide 59 text

import { /* ... */ } from 'lib-to-configure'; import { NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8

Slide 60

Slide 60 text

@NgModule({ declarations: [LibToConfigureComponent], imports: [CommonModule], exports: [LibToConfigureComponent], }) export class LibToConfigureModule {} 1 2 3 4 5 6

Slide 61

Slide 61 text

@NgModule({ declarations: [LibToConfigureComponent], imports: [CommonModule], exports: [LibToConfigureComponent], }) export class LibToConfigureModule {} 1 2 3 4 5 6 @NgModule({ declarations: [AppComponent], imports: [BrowserModule, LibToConfigureModule], providers: [], bootstrap: [AppComponent], }) export class AppModule {} 1 2 3 4 5 6 7

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

Rethinking Libraries

Slide 69

Slide 69 text

Libraries Share code between apps

Slide 70

Slide 70 text

Libraries Share code between apps Provide feature to one app

Slide 71

Slide 71 text

Libraries Share code between apps Provide feature to one app Multiple Libs = 1 Feature

Slide 72

Slide 72 text

Feature Feature Ui Data access Utils

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

Folders

Slide 76

Slide 76 text

Folders Lib: Api Lib: Data Lib: Ui Lib: Util Lib: Feature

Slide 77

Slide 77 text

Folders Lib: Api Lib: Data Lib: Ui Lib: Util Lib: Feature

Slide 78

Slide 78 text

Feature Ui vs.

Slide 79

Slide 79 text

Container Presentational vs.

Slide 80

Slide 80 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Slide 81

Slide 81 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable; loading$: Observable; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21

Slide 82

Slide 82 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable; loading$: Observable; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21

Slide 83

Slide 83 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable; loading$: Observable; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21

Slide 84

Slide 84 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable; loading$: Observable; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 19 } 20 } 21

Slide 85

Slide 85 text

Stateful import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable; loading$: Observable; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable; 9 loading$: Observable; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 19 } 20 } 21 import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable; loading$: Observable; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Slide 86

Slide 86 text

Stateful

All Gatherings of the Group

1 2 3 4 5 6 7 8 9 10 11 12 13

Slide 87

Slide 87 text

Stateful

All Gatherings of the Group

1 2 3 4 5 6 7 8 9 10 11 12 13
1

All Gatherings of the Group

2 3 7 8 9 10 11 12
13

Slide 88

Slide 88 text

Stateless import { /* ... */ } from '...'; @Component({ selector: 'workspace-meetup-list', templateUrl: './meetup-list.component.html', styleUrls: ['./meetup-list.component.scss'], }) export class MeetupListComponent { @Input() items: Meetup[] = []; } 1 2 3 4 5 6 7 8 9 10 11 12

Slide 89

Slide 89 text

Stateless
No items found...
  • {{ item.name }}
1 2 3 4 5 6 7 8 9 10 11 12 13 14

Slide 90

Slide 90 text

Stateless
1 2 No items found... 3 4 5
6 7 8 9 10 11
12
13

Slide 91

Slide 91 text

Stateless
1 2 No items found... 3 4 5
6 7 8 9 10 11
12
13
No items found...
1 2 3 4 5 6 7 8 9 10 11 12 13

Slide 92

Slide 92 text

Stateless
1 2 No items found... 3 4 5
6 7 8 9 10 11 12
13
14

Slide 93

Slide 93 text

Stateless
No items found...
1 2 3 4 5 6 7 8 9 10 11 12 13 14

Slide 94

Slide 94 text

Stateless import { /* ... */ } from '...'; @Component({ selector: 'workspace-meetup-list', templateUrl: './meetup-list.component.html', styleUrls: ['./meetup-list.component.scss'], }) export class MeetupListComponent { @Input() items: Meetup[] = []; @Output() itemClicked = new EventEmitter(); bubbleNavigationEvent(meetup: Meetup) { this.itemClicked.emit(meetup); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 95

Slide 95 text

Stateful Stateless [ ... ] ( ... )

Slide 96

Slide 96 text

Stateful Stateless [ ... ] ( ... ) Stateless [ ... ] ( ... )

Slide 97

Slide 97 text

Stateful Stateless [ ... ] ( ... ) Stateless [ ... ] ( ... )

Slide 98

Slide 98 text

Overlook The dependency graph

Slide 99

Slide 99 text

“ Nx is built on a technology-agnostic core that maintains modular units of code and understands the dependency graph between them. nx.dev

Slide 100

Slide 100 text

Dependency Graph

Slide 101

Slide 101 text

What is it? The dependency graph helps you overlook all your applications and libraries. You learn about relations and dependencies of each module in your workspace.

Slide 102

Slide 102 text

Why is it helpful? On-Boarding new colleagues Cyclic library dependency

Slide 103

Slide 103 text

Graph

Slide 104

Slide 104 text

Graph

Slide 105

Slide 105 text

Graph

Slide 106

Slide 106 text

Command nx dep-graph 1 2 > NX NOTE Dep graph started at http://127.0.0.1:4211 3

Slide 107

Slide 107 text

Command nx dep-graph 1 2 > NX NOTE Dep graph started at http://127.0.0.1:4211 3 > NX NOTE Dep graph started at http://127.0.0.1:4211 nx dep-graph 1 2 3

Slide 108

Slide 108 text

Affected Dependency Graph

Slide 109

Slide 109 text

Why is it helpful? See impact a library has on the workspace. Optimize build, lint, serve, test commands.

Slide 110

Slide 110 text

main feature HEAD nx affected:dep-graph \ --base main How it works Analyse differences between git branches

Slide 111

Slide 111 text

Command nx affected:dep-graph 1 \ --base # default: main 2 \ --head # default: HEAD 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5

Slide 112

Slide 112 text

Command nx affected:dep-graph 1 \ --base # default: main 2 \ --head # default: HEAD 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5 \ --head # default: HEAD nx affected:dep-graph 1 \ --base # default: main 2 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5

Slide 113

Slide 113 text

DEMO

Slide 114

Slide 114 text

Restrict Module dependencies

Slide 115

Slide 115 text

Why is it helpful? Control encapsulation of workspace modules. Prevent accidental module pollution.

Slide 116

Slide 116 text

nx.json eslintrc.json or tslint.json Tag projects nx-enforce-module- boundaries Declare dependencies

Slide 117

Slide 117 text

nx.json "projects": { { 1 2 "gatherr": { 3 "tags": [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26

Slide 118

Slide 118 text

nx.json "projects": { { 1 2 "gatherr": { 3 "tags": [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26 "gatherr": { "gatherr-e2e": { "auth-feature": { "groups-feature": { "home-feature": { "personal-feature": { "profile-feature": { { 1 "projects": { 2 3 "tags": [] 4 }, 5 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 10 "tags": [] 11 }, 12 13 "tags": [] 14 }, 15 16 "tags": [] 17 }, 18 19 "tags": [] 20 }, 21 22 "tags": [] 23 } 24 } 25 } 26

Slide 119

Slide 119 text

nx.json "projects": { { 1 2 "gatherr": { 3 "tags": [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26 "gatherr": { "gatherr-e2e": { "auth-feature": { "groups-feature": { "home-feature": { "personal-feature": { "profile-feature": { { 1 "projects": { 2 3 "tags": [] 4 }, 5 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 10 "tags": [] 11 }, 12 13 "tags": [] 14 }, 15 16 "tags": [] 17 }, 18 19 "tags": [] 20 }, 21 22 "tags": [] 23 } 24 } 25 } 26 "tags": [] "tags": [], "tags": [] "tags": [] "tags": [] "tags": [] "tags": [] { 1 "projects": { 2 "gatherr": { 3 4 }, 5 "gatherr-e2e": { 6 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 11 }, 12 "groups-feature": { 13 14 }, 15 "home-feature": { 16 17 }, 18 "personal-feature": { 19 20 }, 21 "profile-feature": { 22 23 } 24 } 25 } 26

Slide 120

Slide 120 text

nx.json "tags": ["app"] "tags": ["app-feature"] "tags": ["app-feature"] "tags": ["app-feature"] "tags": ["app-feature"] "tags": ["app-feature"] { 1 "projects": { 2 "gatherr": { 3 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 11 }, 12 "groups-feature": { 13 14 }, 15 "home-feature": { 16 17 }, 18 "personal-feature": { 19 20 }, 21 "profile-feature": { 22 23 } 24 } 25 } 26

Slide 121

Slide 121 text

linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15

Slide 122

Slide 122 text

linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15

Slide 123

Slide 123 text

linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15

Slide 124

Slide 124 text

linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "depConstraints": [ { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15

Slide 125

Slide 125 text

linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "depConstraints": [ { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 { "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] } { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 9 10 11 12 }] 13 } 14 } 15

Slide 126

Slide 126 text

linter { "sourceTag": "app", "onlyDependOnLibsWithTags": ["app-feature"] }] { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 13 14 15 16 }] 17 } 18 } 19

Slide 127

Slide 127 text

linter "error", { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 { 13 "sourceTag": "app", 14 "onlyDependOnLibsWithTags": ["app-feature"] 15 }] 16 }] 17 } 18 } 19

Slide 128

Slide 128 text

Wildcards https://nx.dev/latest/angular/structure/monorepo-tags#exceptions

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

linter "warn", { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 { 13 "sourceTag": "app", 14 "onlyDependOnLibsWithTags": ["app-feature"] 15 }] 16 }] 17 } 18 } 19

Slide 131

Slide 131 text

No content

Slide 132

Slide 132 text

DEMO

Slide 133

Slide 133 text

Speed Up Optimize Build- & Test- Runs

Slide 134

Slide 134 text

No content

Slide 135

Slide 135 text

No content

Slide 136

Slide 136 text

No content

Slide 137

Slide 137 text

No content

Slide 138

Slide 138 text

No content

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

No content

Slide 141

Slide 141 text

"paths": { "@workspace/shared/ui-common": ["libs/shared/ui-common/src/index.ts"], "@workspace/shared/ui-layout": ["libs/shared/ui-layout/src/index.ts"], "@workspace/shared/data": ["libs/shared/data/src/index.ts"], "@workspace/shared/utils": ["libs/shared/utils/src/index.ts"], "@workspace/category/data": ["libs/category/data/src/index.ts"], "@workspace/category/api": ["libs/category/api/src/index.ts"], "@workspace/auth/api": ["libs/auth/api/src/index.ts"], "@workspace/auth/feature": ["libs/auth/feature/src/index.ts"], "@workspace/auth/util": ["libs/auth/util/src/index.ts"], "@workspace/auth/data": ["libs/auth/data/src/index.ts"], "@workspace/home/feature": ["libs/home/feature/src/index.ts"], "@workspace/home/ui": ["libs/home/ui/src/index.ts"], "@workspace/maps/util": ["libs/maps/util/src/index.ts"], "@workspace/groups/api": ["libs/groups/api/src/index.ts"], "@workspace/groups/feature": ["libs/groups/feature/src/index.ts"], "@workspace/groups/ui": ["libs/groups/ui/src/index.ts"], "@workspace/groups/data": ["libs/groups/data/src/index.ts"], "@workspace/personal/feature": ["libs/personal/feature/src/index.ts"], { 1 "compileOnSave": false, 2 "compilerOptions": { 3 "rootDir": ".", 4 "sourceMap": true, 5 "declaration": false, 6 "moduleResolution": "node", 7 "emitDecoratorMetadata": true, 8 "experimentalDecorators": true, 9 "importHelpers": true, 10 "target": "es5", 11 "module": "esnext", 12 "typeRoots": ["node_modules/@types"], 13 "lib": ["es2017", "dom"], 14 "skipLibCheck": true, 15 "skipDefaultLibCheck": true, 16 "baseUrl": ".", 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

Slide 142

Slide 142 text

import { /* ... */ } from 'lib-to-configure'; import { NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8

Slide 143

Slide 143 text

libs/shared-utils

Slide 144

Slide 144 text

libs/shared-utils

Slide 145

Slide 145 text

libs/shared-utils /ServiceA libs/shared-utils

Slide 146

Slide 146 text

libs/shared-utils /ServiceA libs/shared-utils /ServiceB libs/shared-utils

Slide 147

Slide 147 text

Lib1 Lib2

Slide 148

Slide 148 text

Changes on Service A or B builds both libs!

Slide 149

Slide 149 text

libs/shared-utils

Slide 150

Slide 150 text

libs/shared-utils Use with caution!

Slide 151

Slide 151 text

libs// App Specific Library libs//

Slide 152

Slide 152 text

libs/shared// Generic for one App libs/shared/

Slide 153

Slide 153 text

libs/shared/feature-logging libs/shared/feature-notification libs/shared/feature-... Generic for x Apps

Slide 154

Slide 154 text

Move Command nx g @nrwl/angular:move --project my-feature-lib shared/my-feature-lib 1

Slide 155

Slide 155 text

Migration Update your Workspace

Slide 156

Slide 156 text

Why is it helpful? Benefit from automatic migration scripts. Get new features enabling your team building more efficient solutions.

Slide 157

Slide 157 text

Prepare Migration nx migrate Update package.json Identify available migrations Write migrations.json Preparation complete

Slide 158

Slide 158 text

Execute Migration Install packages (npm, ...) nx migrate --run-migrations=migrations.json nx migrate latest

Slide 159

Slide 159 text

nx migrate latest 1 2 > NX NOTE Dep graph started at http://127.0.0.1:4211 3 Command

Slide 160

Slide 160 text

nx migrate latest 1 2 > NX NOTE Dep graph started at http://127.0.0.1:4211 3 > NX NOTE Dep graph started at http://127.0.0.1:4211 nx migrate latest 1 2 3 Command

Slide 161

Slide 161 text

↑ nx migrate latest Fetching meta data about packages. It may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 Prepare Migration

Slide 162

Slide 162 text

↑ nx migrate latest Fetching meta data about packages. It may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - migrations.json has been generated ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 Prepare Migration

Slide 163

Slide 163 text

↑ nx migrate latest Fetching meta data about packages. It may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - migrations.json has been generated ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - Run 'nx migrate --run-migrations=migrations.json' ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 26 Prepare Migration

Slide 164

Slide 164 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 migrations.json

Slide 165

Slide 165 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json

Slide 166

Slide 166 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json

Slide 167

Slide 167 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json

Slide 168

Slide 168 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "package": "@nrwl/workspace", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json

Slide 169

Slide 169 text

"migrations": [ ] { 1 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "package": "@nrwl/workspace", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "name": "update-decorate-angular-cli" { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 8 }, 9 ... 10 ] 11 } 12 migrations.json

Slide 170

Slide 170 text

> NX Running migrations from 'migrations.json' 1 2 Running migration rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Execute Migration

Slide 171

Slide 171 text

> NX Running migrations from 'migrations.json' 1 2 Running migration rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Running migration rename-workspace-schematic-script Running migration update-ngcc-postinstall Running migration migration-v11.1-can-activate-with-redirect-to Running migration always-use-project-level-tsconfigs-with-eslint > NX Running migrations from 'migrations.json' 1 2 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Execute Migration

Slide 172

Slide 172 text

> NX Running migrations from 'migrations.json' 1 2 Running migration rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Running migration rename-workspace-schematic-script Running migration update-ngcc-postinstall Running migration migration-v11.1-can-activate-with-redirect-to Running migration always-use-project-level-tsconfigs-with-eslint > NX Running migrations from 'migrations.json' 1 2 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 > NX Successfully finished running migrations from 'migrations.json' > NX Running migrations from 'migrations.json' 1 2 Running migration rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 16 Execute Migration

Slide 173

Slide 173 text

Caution Migrations can fail. Be aware of script errors or situations NX cannot automatically complete the migration for you.

Slide 174

Slide 174 text

Missing @nrwl/tao https://github.com/nrwl/nx/issues/2120 A misconfigured dependency prevented the migration from running. npm install --save-dev @nrwl/tao 1 FIX

Slide 175

Slide 175 text

Trouble- shooting - Read errors carefully. - Check GitHub for existing issues. - File an issue if you have encountered a new bug.

Slide 176

Slide 176 text

ESLint Updating to the latest NX workspace allows to migrating from TSLint to ESLint automatically. You can migrate one project after another to split the effort.

Slide 177

Slide 177 text

nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 List nx commands

Slide 178

Slide 178 text

nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 init : Initialize the @nrwl/angular plugin application : Create an Angular application library : Create an Angular library karma : Add karma configuration to a workspace karma-project : Add karma testing to a project ngrx : Add an ngrx config to a project downgrade-module : Setup Downgrade Module upgrade-module : Add an upgrade module storybook-configuration : Create stories/specs for all components declared in a library storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story stories : Create stories/specs for all components declared in a library component-cypress-spec : Create a cypress spec for a ui component that has a story component-story : Create a stories.ts file for a component move : Move an Angular application or library to another folder add-linting : Add linting configuration to an Angular project convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 List nx commands

Slide 179

Slide 179 text

nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 init : Initialize the @nrwl/angular plugin application : Create an Angular application library : Create an Angular library karma : Add karma configuration to a workspace karma-project : Add karma testing to a project ngrx : Add an ngrx config to a project downgrade-module : Setup Downgrade Module upgrade-module : Add an upgrade module storybook-configuration : Create stories/specs for all components declared in a library storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story stories : Create stories/specs for all components declared in a library component-cypress-spec : Create a cypress spec for a ui component that has a story component-story : Create a stories.ts file for a component move : Move an Angular application or library to another folder add-linting : Add linting configuration to an Angular project convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 22 List nx commands

Slide 180

Slide 180 text

nx g convert-tslint-to-eslint --project 1 DELETE /tslint.json CREATE /.eslintrc.json UPDATE angular.json UPDATE .eslintrc.json Migrate to ESLint

Slide 181

Slide 181 text

NX Plugins Cypress, Storybook, Bring your own

Slide 182

Slide 182 text

Plugins Nx is a plugin system. nx list 1 List all available plugins

Slide 183

Slide 183 text

Protractor Your choice

Slide 184

Slide 184 text

Protractor Your choice Deprecation Note https://github.com/angular/protractor/issues/5502

Slide 185

Slide 185 text

Story Book

Slide 186

Slide 186 text

Why is it helpful? Benefit from automatic migration scripts. Get new features enabling your team building more efficient solutions.

Slide 187

Slide 187 text

No content

Slide 188

Slide 188 text

Install https://nx.dev/latest/angular/storybook/overview

Slide 189

Slide 189 text

Configure

Slide 190

Slide 190 text

Configure

Slide 191

Slide 191 text

Configure

Slide 192

Slide 192 text

Write

Slide 193

Slide 193 text

Write

Slide 194

Slide 194 text

Write

Slide 195

Slide 195 text

Write

Slide 196

Slide 196 text

Write

Slide 197

Slide 197 text

Run

Slide 198

Slide 198 text

DEMO

Slide 199

Slide 199 text

Wrap up

Slide 200

Slide 200 text

Wrap up

Slide 201

Slide 201 text

Wrap up Workspace Enterprise Applications

Slide 202

Slide 202 text

Wrap up Workspace Enterprise Applications Latest Toolset

Slide 203

Slide 203 text

Wrap up Workspace Enterprise Applications Latest Toolset Affected Build & Cache

Slide 204

Slide 204 text

Wrap up Workspace Enterprise Applications Latest Toolset Affected Build & Cache SoC through Libs

Slide 205

Slide 205 text

Wrap up Workspace Enterprise Applications Latest Toolset Affected Build & Cache SoC through Libs Dep Graph

Slide 206

Slide 206 text

Wrap up Workspace Enterprise Applications Latest Toolset Affected Build & Cache SoC through Libs Dep Graph Two-Step Migration

Slide 207

Slide 207 text

Wrap up Workspace Enterprise Applications Latest Toolset Affected Build & Cache SoC through Libs Dep Graph Two-Step Migration Plugin Architecture

Slide 208

Slide 208 text

Next Talk...

Slide 209

Slide 209 text

Thank you!