NG-CONF 2017 Day Three Highlights

Keynote

Brad Green spent some time discussing how Google as a company is fully embracing Angular as an application development framework. In fact, the goal is to have all web application development within Google to be using Angular by the end of 2017. He also explained that it makes sense for them to invest so many resources into Angular as an open-source project because of the additional benefits to Google itself. The primary benefit is the large ecosystem that has grown around Angular. Libraries and tools would probably not exist if Angular was simply an internal Google project. In addition, Google has built several tools internally that have been reproduced in other open-source projects. It makes sense that there is benefit to sharing these efforts with the community. It also helps with hiring within Google, where proprietary in-house tools require additional training and ramp-up for new hires. And, of course, the overall quality of the source code is improved by the great feedback received from the community through PRs, documentation, and training.

For example, the Angular team originally went down the path of creating its own JavaScript super-set called AtScript. It became evident that TypeScript was a more viable option, though, so the Angular team abandoned this effort and fully embraced TypeScript. Interestingly though, behind the scenes at Google, there was a formal process for approving a new development language and TypeScript needed to go through this process to gain approval for use in Google projects. It turned out, however, that this process had been set up but nobody had attempted to walk through it. TypeScript was the first to go through this process. But after much back and forth over two years, the process was completed and TypeScript was finally approved.

Google over the years has also implemented tools to build JavaScript applications at a very large scale and tools like the Angular CLI were not really appropriate for building applications within Google. A tool named “Blaze” is what is currently used for these builds. But they are beginning to open source components of this tool in a project called the “Bazel” (the name “Blaze” with letters rearranged).

Rob Wormald then focused on the goals of the Angular project in general. He talked about three general categories of web applications, highly interactive sites (like a commerce application and shopping cart), mostly static sites (like a blog), and mixtures of interactive and static content. Whatever the application, Google’s own analysis has shown that users expect a web page to display in less than two seconds and will most likely abandon the page if it takes longer than three seconds. Rob emphasized that a goal for the Angular project was to help make web applications that meet these expectations.

So, improvements have been made to what was formerly called Angular Universal and brought in under the main core framework as the platform-server package. Functionality has been added to support these scenarios, allowing the construction of the page server-side, rendering to more complete HTML before being delivered to the browser. (More details below.)

Upgrade Story

“AngularJS” is the name that is used for Angular version 1 applications, “Angular” is the name for all other versions. If you’ve already started a project in AngularJS, there is a path to upgrading the application to “Angular” through the NgUpgrade module. This module allows you to run both AngularJS and Angular components within the same application at the same time (you end up with two “instances” of the Angular framework running together). It allows you to share components and services between the instances of AngularJS and Angular. It also allows you to share routes between them.

Victor Savkin gave some practical instructions for upgrading your application. You could approach upgrading using “vertical slicing” where overall features of the application are upgraded individually. Or approach using “horizontal slicing” where individual components and services are upgraded. There are also different ways that you might arrange the routing within the application.

Whatever approach you take, components and services can be upgraded to be used by Angular or downgraded to be used by AngularJS. This should give you a good iterative way to progressively migrate your application over to the latest version.

Pre-Rendering

Jeff Cross dug more deeply into pre-rendering, the process of generating more complete HTML before the content is sent to the browser. The NodeJS-based server module (in the platform-server package) provides services for rendering an Angular application offline. It also provides functionality that can deal with HTTP requests and routing as well.

The main reasons for pre-rendering are ensuring that your application can to load and become interactive as quickly as possible, to allow the pages to be scrapeable (i.e. in order to display the preview pane you see in other applications like Twitter or Facebook), and to allow the pages to be crawlable (i.e. for Search Engine Optimization).

Jeff displayed a graph with axes of “Completeness” vs. “When Rendered”. How much pre-rendering you might do in your application depends on different factors, such as the number of pages that are included, the volume of content, localization used, the amount of user-customized content, the freshness of the data, and the frequency of deployment and rebuilding content.

An additional consideration is what to do with user-generated events that occur before the page has been made fully interactive (i.e. before the Angular application has been bootstrapped and is fully functional). If the user begins typing or clicking on the page before it is ready, what should happen? To address this, a component called “preboot” is used to record these user events and then play them back accordingly. This does require some careful consideration of exactly what happens, though.

Right now, the documentation for the pre-rendering functionality is not yet available, but it should be included on the Angular.io site soon.

Packaging Libraries

The Angular team has put together a specification for the requirements and best practices of building packages that can be used as libraries within Angular applications. There are a number of things that should be included in a library package such as TypeScript definition files (*.d.ts) and a *.metadata.json file. Common packages that are used by the library should be added as peer dependencies in the project’s package.json file. The Angular compiler, ngc, which wraps the TypeScript compiler, can be used to build the artifacts required for the library.

To take advantage of optimizations like tree shaking, the recommendation is to create a single NgModule for each component within the library. Having multiple (or all) components in the library within a single NgModule will end up bringing in code that may not be used by the consumer of the library.

Other Highlights

  • The Angular Language Service can be used in our code editors to help with code completion, errors, and references between components and templates. This service is available in Visual Studio Code and WebStorm and will be available for other editors soon.

  • If you’ve used the Redux library in your application, you have probably also used selectors from the Reselect library. Kara Erickson demonstrated using selectors for form validations when building reactive forms. But she also talked about how reactive forms will be moving towards using observable streams for validations rather than the current implementation. This gives complete control over form validation, including debouncing frequent field changes, prioritizing asynchronous validations, and presenting errors less frequently. It also allows for push validations, i.e. validations that come from the server (for example, the highest bid for a product has changed and a current bid is no longer valid).

NG-CONF 2017 Day Two Highlights

Day Two changes things up from the Day One and Day Three single-track format. There are breakout sessions focused on a specific topic as well as chances to sit down and ask questions of others who have expertise in Angular, including members of the Angular team themselves. It’s a good chance to listen to how others are approaching their development challenges and opportunity to discuss lots of interesting details in depth.

Practices and Performance

The first session of the day had a few large organizations talk about the things they are doing within their organization to manage Angular projects, how they approach analyzing the performance of these applications, and what kinds of things might help them improve these operations.

In terms of analysis, there was a great emphasis on metrics (“plan and measure”). This included low level tracking of “time to first paint”, “time to meaningful content”, and “time to interactive”. But it also included higher-level tracking of things like “perceived performance” (obtaining feedback about how the user perceives the performance of the application).

Tools were mentioned that help in this analysis. The primary tool, of course, is the Developer Tools within the browser (there was a lot of praise for the capabilities of Chrome Developer tools particularly). Other tools mentioned were:

  • Lighthouse – a chrome extension / command-line tool that audits for performance, accessibility, and other interesting metrics. This can be used in a continuous integration environment as well.

  • Source Map Explorer – a tool that helps analyze code bundles to avoid bringing in additional code that is not used, improving the size of bundles.

  • Webpack Bundle Analyzer – a tool for analyzing the bundles that are created specifically by Webpack (generated from its own log files).

  • Bazel – an open-source version of Google’s own build tool. It represents work that they’ve done internally for fast, optimized builds on an extremely large scale.

