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

August 29, 2016 in JavaScript Articles

Written by Eric Greene


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.


Written by Eric Greene

Eric Greene

Eric is a professional software developer specializing in HTML, CSS, and JavaScript technologies. He has been developing software and delivering training classes for nearly 19 years. He holds the MCSD Certification for ASP.Net Web Applications, and is a Microsoft Certified Trainer.



Contact Us:

Accelebrate’s training classes are available for private groups of 3 or more people at your site or online anywhere worldwide.

Don't settle for a "one size fits all" public class! Have Accelebrate deliver exactly the training you want, privately at your site or online, for less than the cost of a public class.

For pricing and to learn more, please contact us.

Contact Us Train For Us

Toll-free in US/Canada:
877 849 1850
International:
+1 678 648 3113

Toll-free in US/Canada:
866 566 1228
International:
+1 404 420 2491

925B Peachtree Street, NE
PMB 378
Atlanta, GA 30309-3918
USA

Subscribe to our Newsletter:

Never miss the latest news and information from Accelebrate:

Microsoft Gold Partner

Please see our complete list of
Microsoft Official Courses

Recent Training Locations

Alabama

Huntsville

Montgomery

Birmingham

Alaska

Anchorage

Arizona

Phoenix

Tucson

Arkansas

Fayetteville

Little Rock

California

San Francisco

Oakland

San Jose

Orange County

Los Angeles

Sacramento

San Diego

Colorado

Denver

Boulder

Colorado Springs

Connecticut

Hartford

DC

Washington

Florida

Fort Lauderdale

Miami

Jacksonville

Orlando

Saint Petersburg

Tampa

Georgia

Atlanta

Augusta

Savannah

Idaho

Boise

Illinois

Chicago

Indiana

Indianapolis

Iowa

Ceder Rapids

Des Moines

Kansas

Wichita

Kentucky

Lexington

Louisville

Louisiana

Baton Rouge

New Orleans

Maine

Portland

Maryland

Annapolis

Baltimore

Hagerstown

Frederick

Massachusetts

Springfield

Boston

Cambridge

Michigan

Ann Arbor

Detroit

Grand Rapids

Minnesota

Saint Paul

Minneapolis

Mississippi

Jackson

Missouri

Kansas City

St. Louis

Nebraska

Lincoln

Omaha

Nevada

Reno

Las Vegas

New Jersey

Princeton

New Mexico

Albuquerque

New York

Buffalo

Albany

White Plains

New York City

North Carolina

Charlotte

Durham

Raleigh

Ohio

Canton

Akron

Cincinnati

Cleveland

Columbus

Dayton

Oklahoma

Tulsa

Oklahoma City

Oregon

Portland

Pennsylvania

Pittsburgh

Philadelphia

Rhode Island

Providence

South Carolina

Columbia

Charleston

Spartanburg

Greenville

Tennessee

Memphis

Nashville

Knoxville

Texas

Dallas

El Paso

Houston

San Antonio

Austin

Utah

Salt Lake City

Virginia

Richmond

Alexandria

Arlington

Washington

Tacoma

Seattle

West Virginia

Charleston

Wisconsin

Madison

Milwaukee

Alberta

Edmonton

Calgary

British Columbia

Vancouver

Nova Scotia

Halifax

Ontario

Ottawa

Toronto

Quebec

Montreal

Puerto Rico

San Juan

© 2013-2019 Accelebrate, Inc. All Rights Reserved. All trademarks are owned by their respective owners.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.