US & Canada: 877 849 1850
International: +1 678 648 3113

Accelebrate Blog

ACCELERATED LEARNING, CELEBRATED RESULTS

Component-driven Development with Angular 1 and Angular 2 (Part 1 of 2)

The prospect of Web Components is the gleeful hope of web developers for the future of their craft. Simply, Web Components are self-contained, reusable, distributable HTML elements that can be isolated from the surrounding page and are packed full of functionality and styling, providing a useful user interface component for enhancing the user experience.

To learn more about web components, please see: http://webcomponents.org/.

While the dream of Web Components as an implemented standard has not yet been realized, many attempts have been made to bring the concepts behind the standard (really a group of standards including custom elements, HTML imports, templates and the shadow DOM) to developers now through various coding patterns and technical wizardry. Some attempts to encapsulate some of the ideas of web components include the Polymer Project, React, SkateJS, and Angular 2. While some projects such as React were not designed specifically to be like web components, many of the ideas captured in the project follow the best practices of working with web components.

This blog post is going to focus on Angular 2, and yes, Angular 1. Why Angular 1? The web component concept in Angular 2 was so successful, the Angular team backported some of the ideas and concepts of web components into a more formal API in Angular 1. The new component API was introduced with Angular 1.5. Using the patterns of web components is not new to Angular 1, in fact, many developers have used custom directives from a component perspective for years. With Angular 1.5 incorporating some of Angular 2’s component API, the commonly used pattern in Angular 1 became part of the actual Angular 1 API. This allowed developers to leverage the already existing built-in capabilities of Angular 1.

For many students, transitioning to components is a little difficult. They struggle with what a component is, how to compose multiple components to make larger components, and how to manage data between components. Once these obstacles are overcome, using web component practices becomes second nature, and greatly simplifies application development and increases code re-use. The cool aspect is that once a person masters web components in one framework, they can almost instantly use them in any other framework because the ideas are exactly the same.

So let’s look at web components in Angular 1 and Angular 2 from the following perspectives: user interface composition and data sharing between components. The first post in this two-part series will focus on the creation and composition components. The second post in this series will focus on data sharing between components.

The code demonstrations use ES5.1 for Angular 1, and TypeScript for Angular 2. While TypeScript is a superset of JavaScript, and is ultimately transpiled to ES5.1, it’s important to understand they are at best two very different versions of a similar language, and at worst two different languages. The goal here is not to focus on language syntax, but on component concepts.

Creating a Component

While components have many qualities, by definition they have a user interface which is bound to data and actions (“actions” are really just “event handlers”). The data is displayed within the user interface, and the user interacts with the user interface generating actions which are handled by the component. While different component libraries implement these component qualities in different ways, the concepts themselves are ubiquitous across all component-based frameworks and libraries.

In Angular 1 and 2, components are essentially implemented the same way, even though the syntax appears to be much different. The user interface is an HTML template with various data bindings and other user interfaces functionalities such as directives and filters (known as pipes in Angular 2). The template syntax in Angular 1 and 2 looks very similar.

Angular 1 Hello World Component Template – HTML

<h1>{{$ctrl.message}}</h1>

Angular 2 Hello World Component Template – HTML

<h1>{{message}}</h1>

The component is placed in the HTML using a custom element. The usage of a custom element is required for components. HTML 5 requires that custom elements have a dash in the element name, therefore all component names have two words. Here are examples of the placement of the HelloWorld component within templates.

Angular 1 Hello World Component Placement in Template – HTML

<div ng-app="myApp">
  <hello-world></hello-world>
</div>

Angular 2 Hello World Component Placement in Template – HTML

import { Component } from '@angular/core';
import { HelloWorld } from './hello-world.component';

@Component({
  selector: 'my-app',
  template: `<hello-world></hello-world>
  `,
  directives: [HelloWorld]
})
export class AppComponent { }

Where the real syntax difference exists is in the implementation of the data and event handler systems. On the surface, ES2015 obscures the same conceptual approach with significantly different syntax. Yet under the hood, they are remarkably similar.