Generally, the presenters seemed satisfied with what they could do with Angular within their own organizations. They were also continuing to research and investigate additional optimizations, such as better performance by using web workers or service workers.

About Angular

In the “Ask the Experts” room, several of us surrounded Rob Wormald. My thanks to him for his patience while we peppered him with our questions.

It was a very useful discussion for me. I am currently in the middle of a big React-based project. At the beginning of the project we had in-depth discussions about what platform that we should build on. Obviously React is very popular and perfectly acceptable for building big web applications. But even after diving into deep into React, there are still some things that I prefer in Angular when on large projects with distributed teams.

AngularJS (version 1) of the framework and its shortcomings are well known, but Angular (the name used for all later versions) has addressed these concerns. However, since it is a full framework, rather than having a narrower focus on just the view (as React does), it is definitely more complex. It requires a build system and is best used with TypeScript. There are additional requirements for optimizing production builds. It requires registration of components, directives, and services within an NgModule. Decorators are used to describe how a component is to be used. And Observables are used throughout the framework to deal with asynchronous operations.

But this additional complexity is not haphazard and comes from trying to build a great framework for building applications in the browser, for mobile devices, and for the desktop. The Angular team also wants to be supportive of the community overall and so they don’t spend a lot of time trying to explain to folks why Angular is better. It just leads to toxic, unhelpful discussions so many times.

Rob emphasized that they want the documentation to be better overall, which should include more “philosophical” information about the framework, i.e. why a particular tact was taken for some feature.

Ultimately, your team will make the decisions about what platforms they will use to build an application. I look forward to seeing more information about the technical distinctions of Angular to help teams make better informed decisions when choosing from the wide breadth of options.

Reactive Applications

One thing that was evident is that “reactive programming” is where Angular has a strong focus. This is certainly one of the reasons that RxJS is such a key component of the Angular framework. Reactive programming views information as an asynchronous stream of data. The application can orchestrate these streams in interesting ways. Even for simple HTTP requests, observable versions of these requests can easily be enhanced by adding automatic retries upon failure, caching and expiration of resources, combinations with other requests (in series or in parallel), and data refreshes upon change using simple async binding.

There are several libraries that also provide additional functionality built on top of observables. For example, the store component provides an implementation inspired by the Redux pattern but using observables to communicate application state changes.

WebPack

Sean Larkin is a member of the WebPack team and a primary advocate for the tool as well. He did a talk to a large audience going into some of the inner workings of WebPack. Interest is high in this tool and many projects have adopted it, including Angular. The talk emphasized that “everything is a plugin”. A plugin just expresses the events that it is interested in and responds accordingly. The result is a tool that is extremely flexible and configurable.

This flexibility is what has made WebPack so popular. It ends up replacing build tools (like gulp or grunt) and has a large ecosystem to transform, analyze, and optimize your JavaScript projects. Sean also talked about work to expand this ecosystem further by making WebAssembly a first-class citizen in the plugin architecture, which should allow even further optimization and additional inputs into our builds.

Sean stepped us through a project that he had set up to demonstrate writing a plugin yourself (a brave move for such a large audience all trying to work through the examples at the same time!) But it was a successful and very helpful talk. Definitely worth watching.

NG-CONF 2017 Day One Highlights

I’m glad to be back again at ng-conf in Salt Lake City. I’ve used Angular since the very beginning and it continues to get better. Here are some of the highlights that stood out to me from the first day of the conference.

Keynote

During the keynote, there was discussion about gauging the success of Angular. They estimated that the community is around 1.3 million users of AngularJS (version 1 of the framework) and 810 thousand users of Angular (versions 2 and 4 of the framework, they skipped version 3). Of all the applications out there, about 90% of them are internal applications (ones we can’t see because they are behind the corporate firewall). 17% of the public Angular applications are already on version 4 of the framework.

There are over 200 applications internal to Google that are using the framework. These applications serve as an initial test bed for all updates of the framework, helping to ensure smooth updates to new versions.

Impressive statistics.

Version 4, released a short time ago, has some great improvements in performance and the size of payloads. The team worked hard to ensure that upgrades went smoothly and there were no breaking changes in the framework APIs. They’ve released the first version of the Angular CLI, a helpful tool for building and maintaining Angular applications (more details below).

For enterprises that can’t upgrade on a regular cycle, they announced Long-Term Support (LTS) for Angular version 4 through October 2018. This ensures that critical bug-fixes and security patches will be applied for a while.

Tools

JavaScript tooling has always been the biggest source of frustration for building applications. Angular is improving in this area by introducing the Angular CLI, a command-line tool that can construct an initial Angular application as well as assist in the continuing development of the application, adding new components, modules, services, etc. It does many of the chores of constructing module definitions and building development and production versions of the application. It also includes unit testing and end-to-end testing.

The CLI can be configured if required, but at some point if you out-grow the CLI, you can eject the build scripts and customize them as much as you need.

Minko Gechev created a tool, called Codelyzer, that extends TSLint to check source code for common problems, specifically in Angular applications. But he also went farther by building a tool that creates an abstract syntax tree specific to Angular as well as a tool to visualize this tree. And just for fun, created a tool to see this visualization in 3D.

Components

Angular (the name for all versions of framework after version 1, as opposed to AngularJS which is the name used for version 1) has fully embraced the component model. These means that you construct your application by building a tree (hierarchy) of components. This provides a good way to organize your application into logical pieces (areas of your application) as well as functional pieces (responsibilities). Some components can be container (“decision”, “smart”) components that know how to interact with business services, where others are presentation (“presenter”, “dumb”) components that are only concerned with presenting user interface. Container components generally pass data down to presentation components and respond to events from the presentation components by translating them into some business function.

Justin Schwartzenberger presented a talk that dug into some of the performance and architectural decisions that should be considered when creating your component hierarchy. He talked about different “taxes” paid for different implementations, the tax paid for rendering components and change detection tracking, the tax paid for using HTML elements just for containment, the tax paid for coupling parent and children components. It was really a great explanation of how to think about the organization of your components.

Related to this, the Material Design team has done great work to make the Material components integrate better with standard HTML markup. For example, instead of defining a <md-button> custom component, you can attach an md-button attribute to the button element itself instead, <button md-button ...>. These, along with many other optimizations make the library much more flexible and more natural to those creating HTML markup.

Other Highlights

  • The Angular Animations module has been updated to include a feature that has been requested frequently, the ability to add animations to route transitions.

  • The Angular team wants to continue to improve the CLI in many areas, including increasing the speed of building the application, reducing the size of the output, using a common configuration for both development and production (to reduce differences in operation between environments), better error messages. They also want to get to the point where the CLI is more like an SDK that can be customized with plugins, custom templates, and additional tooling.

  • Tobias Bosch did a deep dive into the Angular Ahead-of-Time (AOT) compiler, explaining what was going on in building components and HTML templates into JavaScript executed at run time.

Angular2 UrlResolver for Component Assets

Thoughtram published a piece about Component Relative Paths in Angular2. This is great! It makes working with components much easier.

However, I wasn’t sure about other component-specific assets, like images for example. So I asked about it:

There is a solution, it turns out. There may be some better options in the future, but you can use the UrlResolver object to do this.

