Angular JS - Components

Who am i?

  • Kyle Muir
  • Snr Web Developer at Intergen
  • Specialising in large SPAs + Dev ops\automation
    • Largest SPA is currently >25k JS LOC

What are components trying to solve?

God controllers

Text

How?

  • Isolate scopes
  • Encapsulate logic for related parts
  • Explicitly pass dependencies
  • Simpler components to reason about

An Example

Bug raised - Buyin reasons not persisting correctly

Hmm where to begin? 760 lines. This will be easy, i'm sure.

Directive

 

Dedicated controller

Isolate scope

Automatic binding to controller and defining controllerAs variable for view

Replace by default, can transclude if needed.

vm. changed to ctrl. to match directive

Fourty-five lines of code. Much better. Much easier to spot improvements

module Booking {
    'use strict';

    export class BuyinReasonsController {
        public selectedOeReason: Model.OeSelectionReason = Model.OeSelectionReason.empty();
        public oeReasonsOption: Array<Model.OeSelectionReason> = [];

        constructor(private jobRepository: Services.IJobRepository, private oeReasonsCache: Services.IOeReasonsCache, 
                    private oeSelectionUpdater: Services.IOeSelectionUpdater) {
            this.oeReasonsOption = this.oeReasonsCache.retrieveAllOeReasons();
            this.initialiseOeSelectionReason();
        }

        public isReadOnly = () => {
            var job: Model.Job = this.jobRepository.loadJob();
            return job.isReadOnly();
        }

        public areBuyinReasonsRequired = (): boolean => {
            var job = this.jobRepository.loadJob();
            return job.hasOeProductsSelected();
        }

        private initialiseOeSelectionReason = (): void => {
            //There will be issues with this, the set up should be seperate to the updating of the job.
            var job = this.jobRepository.loadJob();
            if (!job.hasOeProductsSelected()) {
                return;
            }

            this.selectedOeReason = job.selectedOeReason.equal(Model.OeSelectionReason.empty())
                ? this.selectedOeReason = _.first(this.oeReasonsOption)
                : this.selectedOeReason = job.selectedOeReason;

            this.oeReasonsChanged();
        }

        public oeReasonsChanged = (): void => {
            var job = this.jobRepository.loadJob();
            job.selectedOeReason = this.selectedOeReason;
            this.jobRepository.saveJob(job);
            this.oeSelectionUpdater.sendSelectionToCrm();
        }
    }

    angular.module(Resources.App.Name).controller('BuyinReasonsController', BuyinReasonsController);
} 

Why bother?

  • Smaller class - easier to spot mistakes
  • Easier to refactor
  • Easier to maintain going forward
  • Easier for a new developer to get their head around
  • Isolate scope

Approach

  • Isolated ideas within large controllers
  • Pull out into a template, directive and controller
  • Refactor accordingly

Angular 1.5 - components

  • Directives with sensible defaults
  • Approach is standard\best practice
  • Apps are simply a collection of components
  • Easier upgrade path to angular 2.0
  • Prefer over directives unless DOM needed

Based on directives, but components makes this transition even easier.

Resources

Angular JS meetup - components

By kylemuir

Angular JS meetup - components

Example of how we utilised angular components to reign in our unwieldy angular app

  • 457