Component Architecture

Your own components

integrate components

  • Same as in index.html: simply include with its selector as an html element
  • components can be nested unlimitedly - common sense applies ;-)

Angular CLI Component creation

  • Why? usage of Angular CLI is ways faster
  • ng generate --help (shorthand ng g)
  • can generate several different types e.g. services, guards, pipes, modules and many more
  • ng generate component --help (shorthand ng g c
  • --flat, --style none, --type page and many more

Task 8.0

  • Create a component with the angular cli
  • use ng g c mycomponent --flat --style none --type example --skip-tests
  • create a second component with the default ng g c
  • what is the difference and why?

Task 8.1 - another component

  • Base branch: master
  • create a subfolder in src/app, name it pizzeria
  • create a pizzeria.component.ts typescript class with a pizzeria.component.html template
  • the template should contain a bal-heading from design system heading "Pizzeria"
  • the main section of the app.component should only contain the new pizzeria component
  • Do not forget to add the component in app.module.ts to declarations

Task 8.1 - General

  • you start the dev server with npm start
  • you can find an overview of the available commands the README.md in the root of the project

Task 8.1 - Possible Solution

  • Branch: 08_ComponentArchitecture_1_solution
  • The solutions now depend on the previous task
  • if you do not encounter any problems you can continue working with your own solution without switching branches
  • the solution branch is always the starting point for the next task
  • if you want to throw your local changes away and you want to see the solution: git reset --hard && git checkout 08_ComponentArchitecture_1_solution

Task 8.2 - display data

  • Base branch: 08_ComponentArchitecture_1_solution
  • There is an existing interface Order (see: src/app/types/order.types.ts)
  • create a new pizza-order component. It has an array of pizzas (copy over from src/assets/data.json)
  • render the pizzas in HTML and display all properties - the image url as img
  • Hint: an img has a src-property

Task 8.2 - Possible Solution

  • Branch: 08_ComponentArchitecture_2_solution
  • if you want to throw your local changes away and you want to see the solution: git reset --hard && git checkout 08_ComponentArchitecture_2

@Input - transfer data

Binding: source vs. target

  • there is always a source and a target for bindings
  • the target is on the left side of the =, the source on the right
  • the target is a property or event in square brackets: [prop], (event)
  • the source is in "source" or {{source}}

instance variables vs. Input & Output

  • @Input() and @Output() define the interface of directives (and components)
  • are the target of bindings
  • the source of bindings are all instance variables of directives (and components)

@Input() - Examples

                    
                        import {Component, Input} from '@angular/core';

                        @Component({
                            selector: 'pizza'
                        })
                        export class PizzaComponent {
                            @Input()
                            pizza: Pizza;
                        }
                    
                    
                        
                        
                    
                    
                        private myPizza: Pizza;
                    
                 

@Input() - Examples 2

                    
                        @Component({
                            selector: 'pizza'
                            inputs: ['pizza', 'ingredients']
                        )}
                        export class PizzaComponent {
                        }
                    
                    
                        
                        
                    
                    
                        private myPizza: Pizza;
                        private ingredients: Ingredient[];
                    
                 

Task 8.3 - @Input()

  • Base branch: 08_ComponentArchitecture_2_solution
  • create a new pizza component that displays a single pizza.
  • transfer the pizza to the new component using a binding
  • Hint: you can use components in other components by using their selector as a tag
  • Hint: you can use balCurrency pipe from '@baloise/web-app-pipes-angular' package but do not forget to import BaloisePipeModule in app.module.ts

Task 8.3 - Possible Solution

  • Branch: 08_ComponentArchitecture_3_solution
  • if you want to throw your local changes away and you want to see the solution: git reset --hard && git checkout 08_ComponentArchitecture_3_solution

@Output - react to events

  • we already know events of standard components such as buttons or inputs
  • custom events of our own components are exactly the same
  • they define together with Inputs the interface of a component

@Output() - Example

                    
                        import {.., Output, EventEmitter} from '@angular/core';
                        export class PizzaComponent {
                            @Output()
                            selectPizza: EventEmitter<Pizza> = new EventEmitter<Pizza>();
                        }
                    
                    
                        
                    
                    
                        
                        <pizza (selectPizza)="onPizzaSelected($event)">
                    
                    
                        onPizzaSelected(pizza: Pizza) { console.log(pizza); }
                    
                 

Task 8.4 - @Output()

  • Base branch: 08_ComponentArchitecture_3_solution
  • replace heading in pizza.component with aButton in order to add a pizza to cart
  • every pizza should have its own button
  • the component should emit an event when the user clicks the button - the outer component should handle the event and add the pizza to the cart
  • Hint: you can push to an array.
  • Hint: make dummy cart to display added pizzas (simple list), you can use JSON pipe to show whole pizza object

Task 8.4 - Possible Solution

  • Branch: 08_ComponentArchitecture_4_solution
  • if you want to throw your local changes away and you want to see the solution: git reset --hard && git checkout 08_ComponentArchitecture_4_solution

Component Architecture

Smart / Container Components

  • can access services / the application state
  • handle events of their child
  • pass data from services / state down to their children
  • do not contain any display logic - simply a container for a couple of dumb components

Dumb / Presentation / Stateless Components

  • do not have access to services / the application state
  • data gets passed with @Input
  • throw events with @Output
  • contain display logic
  • are easily reusable

when to use which one?

  • in general, the most outer component of a page should be smart
  • if a component is directly reachable by the router (URL), it has to be smart
  • if a component is routable, is accessible directly by url, it has to be smart
  • start «dumb» only use «smart» if there is no root component on that page

how to cut components

  • same as with a class: Single Responsibility
  • is a component too big, split it into multiple smaller components
  • if your component does 2 things...
  • ... create 3 components ;-)
  • one component as a wrapper, and 2 more components one for each responsibility

what are the advantages of this concept?

  • testability
  • reusability
  • no side effects - easy data flow
  • clear concept

Lifecycle Hooks

OnInit

  • the hook method is named ngOnInit()
  • the component should implement the OnInit interface
  • is called once after the constructor
  • for initialization logic, HTTP Calls etc.
  • Inputs are already initialized

OnInit - example

                    
                        import {Component, OnInit} from "@angular/core";

                        @Component({
                            selector: 'pizza',
                            templateUrl: './pizza.component.html'
                        })
                        export class PizzaComponent implements OnInit {

                            pizze: Pizza[];

                            ngOnInit() {
                                this.pizze = getPizzeFromService();
                            }
                        }
                    
                

Task 8.5 - OnInit

  • Base branch: 08_ComponentArchitecture_4_solution
  • identify a smart and a dumb component
  • add an OnInit life cycle hook in the smart component
  • move that part of the code to ngOnInit that makes sense for you ;-)
  • Hint: move fetching pizzas to private method of class PizzaOrderComponent which will be replaced with service coverd in next chapter
  • make CartComponent to nicely represent results
  • Hint: you can use list to nicely show pizzas in cart

Task 8.5 - Possible Solution

  • Branch: 08_ComponentArchitecture_5_solution
  • if you want to throw your local changes away and you want to see the solution: git reset --hard && git checkout 08_ComponentArchitecture_5_solution
  • Smart Component: PizzaOrderComponent
  • Dumb Component: PizzaComponent
  • Dumb Component: CartComponent

Questions?