You can inject the resolver object into your component and then create a resolvePath function that can be used by the view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {Component} from '@angular/core';
import {UrlResolver} from '@angular/compiler';
@Component({
moduleId: module.id,
selector: 'title-page',
styleUrls: ['title-page.component.css'],
templateUrl: 'title-page.component.html'
})
export class TitlePageComponent {
constructor(private urlResolver: UrlResolver) {
}
public resolvePath(path) {
return this.urlResolver.resolve(module.id, path);
}
}

In the view, just bind an image element to the resolvePath function.

1
<img [src]="resolvePath('angular.svg')">

As the Thoughtram article mentioned, this solution is tied to using the CommonJS format for modules. In other words, the module.id property I’m using here as an input into the UrlResolver object would not exist for other formats.

Updated Mar-19-2017

This technique is now obsolete. https://angular.io/docs/ts/latest/guide/change-log.html

NG-Conf 2016 - Day Three Wrap Up

The last day of ng-conf 2016 continued with lots of great information. Some highlights:

  • Daniel Rosenwasser gave a good overview of TypeScript and a little bit of its history. He also demonstrated benefits of using TypeScript with Javascript directly. It can be beneficial in catching things in regular Javascript code (serving a similar purpose to the ESLint Javascript lint tool). He also showed some new features coming in TypeScript, like async/await and handling null and undefined types.

  • Aysegul Yonet talked about the new features of pipes in Angular2, including how pipes can be configured to better handle change detection.

  • Ken Synder presented details about transclusion udpates. In Angular1 you can now have multiple slots for injecting transcluded elements. The ng-transclude directive now supports a name property and directives can specify multiple transclusion slots that correspond to these names. This allows Angular1 to come closer to how ng-content tags work in Angular2.

  • Matias Niemela gave a whirlwind tour of the new animation features in Angular2. After evaluating the different options of working with animations in the DOM (through CSS transitions, Key Frames, or direct Javascript), they settled on using Javascript, which they call “web animations”. The benefit is that it gives an application very fine-tuned control over animations, works around various issues with key frames and CSS transitions, provides better performance and less UI jank, and allows for better rendering (including in offline scenarios).

  • One of the biggest complaints about using Angular has been the friction around setting up a project and its build tools, even when working with a simple Angular project. Mike Brocchi demonstrated the Angular2 command line interface that aims to address this issue. It is not only for creating a new Angular project, but it also aims to assist in growing and maintaining the application. It can add components, services, routes and other important pieces of an Angular application, but it will also automate upgrades to newer versions of Angular that will come in the future. In addition, it will incorporate the offline template compiler.

  • If you feel a little lost in all of the terminology and moving parts of the Angular framework, you should watch Scott Moss present “Angular for the Rest of Us”. He does a great job (and uses some excellent metaphors) describing Angular2 development.

  • One thing that Scott mentions and was echoed in other presentations as well, is that error reporting is much better in Angular2. In Angular1, the source of an error message was easily lost, usually showing a call stack consisting of references within the Angular1 source code. Angular2 does a great job of helping you to identify the actual source of an error. It also fixes issues with setting breakpoints correctly in the browser developer tools.

  • Rob Wormald showed the Angular-specifc implementation of the ideas behind Redux (which I blogged about a few days ago). He also included a demo of the time-travelling debugger that can be used to diagnose changes to application state, all by including a single element, <ngrx-devtools></ngrx-devtools>, into the page. The ngrx/store project is integrated with the Angular framework and provides a “predicatable state container”. This is one project of a collection that focus on Reactive Programming implementations for Angular2.

  • In Angular2, you can build input forms using templates like you do in Angular1, but you can also build forms using a FormBuilder component that allows you to specify data binding and validations within your component rather than in the template. Deborah Kurata does a great job of showing the differences between template forms and model driven forms (and using a hybrid approach that falls somewhere in between).

Of course, there were other presentations as well over the last three days. They are on the ng-conf 2016 YouTube channel. Unfortunately, the break-out sessions from Day Two did not get recorded, as far as I know.

Last up was the usual panel of Angular team members where folks could ask questions. Some of the team’s answers that stood out to me were:

  • The question always comes up, “When is Angular2 going to be ready?” A key piece is the template compiler. When that is ready, then Angular2 will be ready. However, Brad Green stated clearly that for several large customers and internal Google projects, it is production ready now. If I understood correctly, AdWords, which may be the biggest money-making application on the planet, has already deployed Angular2 components.

  • And it’s not an “us vs. them” mentality. Angular is one of many solutions and can work alongside of other solutions (including React).

  • The template compiler might even be available for preview by release candidate 2. (They are at release candidate 1 as of this writing.)

  • HTTP2 is a technology that is very interesting to the Angular team. It may reduce or eliminate the need for bundling and has already been proven to have other performance benefits as well.

  • TypeScript is highly recommended for Angular2 projects, even though it is not strictly Javascript. The TypeScript team is eager to work with the standards organizations to make sure TypeScript doesn’t diverge from Javascript in problematic ways. The Angular team even did some research to determine if type systems (like implemented in TypeScript) would be useful in the browser, and came to the conclusion that it was not a good fit.

  • If your team decided that TypeScript was not a good solution, you can always use the Javascript generated by the TypeScript compiler moving forward since it would be close to the original source anyway (unlike CoffeeScript which is generally a separate language, not a superset of Javascript like TypeScript is).

I would like to personally say thanks again to all of the organizers and sponsers of ng-conf. My third year at ng-conf has been as satisfying as the first two. I would also like to give a special thanks to the Angular team for all of their efforts. The Angular2 framework is very exciting and I am looking forward to building great things with it.

NG-Conf 2016 - Day Two Wrap Up

Some highlights of day two of ng-conf 2016:

  • Day two started with a focus on the future of Angular2. No they didn’t announce Angular3, but they did focus on the work they are finishing up and looking towards the future. For example, work is in progress to build an offline compiler for Angular2 applications. This should greatly reduce the payload delivered to the browser. In fact the Angular2 team’s aggressive goal is to have the “Hello World” version of an an Angular2 application be only 10K!

  • A second release candidate came out today. Part of the changes from beta to release candidate includes changing the package references. For example, angular2/core is now referenced as @angular/core. This change allows for better use of the ES2015 modules and better optimization when using the offline compiler.

  • The Material Design team is creating a base set of services that are common to user interface components in general, such as mobile usage, accessibility, internationalization, overlays, and more. The broad set of material design components build on these services, but other components can as well. They are also looking to expand the set of components to things like a Google Map component, a video player, rich text editor, and more.

  • Tools have been created for assisting in upgrading Angular1 applications to Angular2. During the upgrade, Angular1 hosts Angular2 components. Angular1 components and services can be upgraded to be used by Angular2 components and services. Likewise, Angular2 components and services can be downgraded and used by Angular1 components. This allows you to migrate an Angular1 application piecemeal instead of using a “big-bang” rewrite.

  • The Component Router was updated significantly from Beta to the Release Candidate. The Beta router was in fact deprecated. There are still features to add to the latest router code (such as the RouteData service, default routes, and protected routes), but it is functional as it is. In the Angular2 router, components define their own routes, rather than defining all routes up front. This also allows for dynamically loading routes, rather than having to load an entire SPA implementation all at once.

  • NativeScript continues to impress. NativeScript allows you to write truly native mobile applications using Javascript. These applications use native user interface on the device, not user interface run within a web frame. It is similar to the recently acquired Xamarin platform, except that Javascript is the language used rather than C#. The integration with Angular2 allows you to use the same property and event binding as you would in web applications, except on native components.

  • The NativeScript team was so impressed with the “Generative Art” presentation that they took the code and published an Android application in the Google Play Store the very next day! Nice!

  • Jeffrey Whelpey and Patrick Stapleton gave a more in-depth presentation about Angular Universal features that really demonstrated its power. The team has created a starter application that you can learn from and talked about some of the configuration and setup required to use it. Universal delivers the server-rendered Angular2 application while the preboot function records events from a user (such as a keystroke or mouse click) and then replays these events once the server-rendered application is replaced with the client-rendered code. This should greatly enhance the user’s experience with our web applications. There are even scenarios where Universal can be used in dealing with static rendering that is stored on a CDN (Content Delivery Network).