Angular 1 components use a controller function that is used to instantiate a new object using the new operator (the instantiation happens behind the scenes; we observe it through the use of the this object inside the controller). The controller is nothing more than a constructor function which produces a new object that is then bound to the template making its data and function properties available.

Ironically, this is essentially what Angular 2 does. Albeit with a much different looking syntax. In Angular 2, an ES2015 class is used to setup the data and function properties. These properties are then available in the template. So if Angular 1 uses constructor function, and Angular 2 uses a class – how are they similar? Well, ES2015 classes are really just constructor functions with new syntax. In fact, if you used ES2015 with Angular 1, you could easily make the controller an ES2015 class.

Angular 1 Hello World Component – JavaScript

angular.module('myApp', [])
  .component('helloWorld', {
    template: '<h1>{{$ctrl.message}}</h1>',
    controller: function() {
      this.message = "Hello World!";
    }
});

Angular 2 Hello World Component – TypeScript

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

@Component({
  selector: 'hello-world',
  template: `<h1>{{message}}</h1>`
})
export class HelloWorld {
  message = 'Hello World!';
}

Where there is more of a difference is the mechanism by which the template and the controller are associated with each other. Angular 1 uses a component registration function along with a configuration object which has a template/templateUrl and controller properties to perform the association. Angular 2 uses something called a decoration (sometimes referred to as an annotation), which decorates the controller class. The decoration has a configuration object that has a template/templateUrl property. Decorators are not a feature of JavaScript (not even

ES2015, but are a proposed future standard), and are used via TypeScript, which is the preferred language for Angular 2 development. Without diving into a discussion on TypeScript, all you need to know is that it is a superset of the JavaScript language providing strong typing when desired and ES2015 to ES5.1 transpilation.

When Angular 1 and 2 bring a page to life, the component template and data/actions are bound together and displayed on the screen for interaction with the user.

The next two sections will focus on how to extend a single component to work with multiple components through templates and data/actions. The next section will explore how components can be composed together through HTML templates and using techniques such as transclusion. Then, we will explore how to share data/actions between components. Tying the two essential qualities of a component to other components is where the real power of component-driven development is made manifest.

Composing Components

One of the chief benefits of working with components is the ability to compose components together to build larger components. Composing components (along with predictable data flow – covered in the next section) are among the many reasons for using component-driven development.

As mentioned earlier, components are added to HTML via custom elements. Custom elements can contain child elements. These child elements can themselves be custom elements meaning that multiple smaller components can be child elements of the parent element of another custom component.

In both Angular 1 and Angular 2, child components can be added in the template of the parent component. This will incorporate the child components into the rendering of the parent component. This is exactly the approach that was demonstrated in the previous section with the HelloWorld component.

This approach is perfect for situations where the component developer is controlling the composition and not allowing the component consumer to provide additional customized template content (including custom components). To allow the developer using the component to provide customized template content, another approach is needed.

Customizing the Template

In Angular 1, the process of incorporating customized template content from the developer using the component is called transclusion. Previously, Angular 1.5 transclusion was available in two forms: content transclusion and element plus content transclusion. With content transclusion, the content of the element upon which the directive marker was applied would create a template from the content of the element making it available for usage in the directive’s template. The transcluded content would then be inserted into the template of the directive through the ngTransclude directive or the transclude function. Element plus content

transclusion does basically the same thing, except the entire element plus its content is transcluded – think of ngRepeat. The placement of the content is determined by the placement of the ngTransclude directive in the template, or through the use of the transclude function. For the purposes of this post, we will focus on the ngTransclude directive.

Components in Angular 1.5 are simply specialized directives, and they support transclusion. Here is an example of component transclusion. The HTML content of the list-box element will be added to the template of the listBox component where the ng-transclude directive is placed.

Angular 1 Transclusion Example – HTML

<div ng-app="myApp">
  <list-box>
    <header>
      <h2>List of Colors</h2>
    </header>
  </list-box>
</div>

Angular 1 Transclusion Example – JavaScript

angular.module('myApp', [])
  .component('listBox', {
    transclude: true,
    template: `<ng-transclude></ng-transclude>
      <ul><li ng-repeat='item in $ctrl.items'>{{item}}</li></ul>`,
    controller: function() {
      this.items = ['red','blue','green','yellow','orange'];
    }
});

