UI Bakery Challenges

@nikpoltoratsky

Nikita Poltoratsky

Lead Engineer at Akveo

@nikpoltoratsky

@nikpoltoratsky

tibing/platform-terminal

dev.to/nikpoltoratsky

medium.com/@nik.poltoratsky

@nikpoltoratsky

@nikpoltoratsky

3 AM

Nikita Poltoratsky

Lead Engineer at Akveo

@nikpoltoratsky

@nikpoltoratsky

tibing/platform-terminal

dev.to/nikpoltoratsky

medium.com/@nik.poltoratsky

Agenda

  1. Code organization
  2. State management
  3. Code generation
  4. Rendering Angular from plain old JSON model

@nikpoltoratsky

Code organization

@nikpoltoratsky

Monorepo for

the great good

@nikpoltoratsky

@nikpoltoratsky

Monorepo for the great good

Pros

  • Simple code reuse
  • Atomic commits
  • Easy dependency management

Monorepo for the great good

@nikpoltoratsky

Simple code reuse

@nikpoltoratsky

Atomic commits

@nikpoltoratsky

Atomic commits

@nikpoltoratsky

Easy dependency management

@nikpoltoratsky

Easy dependency management

Angular V 7

Angular V 6

Angular V 8

+

+

+

=

PAIN

@nikpoltoratsky

Easy dependency management

Angular V 8

Angular V 8

Angular V 8

+

+

+

=

happiness

@nikpoltoratsky

Cons

  • It’s easy to introduce tight coupling

Monorepo for the great good

@nikpoltoratsky

It’s easy to introduce tight coupling

@nikpoltoratsky

Nx

@nikpoltoratsky

Nx - dealing with tight coupling

{
  "nx-enforce-module-boundaries": [
    true,
    {
      "allow": [],
      "depConstraints": [
        {
          "sourceTag": "ui-components",
          "onlyDependOnLibsWithTags": ["ui-components"]
        },
        {
          "sourceTag": "admin",
          "onlyDependOnLibsWithTags": ["ui-components", "admin"]
        },
        {
          "sourceTag": "client",
          "onlyDependOnLibsWithTags": ["ui-components", "client"]
        },
        {
          "sourceTag": "*",
          "onlyDependOnLibsWithTags": ["*"]
        }
      ]
    }
  ]
}

@nikpoltoratsky

Cons

  • It’s easy to introduce tight coupling

Monorepo for the great good

Pros

  • Simple code reuse
  • Atomic commits
  • Easy dependency management

@nikpoltoratsky

State management

@nikpoltoratsky

Use NgRX wisely

@nikpoltoratsky

What is NgRx

@nikpoltoratsky

NgRx everywhere!

@nikpoltoratsky

NgRx

NgRx Everywhere!

NgRx everywhere!

@nikpoltoratsky

NgRx everywhere!

@nikpoltoratsky

NgRx everywhere!

@nikpoltoratsky

Refactoring

NgRx with facades!

@nikpoltoratsky

Behavior Subjects!

@nikpoltoratsky

Code generation

@nikpoltoratsky

vast Angular schematics

@nikpoltoratsky

What is Angular Schematics

@nikpoltoratsky

# Create component
ng g component LoginComponent

# Setup component from library
ng generate @angular/material:table 

# Update library
ng update @angular/material

We have application model

@nikpoltoratsky

{
  component: 'card',
  children: [
    ...
  ],
  properties: {
    backgroundColor: 'red',
    ...
  }
}

What Angular Schematics do for UI Bakery?

@nikpoltoratsky

  1. pages
  2. components
  3. template
  4. Import required modules
  5. install required packages
  6. Apply required theme

Rendering Angular from plain old JSON model

@nikpoltoratsky

Reinventing the wheel — custom Angular renderer

@nikpoltoratsky

@nikpoltoratsky

It's me! Reinventing the wheel!

We have application model

@nikpoltoratsky

{
  component: 'card',
  children: [
    ...
  ],
  properties: {
    backgroundColor: 'red',
    ...
  }
}

Rendering without wrappers

@nikpoltoratsky

  • Render without additional HTML elements
  • Use flexbox instead of absolute positioning

How to do that?

@nikpoltoratsky

  • Iterate through the application tree
  • find differences between the current and previous state
  • change the state operating Angular Views

Diffing the model

@nikpoltoratsky

class Renderer {
  private differ: IterableDiffer;
  
  render(components: Component[]) {
    
    const changes: IterableChanges<Component> = this.differ.diff(users);
    
    changes.forEachOperation((change: IterableChangeRecord<Component>,
                          previousIndex: number | null,
                          currentIndex: number | null) => {
  

  	  if (previousIndex === null) {
	    // new component appeared
	  } else if (currentIndex === null) {
	    // component existed before but it's removed
	  } else {
	    // component was moved from one position to another
	  }
	});
  }
}

Manipulating Angular Views

@nikpoltoratsky

changes.forEachOperation((change: IterableChangeRecord<Component>,
                          previousIndex: number | null,
                          currentIndex: number | null) => {

  if (previousIndex === null) {
    viewContainerRef.remove(previousIndex);
  } else if (currentIndex === null) {
	viewContainerRef.createComponent(component, currentIndex)
  } else {
	viewContainerRef.move(component, currentIndex);
  }
});

Agenda

  1. Monorepo for the great good
  2. Use NgRX wisely
  3. UI Bakery is a frontend for the vast Angular schematics
  4. Reinventing the wheel — custom Angular renderer

@nikpoltoratsky

The End!

@nikpoltoratsky

Questions?

UI Bakery Challenges

By Nikita Poltoratsky

UI Bakery Challenges

  • 696