Looking forward to Day Three!

NG-Conf 2016 - Day One Wrap Up

I just finished up the first day of ng-conf 2016 and as usual, it has been a great informative conference. Thanks to the organizers, sponsors, and Angular team members for all your efforts.

Here are some highlights from today:

  • Brad Green’s keynote address, of course, covered the big picture of the Angular development work. The Angular1 site had 1.3 million visitors and the Angular2 site has already had 306 thousand visitors, even though the first release candidate was only made available yesterday. The team is focused on making Angular2 a robust framework, but there are still some gaps to fill before it will be ready for production release.

  • What was really impressive is the breadth of applications that can be created with Angular2. Web applications we all know and love are just part of the story. You can create progressive apps that can be used offline. You can create mobile web applications with native-like user interface using Ionic. You can create truly native mobile applications with NativeScript. You can create cross-platform desktop application on top of Electron.

  • If need a quick look at what Angular2 actually looks like, John Papa does a great job of live-coding an Angular2 application.

  • The features around localized CSS are a very nice addition to Angular2. Being able to create a component with it’s own CSS that doesn’t leak out to other HTML elements will really help in the creation of reusable components. Justin Schwartzenberger provides the details.

  • Universal applications (where the Angular2 application is rendered on the server rather than just in the browser) will provide a lot of value. This is not only for Search Engine Optimization (SEO), but it will also help with Progressive Web Applications. Jeffrey Whelpey and Patrick Stapleton talk about some of the issues they had to work through to create the framework for Universal applications.

  • Misko Hevery gives a big picture view of the important features of the router in Angular2. As I mentioned, the first Angular2 release candidate (RC1) was made available just yesterday and I’m in the process of upgrading my Angular2/Redux example application to the release candidate. But the router functionality was changed significantly from the Beta release. These changes are detailed here.

  • Augury is the new tool that replaces the Batarang tool used for Angular1 applications. It will have a much broader feature set than the original.

  • The documentation has been updated at Angular.io and now also includes the Angular2 Style Guide.

  • Julie Ralph gave some interesting details about how Zones.js is used to improve testing in Angular2.

  • One of the most interesting presentations was by Tero Parviainen demonstrating “Generative Art” using Angular2. It was an impressive demo not only because of the application being demonstrated, but I thought it also showed the elegance of Angular2 itself.

  • And of course, Shai Reznik does another hilarious ribbing of the Angular team.

Looking forward to Day Two!

Using Redux to Manage Angular2 Application State

In this post, I want to focus on managing application state. In the example application, I tried to create an application that had a reasonably complex user interface (“complex” is relative here, of course). The user interface needed to show where changes in one area of the page had immediate impact on other areas.

If we look at the “edit view” of the application, there are three panels: the list of images grouped by tags, a details table of image information, and the image edit panel. User actions performed in one of these panels have an immediate effect on the other panels.

Edit View

For example,

  • In the details table, a user can sort the image details by the image title, size of the image, and by the date the image was taken. When the images are sorted by title, changes to the title in the edit view are reflected in the table itself and may change the order the images are listed in.

  • Changing the order that images are sorted by affects not only the details table, but also changes the order that images are displayed within each group. Images in each group will be sorted in the same order as the detail table.

  • Picking a subset of tags to filter the list of images by changes the images displayed in both the details table and in the grouped images.

  • Assigning tags to an image in the edit panel changes the list of tags and images displayed in the group view. Tags are added or removed as necessary based on the set of all tags assigned to all images. Of course, this changes the list of tags that can be selected for filtering as well.

These interactions represent changes in application state.

Managing Application State

If you consider the pieces that make up the application state, you might come up with a list like:

  • The list of images and their details.
  • The current order that images are sorted in and whether it is ascending or descending order.
  • The set of tags that the user has selected to display.

There are, of course, a number of ways to approach the problem of dealing with your application’s state and how changes are managed. A popular idea has been using publish/subscribe messaging to pass around information about changes to state. For example, a “sorted images” message might be published where subscribers would respond to this event by performing a sort operation on the displayed images.

Angular1 even formalized this message passing through the use of the $scope $emit/$broadcast (publish) and $on (subscribe) functions.

The problem with this technique is that as the application grows and includes more complex interactions with the user, it becomes harder to reason about what is actually happening in the application. How does one message affect the application overall? What happens when one message causes a cascade of other messages? It becomes harder to predict what an application will actually do as the user interacts with the application.

So, what if you could organize your application so that changes to application state are more understandable and predictable? This is the problem that Redux attempts to address.

Redux

Redux bills itself as a “predictable container for Javascript apps”. It does this by specifying a pattern for constraining access to the application state and how it is changed (mutated). I don’t want to go into all the details of Redux here because the documentation is excellent. Also, the “Cartoon Intro to Redux” is also a fun introduction to the basic ideas behind Redux.

Components obtain application state information, in our case things like images, sort order, tags, etc., from a central “application store”, which is managed by the Redux library. Components do not directly change the state, but create actions that are dispatched to the application store. The actual changes are performed by reducers that are added to the application store when it is first set up. Once the changes are made, the new application state is passed out to the components that subscribe to the application store change event. View components can then modify the user interface accordingly.

There is a cycle, then, that the application goes through to manage transitions from one application state to the next. A user interacts with the application, a component translates that interaction into an action that is dispatched to the application store, the store applies reducers to the action to mutate the state, the application state is passed out to components that have subscribed to the application state change event, and the views update their UI based on the new state.

Redux Application Cycle

At first glance, this extra complexity might not seem worth the trouble. And it may not be the right solution for a lot of applications, but the ideas behind Redux have become popular and offer several benefits.

  • View components are simpler since decisions about how the application state should change based on some user action are delegated to another isolated area of the application (the reducers). In other words, view components translate a user activity to an action and the reducers determine how that action should mutate the application state. A nice, clean separation of concerns.

  • The application store is the single authority (the “system of record”) for the application state. View components serve a simple purpose to take in the application state and generate the view that represents that state.

  • Since all application state changes go through the application store, many different user interactions can have the same result on application state. Application state is maintained consistently regardless of how the state change was initiated and even if it was initiated by different events.

  • Having a central application store then allows your application to hook into the store itself to provide additional functionality, such as logging application state changes, undo/redo operations, time-travelling debugging, and more.