With Angular 1.5, the notion of multi-slot transclusion was added. Instead of transcluding the contents of the element into one location, the content can be divided up and inserted into different slots. To do this, the transclude option is assigned an object, and the object has key value pairs which match the content to the desired named slot. This approach to transclusion is easier to understand, more flexible, better suited for composing components, and mirrors the approach of Angular 2.

Angular 1 – Multi-slot Component Transclusion – HTML

<div ng-app="myApp">
  <list-box>
    <header>
      <h2>List of Colors</h2>
    </header>
    <footer>
      <small>Source: Sample Color Institute</small>
    </footer>
  </list-box>
</div>

Angular 1 – Multi-slot Component Transclusion – JavaScript

angular.module('myApp', [])
  .component('listBox', {
    transclude: {
      'header': 'header',
      'footer': 'footer'
    },
    template: `<ng-transclude ng-transclude-slot='header'></ng-transclude>
      <ul>
        <li ng-repeat='item in $ctrl.items'>{{item}}</li>
      </ul>
      <ng-transclude ng-transclude-slot='footer'></ng-transclude>`,
    controller: function() {
      this.items = ['red','blue','green','yellow','orange'];
    }
});

So what is the approach of slotted transclusion in Angular 2? There is no transclude option and there is no extra mapping object. Instead, the element ng-content is used to identify where to place the content in the template. Here is an example of single-slotted ng-content:

Angular 2 Single Slot Component HTML

import { Component } from '@angular/core';
import { ListBox } from './list-box.component';

@Component({
  selector: 'my-app',
  template: `<list-box>
      <header>
        <h1>List of Colors</h1>
      <header>
    </list-box>`,
  directives: [ListBox]
})
export class AppComponent { }

Angular 2 Single Slot Component TypeScript

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

@Component({
  selector: 'list-box',
  template: `<ng-content></ng-content>
    <ul>
      <li *ngFor='let item of items'>{{item}}</li>
    </ul>`
})
export class ListBox {
  items = ['red','blue','green','yellow','orange'];
}

The element ng-content has an attribute named select which is used to select which part of the content is inserted at that location. If no select option is specified, then all of the content is inserted. The method to select the content is a simple CSS selector. Here is an ng-content multi- slot example:

Angular 2 Multi-Slot Component HTML

import { Component } from '@angular/core';
import { ListBox } from './list-box.component';

@Component({
  selector: 'my-app',
  template: `<list-box>
      <header>
        <h1>List of Colors</h1>
     </header>
     <footer>
       <small>Source: Sample Color Institute</small>
     </footer>
    </list-box>`,
  directives: [ListBox]
})
export class AppComponent { }

Angular 2 Multi-Slot Component TypeScript

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

@Component({
  selector: 'list-box',
  template: `<ng-content select="header"></ng-content>
    <ul>
      <li *ngFor='let item of items'>{{item}}</li>
    </ul>
    <ng-content select="footer"></ng-content>`
})
export class ListBox {
  items = ['red','blue','green','yellow','orange'];
}

Regardless of the Angular version, both component-driven approaches allow components to use developer provided template through the child content of the component’s custom element.

Conclusion

The ability to define and configure the user interface of components is critical. While different systems take different approaches, Angular’s (both 1 and 2) is decidedly in favor of templates tied to JavaScript code through component APIs. In Angular 1, the component API is a function that accepts an object of configuration options, and in Angular 2, it is an ES2015 class decorated with component configuration information. Both Angular 1 and Angular 2 provide a mechanism for allowing the developer who consumes the component to further customize the user interface through a system called transclusion in Angular 1 and slots in Angular 2.

Continue to Part 2.


Author: Eric Greene, one of Accelebrate’s instructors.

Accelebrate offers private AngularJS training and JavaScript training for groups and instructor-led online JavaScript classes for individuals.

Categories: JavaScript Articles
Tags: , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

*



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Please contact us for GSA pricing.
Contract #GS-35F-0307T

Please see our complete list of
Microsoft Official Courses