Some of this might seem like common sense and could be accomplished in different ways. However, I’ve worked on many different applications with distributed teams of various sizes and skill levels, and I like the benefit that it brings to this situation as well. In applications with a complex user interface and with team members working on related parts at the same time, it defines good boundaries that help prevent cases where subtle changes to the application state are hidden within a large project. It allows teams to reason better about what changes to the application state really mean.

Actions

Let’s look at some of the actions that the example application uses. An action corresponds to something important that happened within the application and holds additional information about what happened. An action does not perform any function. It only serves to hold information (that ultimately will be passed on to a reducer).

For example, when the user chooses to sort the images, an action is created that holds the property that the images should be sorted by and includes whether the images should be sorted in ascending or descending order.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export const SORT_IMAGES = 'SORT_IMAGES'
export enum ImageSortBy {
title,
size,
date
}
export function sortImages(sortBy: ImageSortBy, isAscending: boolean = true) {
return {
type: SORT_IMAGES,
payload: { sortBy, isAscending }
}
}

The sortImages function is called an “action creator”. It creates the action itself. The action has a type (SORT_IMAGES) and additional “payload” information: the property to sort by and the order of the sort.

An action creator function is not required, but I find that action creators are handy in ensuring consistency of the action data itself. Also, in a future post I’ll talk about using action creators as a “thunk” into the Redux dispatch mechanism that allows us to do asynchronous operations (like loading the initial list of images).

I’m also using a format for actions that follows the Flux Standard Action syntax. Actions are just objects and can be any format you want, but conforming actions to a standard like this helps to make actions more understanble and consistent.

Another example of an action is when the user selects the list of image tags that should be displayed.

Image Tag Selection

The user is presented with a selection of tags that should be included, where a checkbox selects the tag or not, but the implementation reverses the sense of this and instead maintains a list of the tags that are excluded, which is reflected in the EXCLUDE_IMAGE_TAGS action.

1
2
3
4
5
6
7
8
export const EXCLUDE_IMAGE_TAGS = 'EXCLUDE_IMAGE_TAGS'
export function excludeImageTags(excludedTags: string[]) {
return {
type: EXCLUDE_IMAGE_TAGS,
payload: { excludedTags }
}
}

Reducers

Reducers take an action and transform the application state accordingly based on the action. For the example application, information about the images themselves, displayed items, sort order, and selected tags are maintained. The initial state is defined below. Reducers take this initial state and transform it based on the actions that are dispatched to the application store.

1
2
3
4
5
6
7
8
9
const defaultState = {
sortBy: ImageSortBy.title,
isAscending: true,
isLoading: true,
dataSet: {},
displayedItems: [],
excludedTags: [],
currentImageId: null
}

A reducer (which comes from the fold function), is a pure function that takes as input the application state (or some subset of it) and transforms it into a new state. A reducer encapsulates what it means for the application to transition from one state to another.

For example, when the SORT_IMAGES action is passed to the reducer, it stores the new sort order and then resorts the images. This includes taking into consideration what images are actually displayed (where the selected image tags have excluded some images).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function sortImageData(state, action) {
return Object.assign({}, state, {
sortBy: action.payload.sortBy,
isAscending: action.payload.isAscending,
displayedItems: getDisplayedItems({
dataSet: state.dataSet,
sortBy: action.payload.sortBy,
isAscending: action.payload.isAscending,
excludedTags: state.excludedTags
})
})
}
function getDisplayedItems(options) {
let sortOperator: any;
switch (options.sortBy) {
case ImageSortBy.size:
sortOperator = (v: any) => v.size
break
case ImageSortBy.date:
sortOperator = (v: any) => v.dateTaken
break
default:
sortOperator = (v: any) => v.title.toLocaleLowerCase()
break
}
return _(_.values(options.dataSet))
.filter((img: any) => !areAllTagsExcluded(img.tags, options.excludedTags))
.orderBy([sortOperator], [options.isAscending ? 'asc' : 'desc'])
.map((img: any) => img.id)
.value()
}

Reducers should not have “side effects”. A reducer takes the input and produces a new output. It should never change things outside of the application state. It shouldn’t execute a function to request data from a web service, for example.

This allows changes to application state to be tightly constrained. Changes will always come through this prescribed pattern. In fact, you can further ensure that outside changes are not made to application state by introducing immutable data into the application store (which we will talk about in a future post).

Application State Changes

Once reducers have performed their duty, a new application state has been created. Components can subscribe to a notification generated by the application store whenever this occurs. View components can then re-render their user interface based on this updated application state.

Note that it doesn’t matter what the catalyst was for the application state change: a user interaction, a message from the server, whatever. View components only have to be concerned about what they do with the new application state.

In the example application, the application store is incorporated into the application as an injectable service. This is done by wrapping the application store in a Provider class.

1
2
3
4
5
6
7
8
9
10
export function provideAppStore() {
return new Provider(AppStore, {
useFactory: reducers => {
let combinedReducers = reducers.reduce((combined, reducer) => Object.assign(combined, reducer), {})
AppStore.instance = new AppStore(combineReducers(combinedReducers))
return AppStore.instance
},
deps: [APP_STORE_REDUCERS]
})
}

The application store has an important dependency — the list of reducers that will be included in the application store itself. We can create an Angular2 multi-value provider to consolidate the list of reducers together. This list then gets injected as the APP_STORE_REDUCERS dependency of the AppStore provider above.

1
2
3
4
5
6
7
8
export function provideReducer(stateName: string, reducer: (state: any, action: any) => any) {
return new Provider(APP_STORE_REDUCERS, {
useValue: {
[stateName]: reducer
},
multi: true
})
}

The combineReducers function allows you to specify a reducer that is associated with a particular subset of the overall application state. In other words, if the application state has a root property named imageData, you can define a reducer that only needs the object associated with this property. In Angular2, we use the above providers to define this association, specify the providers we need, and provide the instance of the application store as well.

1
2
provideReducer('imageData', imageData),
provideAppStore()

State Change Subscriptions

One additional thing to consider is that subscriptions to changes in the application state are subscribing to a global object. The subscription itself will link a subscriber component to this object. Closing the component will leave a reference to the component in memory, causing a memory leak. So unsubscribing from the application store is important before destroying a component.

To make this process easier, I created an AppStoreSubscriber decorator to assist in the subscribe and unsubscribe processes. A decorator is a proposed feature of Javasscript but is implemented in TypeScript (see more here).

Angular2 components have life cycle hooks that we can take advantage of to inject functionality that subscribes and unsubscribes from the application store event. A decorator allows us to inject this functionality into the ngOnInit and ngOnDestroy life-cycle hooks. The application store change event is published as an Observable. The decorator requires a function that subscribes to this Observable and also returns the subscription (or an array of subscriptions) so that the decorator can unsubscribe when the component is being destroyed.

For example, the image detail table takes the application state and binds the table markup to the image data.

1
2
3
4
5
6
7
8
9
10
public onInitAppStoreSubscription(source: any): void {
return source
.subscribe((state: any) => {
this.sortBy = state.imageData.sortBy;
this.isAscending = state.imageData.isAscending;
this.imageList = _.map(state.imageData.displayedItems, (v: any) => {
return state.imageData.dataSet[v]
})
})
}

Note that the source, the application state Observable, is passed into this function, and the function returns the subscription. The decorator injects this functionality into the ngOnInit method, calling the onInitAppStoreSubscription function on the class that the decorator is applied to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
defineProperty(targetPrototype, onInitName, {
configurable: true,
enumerable: true,
get() {
return () => {
if (ngOnInitOriginal) {
ngOnInitOriginal.bind(this)()
}
let subscription = onInitAppStoreSubscription.bind(this)(AppStore.instance.source)
if (!Array.isArray(subscription)) {
subscription = [subscription]
}
this[componentSubscriptionsName] = [...subscription]
}
}
});

The subscription(s) are saved in a private property on the instance of the class. These are then used later when the component is being destroyed. When the ngOnDestroy life-cycle hook is called, each subscription is unsubscribed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defineProperty(targetPrototype, onDestroyName, {
configurable: true,
enumerable: true,
get() {
return () => {
if (ngOnDestroyOriginal) {
ngOnDestroyOriginal.bind(this)()
}
let subscriptionList = this[componentSubscriptionsName]
subscriptionList.forEach((subscription: any) => subscription.unsubscribe())
delete this[componentSubscriptionsName]
}
}
});

Summary

Hopefully this has shown you the benefits of using Redux to manage your application state and build more robust web applications. Even though Redux was built upon ideas around Flux implementations, it quickly became popular due to its simplicity. The ideas behind Redux have been implemented in other libraries as well (such as ngrx).

Yes, I Used jQuery in my Angular2 Application

An astute observer has probably noticed that I’m using jQuery in my Angular2 example application.

“The horror! Revoke his Angular2 license now!!”

I’m in no way saying this is a best practice or even a good practice. Including jQuery is not required or even desirable for most Angular2 applications. Angular1 had a dependency on jQuery or its own jQuery-lite version, but for Angular2 this is no longer the case.

jQuery will also cause problems for other scenarios, like pre-rendering the Angular2 application on the server or in a web worker, because jQuery components are expecting to work with the physical DOM. Plus it weights your application down with additional dependencies that will cause the application to load more slowly for a user.

But using jQuery was practical for this situation and gives me the chance to demonstrate how Angular2 can interoperate with existing web technologies. Let’s take a step back and look why jQuery might be worth considering in certain scenarios.

User Interface Widgets

There’s been much discussion about creating general purpose user interface components (commonly called “widgets”). Think of widgets as resuable pieces of user interface that can help you to build your application, for example, a progress bar, a dynamic and sortable table view, menu bars, charts and graphs, etc. Web Components are an example of a standard that has been proposed to create general-purpose components like this.

But currently, the most popular standard for general-purpose components are ones based on jQuery (and its companion jQuery UI library). It will take a long time for the vast collection of jQuery-based plugins and user interface components to be implemented in some other form outside of jQuery.

Most of the Angular applications that I’ve written have had the need for some complex UI components. I certainly could have addressed this by writing new components using Angular’s directive syntax, but the goal of my projects has always been to create something of value to the business, not to create user interface components. So it did not make sense for me to recreate the wheel when existing components were available. And most of the time, these components were jQuery-based.

Fortunately, Angular has always had ways to interact with other components and has generally “played well with others”. Angular2 is no different. In fact, a lot of the strange syntax changes that you see in Angular2 come from ensuring that Angular2 works consistently with all types of components, including web components, native elements, or your own custom components.

Angular2 Widgets

That said, there are a large number of Angular2-specific components in the works and the need to use jQuery-based components will rapidly diminish. Web components may also become popular. Other components are using a framework-agnostic approach by making use of DOM APIs directly.

As of this writing, there are options for Angular2 specific components, either in progress or planned.

Widgets

In the example application, I’m using a couple of widgets, a menu bar and an auto-complete (type-ahead) component, which are implemented in the Semantic UI library. Semantic UI is a collection of components that bills itself as “a development framework that helps create beautiful, responsive layouts using human-friendly HTML.” Currently Semantic UI has a dependency on jQuery.

Semantic UI Widgets

User interface widgets usually have either a global or explicit initialization. For global initialization, once the page loads, markers are found in the page that identify elements that should be initialized (for example, a class named menu might initialize an application menu component). Explicit initialization requires JavaScript to explicitly initialize a specific element as a component. The latter is the type of initialization that an Angular2 application will do. Page content is loaded dynamically and the appropriate markup won’t be available for any global initialization to find.

In Angular2, we create an attribute directive to perform the explicit initialization of the menu widget. Angular2 directives have life-cycle hooks that can help us, specifically we will use the ngOnInit and ngOnDestroy hooks.

In the ngOnInit hook, we use jQuery on the element that the directive is attached to in order to call the dropdown() initialization method. In the ngOnDestroy hook, we call the destroy operation to remove the widget and to avoid any memory leaks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
declare var $: any
@Directive({
selector: '.ui.dropdown'
})
export class InitializeDropdown implements OnInit, OnDestroy {
constructor(private el: ElementRef) {
}
public ngOnInit() {
$(this.el.nativeElement).dropdown();
}
public ngOnDestroy() {
$(this.el.nativeElement).dropdown('destroy');
}
}

The directive we’ve created allows us to use jQuery in this way because we’ve also injected the native DOM element into the directive (accessed through the nativeElement property of the ElementRef dependency). Be mindful, however, that this technique is considered to be a “last resort”. Don’t use the nativeElement property unless you need direct access to the DOM (as we do in this case).

Note also that the selector for this directive, .ui.dropdown, is the same as the class names that Semantic UI is using for the menu component itself. So we didn’t have to create a new attribute property to associate this directive with an element and we don’t have to change the markup.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="ui dropdown item">
Views
<i class="dropdown icon"></i>
<div class="menu">
<div class="item">
<a [routerLink]="['/Images', 'List']">Image List</a>
</div>
<div class="item">
<a [routerLink]="['/Images', 'Groups']">Image Groups</a>
</div>
<div class="item">
<a [routerLink]="['/Images', 'Edit', {id: null}]">Image Edit</a>
</div>
</div>
</div>

However, we do have to include the name in the list of directives known by the parent component (otherwise the directive will not get instantiated).

1
2
3
4
5
6
7
import {InitializeDropdown} from '../../directives/semanti-ui-init'
@Component({
selector: 'title-bar',
directives: [LoadingIndicator, RouterLink, InitializeDropdown],
// ...
})

Auto-Complete Widget

Another widget the example application uses is auto-complete (also called “type-ahead”). We create another attribute directive similar to the one for the menu component, but this time the initialization takes some additional configuration and the directive will take an input parameter and emit an event.

This directive wraps the underlying Sematic UI component, so we have the ability to present the component to the overall application in a way that makes sense. It can easily expose all of the functionality of the underlying widget, but it could also present a constrained API, more specific to the needs of the application itself. It’s good to consider the API that will be presented, especially in applications with a lot of shared user interface components. We can present a component that only exposes the things that a developer should change, rather than exposing the entire API for the underlying widget. This makes for components that are easier to reuse and helps ensure that the user interface is consistent. This applies to jQuery-based widgets (as in this example), but it could also be applied to more general-purpose widgets implemented in Angular as well.

For the example application, we are wrapping the general-purpose auto-complete widget and presenting it as a more specific image tag selector. It takes as an input property the list of all distinct tags assigned to all of the images, and it exposes an event that is emitted when a new tag is added.

1
2
3
4
5
6
7
@Directive({
selector: '[.ui.search]'
})
export class TagSelectorInput implements OnInit, OnDestroy {
@Input() public tagsList: string[];
@Output() public addTag: EventEmitter<any> = new EventEmitter();
// ...

On initialization, the directive sets up the configuration for the auto-complete widget which includes the list of tags. The configuration also includes handling the item selected event (onSelect), which is translated into the addTag event emitted by the directive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public ngOnInit() {
let tagMap = this.tagsList.map(tag => ({ title: tag }))
$(this.element)
.search({
source: tagMap,
minCharacters : 1,
onSelect: (result) => {
this.onTagSelected(result.title)
},
// ...
})
}
public onTagSelected(tag: string) {
this.addTag.emit(tag)
setTimeout(() => {
$(this.element).search('set value', '')
$(this.element).search('hide results')
})
}

The setTimeout function clears the current contents of the input control and ensures the auto-complete dropdown is hidden. The reason for doing this within a setTimeout function is that this is sometimes required when modifying properties of the widget itself while in a handler called by the widget. It basically executes these operations within the next event loop, giving the widget time to finish executing the handler.

Another feature we need is that when the user presses the Enter key while in the input control, we want the current text to be added as a new tag. This is a feature that is not directly supported by the auto-complete widget, but we can add this functionality easily and have it work as a companion to the existing tagSelectorInput directive.

We create another attribute directive that will be applied to the input element itself. It uses the host component definition to register a handler for the keypress event.

1
2
3
4
5
6
7
8
@Directive({
selector: '[add-tag-on-enter]',
host: {
'(keypress)': 'onKeyPress($event)'
}
})
export class AddTagOnEnter {
// ...

We can also use Angular’s dependency injection system to inject the tagSelectorInput directive into this directive. This works because the tagSelectorInput directive is a parent of the AddTagOnEnter directive.

1
2
3
4
5
6
export class AddTagOnEnter {
constructor(
private el: ElementRef,
@Optional() private tagSelectorInput: TagSelectorInput) {
}
// ...

Using the @Optional decorator specifies that this dependency is not required. Any code that references the tagSelectorInput property should then check to see if is actually defined before using it.

We respond to the keypress event by calling the onTagSelected method on the tagSelectorInput component directly. The component can then emit the addTag event just like when an existing tag is selected.

1
2
3
4
5
6
7
8
9
10
11
12
public onKeyPress(evt) {
if (evt && evt.code === 'Enter') {
evt.preventDefault()
evt.stopPropagation()
let tag = this.el.nativeElement.value
if (tag && this.tagSelectorInput) {
this.tagSelectorInput.onTagSelected(tag)
}
}
}

This is a powerful technique in Angular2 applications that allows parent and child components to work together nicely.

Container and Presentation Components in Angular2

Angular2 has a strong emphasis on components. An application is made up of a tree of components, starting from the root component and working down to child components. This helps to organize your application into logical and manageable pieces. Complex user interface can be broken down into smaller components, assembling them together, to better organize your application’s functionality and how it is presented to the user.

Components can be further categorized. Some components are just simple user interface components, for example like a date-picker widget or a simple user information card. These components are used throughout your application, but they don’t exercise your application logic. That work is delegated to other parts of the application. These components might be called “Presentation” (or “Dumb”) components.

Other components serve to organize and orchestrate the activities of child components and application services. These components know about the application logic. They might push application data down to child components and respond to events emitted by them. They might transform an event into a transition to a new application state. These components might be called “Container” (or “Smart”) components.

Example Application Components

Let’s look at how some of the components in the example application are organized. Of course, you might organize your components differently, but it at least gives us a base for discussion.

We’ll examine the components that go into one area of the application, the list of images.

List of Images Component

This component displays a simple table of all the images in the collection. Column headers are sortable, the row contains a link to view the image, and the expanding the “Tags” column header allows the user to select a subset of images to be displayed based on tags they have been assigned.

Now you could certainly implement all of this functionality as a single component. But you would find that the component becomes too “heavy”; it’s trying to do too many things. For the example, I broke down this functionality into the following components:

I’ll get into the mechanics of using Redux to manage application state in future post, but of the components above, only two of them work directly with the application state. The ImageDetailList component needs the collection of images and the ImageTagSelector needs the collection of all tags associated with the images. The remaining components have data passed into them (through Input parameters) and tell the parent component that some event has happened (thought Output event emitters).

The components can be viewed as a hierarchy, a tree of components.

Tree of Components

Let’s walk down the tree of components and dig into how they work together.

ImageDetailList - The Main Image Detail List Component

This is the main container component for displaying the list of images. Note that you shouldn’t think that only “widget” user interface controls and other presentation components can be reused within your application. This image list component can stand by itself and is reused within the the “Image List”, the “Image Groups”, and “Image Edit” views of the application.

This component is included within the application using HTML markup.

1
<image-detail-list></image-detail-list>

Using this markup by itself, however, is not sufficient to instantiate the component and include it in the page rendering. Any component that includes this component needs to know about it. This is done using the directives property on the @Component annotation.

1
2
3
4
5
6
import {ImageDetailList} from '../../image-detail-list/image-detail-list'
@Component({
directives: [ImageDetailList],
...
})

Adding the import and component class to the parent definition notifies Angular that the component will be included.

(This will probably be a very common issue that developers using Angular2 will encounter. “Why is my component not showing up?”, “Did you include it the component definition?”, “Arrghh!!”, Smacks forehead.)

This ImageDetailList component uses the ImageDetailTable component as a child component, which is declared using HTML markup.

1
2
3
4
5
6
7
8
<image-detail-table
[tableData]="imageList"
[sortBy]="sortBy"
[isAscending]="isAscending"
(toggleTitleSort)="sortByTitle()"
(toggleSizeSort)="sortBySize()"
(toggleDateSort)="sortByDate()"
></image-detail-table>

This may be like no HTML markup you’ve ever seen, but it is valid markup. The documentation for this template syntax will help to better understand this. Ultimately, however, what is going on here is that the child component, ImageDetailTable, exposes an API that the parent component, ImageDetailList, is using. It is binding its own data to API properties of the child component and responding with its own methods when events occur on the child component.

This is a great feature of Angular2. It makes the interface to your components much more explicit, and much easier to use.

Note also that the child component is a custom component that we’ve created. But this same API interface applies to regular HTML markup elements as well. Elements have properties and expose events and our Angular2 components work with them exactly as they do with custom components. In other words there is a very consistent interface for working with the elements within your web page, regardless what type they are, native element, custom components, or web components.

ImageDetailTable - Image List Table Component

So how does the API that ImageDetailTable exposes actually work? The component requires some inputs, specifically:

  • the list of images
  • how the images are currently sorted
  • and whether they are sorted in ascending or descending order.

It also exposes some events, specifically, an event is emitted whenever the user chooses to sort by a particular column in the table.

So another way to think about this is that everything that the ImageDetailTable needs is passed to it through inputs, and everything that it does passes back out as outputs. It really knows about nothing else. This also makes it a presentation (or “dumb”) component.

The ImageDetailList component passes the data to the ImageDetailTable component through input bindings. It also registers a callback method for each of the emitted events through output bindings. These are defined in the component using:

1
2
3
4
5
6
@Input() public tableData: any;
@Input() public sortBy: ImageSortBy;
@Input() public isAscending: boolean;
@Output() public toggleTitleSort: EventEmitter<any> = new EventEmitter();
@Output() public toggleSizeSort: EventEmitter<any> = new EventEmitter();
@Output() public toggleDateSort: EventEmitter<any> = new EventEmitter();

These declare properties on the component class that can be used as bindings for a parent component. This also makes it clear what the component expects as input and what it emits as output, i.e., the component’s API.

The component can then use these properties just like other properties on the class. For example, the tableData property is used to render each row of the table.

1
2
3
<tbody>
<tr class="image-detail-row" *ngFor="#rowData of tableData" [rowData]="rowData"></tr>
</tbody>

For events, let’s look at the column headers.

SortableColumnHeader - The Sortable Column Component

The SortableColumnHeader component is used in a column header to provide user interface for sorting the column in ascending or descending order. It’s a general component and really knows nothing about what it is sorting. It’s just a button along with an indicator that the column is being sorted.

The markup for this component uses the sort state to indicate what icon should be displayed. It also uses an ng-content element so that the additional content associated with column header can be displayed.

1
2
3
4
5
6
<div (click)="onHeaderClicked($event)" style="cursor: pointer;">
<span *ngIf="sortIndicator < 0"><i class="fa fa-sort-desc"></i></span>
<span *ngIf="sortIndicator > 0"><i class="fa fa-sort-asc"></i></span>
<span *ngIf="sortIndicator === 0"><i class="fa fa-sort"></i></span>
<ng-content></ng-content>
</div>

The markup also uses event binding to specify a method that should be called when the user clicks on the sort icon. (Note that the above technique is not very accessible-friendly.)

The component translates this click event into the specific event, toggleSort, that the component itself publishes.

1
2
3
4
5
6
@Output() public toggleSort: EventEmitter<any> = new EventEmitter();
public onHeaderClicked(event) {
event.preventDefault();
this.toggleSort.emit(null);
}

The ImageDetailTable specifies bindings to the SortableColumnHeader properties. It provides the current sort state of the column and binds the the toggleSort event for the column. In the example application three of the columns can be sorted.

1
2
3
<th class="sortable-column-header" [sortIndicator]="titleSortIndicator" (toggleSort)="sortByTitle()">Title</th>
<th class="sortable-column-header" [sortIndicator]="sizeSortIndicator" (toggleSort)="sortBySize()">Size (bytes)</th>
<th class="sortable-column-header" [sortIndicator]="dateSortIndicator" (toggleSort)="sortByDate()">Taken</th>

The ImageDetailTable then translates the toggle sort events into its own specific events.

1
2
3
@Output() public toggleTitleSort: EventEmitter<any> = new EventEmitter();
@Output() public toggleSizeSort: EventEmitter<any> = new EventEmitter();
@Output() public toggleDateSort: EventEmitter<any> = new EventEmitter();

These events ultimately get handled by the ImageDetailList component, the root component in the hierarchy of components. It knows more about the application state and what should actually happen when the user sorts a column. In this case it creates an action to submit to the Redux application store. (We’ll cover working with Redux and the application state in a future post.)

1
2
3
4
5
6
7
8
9
10
11
public sortByTitle() {
this.appStore.dispatch(sortImages(ImageSortBy.title, this.sortAscending(ImageSortBy.title)))
}
public sortBySize() {
this.appStore.dispatch(sortImages(ImageSortBy.size, this.sortAscending(ImageSortBy.size)))
}
public sortByDate() {
this.appStore.dispatch(sortImages(ImageSortBy.date, this.sortAscending(ImageSortBy.date)))
}

So, the application responds to the user initiated event, selecting a column to sort by, by bubbling up the event through the component tree, transforming the event as necessary, and ultimately having the parent component do something with it.

ImageDetailRow - A Table Row with Image Details

The ImageDetailRow component handles generating a row of image details within the table. The table uses *ngFor to generate the table row (the <tr> element). In the markup below for using this component, #rowData specifies a variable that can be used elsewhere. We then use this variable to pass the image data into the rowData input property on the ImageDetailRow component.

1
2
3
<tbody>
<tr class="image-detail-row" *ngFor="#rowData of tableData" [rowData]="rowData"></tr>
</tbody>

Markup in an table is a bit more constrained than other HTML markup, so we don’t want to use a custom HTML element for the detail row component. In this case we use a different selector, one that selects the image-detail-row CSS class. The template for the row then consists of the table data elements (<td>) within the row. It makes the assumption that the component would only be included within an HTML table row (a <tr> element).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component({
selector: '.image-detail-row',
directives: [RouterLink],
template: `
<td>
<a [routerLink]="imageRouteFor(rowData)">
<i class="fa fa-eye"></i> {{rowData.title}}
</a>
</td>
<td>{{rowData.size | number}}</td>
<td>{{rowData.dateTaken | date}}</td>
<td>{{rowData.width | number}} x {{rowData.height | number}}</td>
<td>{{rowData.portrait ? "Portrait" : ""}}{{rowData.landscape ? "Landscape" : ""}}</td>
<td>{{rowData.tags}}</td>
`
})

If you have experience with Angular1, then you’ll notice that the binding and filters that you used previously are still available in Angular2. However, filters have been enhanced and and now called pipes.

The [routerLink] property in the anchor tag above adds an application route, which we’ll also cover in a future post).

ImageTagSelector - Selecting Image Tags to Include

Finally, the ImageTagSelector component is used to select a subset of images to display based on tags that have been assigned to all of the images. This component deals with the application state directly rather than being a presentation component (though it certainly could have been implemented that way).

When thinking about how best to organize the components in your own application, there will always be trade-offs. Organize your components that make the best sense for the application and the team. I try to be pragmatic, since as developers we are trying to deliver business value.

This component subscribes to the application store and publishes actions to the store. Using Redux, however, tends to isolate your component and makes it easier to refactor later, if necessary.

A set of checkboxes is generated that includes the current list of tags associated with all of the images in the collection.

1
2
3
4
5
6
<label>
<input #tagInput type="checkbox"
[ngModel]="tag.isSelected"
(ngModelChange)="toggleSelectedTag(tag.tag, tagInput.checked)">
{{tag.tag}}
</label>

When checkboxes are toggled, a new list of excluded tags is generated and an action is created for the application store to change the excluded tags state.

1
2
3
4
5
6
7
8
9
public toggleSelectedTag(tag, isSelected) {
this.imageTags = this.imageTags.map(v => isMatchingTag(v.tag, tag) ? {tag, isSelected} : v)
this.onSelectionChanged()
}
private onSelectionChanged(): void {
let excludedTags = getExcludedTagsFromSelectedTagsList(this.imageTags);
this.appStore.dispatch(excludeImageTags(excludedTags))
}

Hopefully this gives you a sense of how components can be used within an Angular2 application.