diff --git a/07425d9b23c4ccbc64b20209dc7a7169/git-branching-commits-abstract-2.svg b/07425d9b23c4ccbc64b20209dc7a7169/git-branching-commits-abstract-2.svg new file mode 100644 index 000000000..a1803c3be --- /dev/null +++ b/07425d9b23c4ccbc64b20209dc7a7169/git-branching-commits-abstract-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/1502fcd5826f063bb070681a078c4fbb/git-branching-head-pointer.svg b/1502fcd5826f063bb070681a078c4fbb/git-branching-head-pointer.svg new file mode 100644 index 000000000..87930eaf2 --- /dev/null +++ b/1502fcd5826f063bb070681a078c4fbb/git-branching-head-pointer.svg @@ -0,0 +1,4 @@ + + + + diff --git a/1d466be0730556c40940e413ecf7b04f/snake_preview.gif b/1d466be0730556c40940e413ecf7b04f/snake_preview.gif new file mode 100644 index 000000000..5e2f6a7d7 Binary files /dev/null and b/1d466be0730556c40940e413ecf7b04f/snake_preview.gif differ diff --git a/2015-06-06-ng-messages-revisited/index.html b/2015-06-06-ng-messages-revisited/index.html new file mode 100644 index 000000000..7bf6f76b6 --- /dev/null +++ b/2015-06-06-ng-messages-revisited/index.html @@ -0,0 +1,50 @@ +ngMessages revisited | Articles by thoughtram

ngMessages revisited

If you’ve followed our series on Exploring Angular 1.3, you know that we’ve written an article about a new module that has been introduced then, called ngMessages. If you haven’t read that article we highly recommend checking it out, since this article builds on top of that.

+

Angular 1.4 has been released just a few days ago and weeks before that, there were several beta releases so we could make our feet wet with it. Next to a ton of improvements, bug fixes and features, this release also introduces a couple of minor breaking changes. In this article we discuss the latest changes to the ngMessages module, so you can update your application code accordingly.

+

ngMessagesInclude no longer an attribute

+

In applications where more than just a single form exists, we might want to reuse message templates. E.g. if we have an input field that is required, we want to display an error message that says so, in case the user forgets to enter anything. Such required fields appear very often in forms, so it would be quite cumbersome if we would have to define the same error message in a template over and over again.

+

To solve that issue, there was an ngMessagesInclude attribute, which we could use in combination with the ngMessages directive, to include existing templates in other ngMessages container.

+

To illustrate this scenario, here’s what such a template definition could look like:

+
<script type="script/ng-template" id="required-message">
+  <ng-message when="required">
+    This field is required!
+  </ng-message>
+</script>
+

It’s an overloaded script tag that has an id attribute so we can refer to it later, and in that script we can just define a template. In order to (re)use that template, all we had to do, was to use the ngMessagesInclude attribute like this:

+
<ng-messages ng-messages-include="required-message" for="otherForm.field.$error">
+  ...
+</ng-messages>
+

What happens here, is that we just have our ngMessages container to display messages, but in addition to that, an existing template will be automatically included and activated. Included templates have always been added to the bottom of the ngMessages container.

+

In Angular 1.4, this has changed. ngMessagesInclude is no longer an attribute but a directive. Which means, if we want to use it the same way, instead of adding an attribute to the ngMessages container, we have to add the ngMessagesInclude directive as a child element to the container like this:

+
<ng-messages for="otherForm.field.$error">
+  <div ng-message="minlength">...<div>
+  <div ng-messages-include="required-message"></div>
+</ng-messages>
+

Of course, this gives us much better control over what happens inside the ngMessages container. Don’t forget that the order of ngMessage directives inside a container configures the priority of each message.

+

Dynamic Message resolution

+

A better method to include existing message templates is already great, but the framework could do better. Even if ngMessages as a whole is a very nice and useful extension, it turns out that there was one issue with it, that should have been supported out of the box based on the nature of Angular.

+

It was not possible to pass expressions to ngMessage directives, that evaluate to any kind of error type. This restricted us to only define static templates for each error message, which not only means we had to type more, it’s also not possible to render error messages dynamically that come from a server. In addition to that, it was not possible to use directives on ngMessages that do structure changes to the DOM (e.g. ngIf, ngRepeat).

+

Angular 1.4 fixes that issue. It introduces another directive called ngMessageExp which gets an expression that evaluates to an error type, so we can dynamically display messages. Combined with directives that do structural changes to the DOM, this can be very powerful. Just imagine you’d get a list of error messages back from a server due to asynchronous validation. With the new added features, this can easily be implemented like this:

+
<ng-messages for="otherForm.field.$error">
+  <div ng-repeat="errorMessage in errorMessages">
+    <div ng-message-exp="errorMessage.type">
+      {{errorMessage.text}}
+    </div>
+  </div>
+</ng-messages>
+

We can simply iterate over a collection of messages using ngRepeat and dynamically display error messages based on the collection’s message objects. Super powerful.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/216c77e6de0df4be406a1548b5410462/git-branching-head-pointer-4.svg b/216c77e6de0df4be406a1548b5410462/git-branching-head-pointer-4.svg new file mode 100644 index 000000000..d47515ac4 --- /dev/null +++ b/216c77e6de0df4be406a1548b5410462/git-branching-head-pointer-4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/21790b11d643613a43a1039157bae9a3/git-detached-head-4.svg b/21790b11d643613a43a1039157bae9a3/git-detached-head-4.svg new file mode 100644 index 000000000..e280d5954 --- /dev/null +++ b/21790b11d643613a43a1039157bae9a3/git-detached-head-4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/2dd8e9b119adc196261798a139c683ab/injector-tree-2.svg b/2dd8e9b119adc196261798a139c683ab/injector-tree-2.svg new file mode 100644 index 000000000..719bb6b7f --- /dev/null +++ b/2dd8e9b119adc196261798a139c683ab/injector-tree-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/404.html b/404.html new file mode 100644 index 000000000..616f9bcc4 --- /dev/null +++ b/404.html @@ -0,0 +1,17 @@ +404: Not Found | Articles by thoughtram
\ No newline at end of file diff --git a/404/index.html b/404/index.html new file mode 100644 index 000000000..4fa57b620 --- /dev/null +++ b/404/index.html @@ -0,0 +1,17 @@ +404: Not Found | Articles by thoughtram
\ No newline at end of file diff --git a/44820d092a70af998a5c33b718f3969d/cd-tree-7.svg b/44820d092a70af998a5c33b718f3969d/cd-tree-7.svg new file mode 100644 index 000000000..c37ad0ce1 --- /dev/null +++ b/44820d092a70af998a5c33b718f3969d/cd-tree-7.svg @@ -0,0 +1,4 @@ + + + + diff --git a/477bd3a6ea642886f1669072185d43e3/transient-dependencies-4.svg b/477bd3a6ea642886f1669072185d43e3/transient-dependencies-4.svg new file mode 100644 index 000000000..a49367a3b --- /dev/null +++ b/477bd3a6ea642886f1669072185d43e3/transient-dependencies-4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/4b191284084e63c8b9e265569a426245/git-branching-head-pointer-3.svg b/4b191284084e63c8b9e265569a426245/git-branching-head-pointer-3.svg new file mode 100644 index 000000000..5bf1362a2 --- /dev/null +++ b/4b191284084e63c8b9e265569a426245/git-branching-head-pointer-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/6f49d0871e4dd635846506901215239e/upgrade-2-phases-3.svg b/6f49d0871e4dd635846506901215239e/upgrade-2-phases-3.svg new file mode 100644 index 000000000..ce1103958 --- /dev/null +++ b/6f49d0871e4dd635846506901215239e/upgrade-2-phases-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/70c21bbe865416334d97588bb4da9a5a/git-branching-new-branch.svg b/70c21bbe865416334d97588bb4da9a5a/git-branching-new-branch.svg new file mode 100644 index 000000000..5fbb98317 --- /dev/null +++ b/70c21bbe865416334d97588bb4da9a5a/git-branching-new-branch.svg @@ -0,0 +1,4 @@ + + + + diff --git a/7ac7580fb5b9791eca7c265ce8f60ab6/git-detached-head-2.svg b/7ac7580fb5b9791eca7c265ce8f60ab6/git-detached-head-2.svg new file mode 100644 index 000000000..7c12f5c3f --- /dev/null +++ b/7ac7580fb5b9791eca7c265ce8f60ab6/git-detached-head-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/7cd38f19f51a597d970346585e57a9c6/injector-tree-3.svg b/7cd38f19f51a597d970346585e57a9c6/injector-tree-3.svg new file mode 100644 index 000000000..1d02f5d50 --- /dev/null +++ b/7cd38f19f51a597d970346585e57a9c6/injector-tree-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/8393e4d5673c51a3779a96651966d72d/cd-tree-10.svg b/8393e4d5673c51a3779a96651966d72d/cd-tree-10.svg new file mode 100644 index 000000000..1c7f26476 --- /dev/null +++ b/8393e4d5673c51a3779a96651966d72d/cd-tree-10.svg @@ -0,0 +1,4 @@ + + + + diff --git a/8b38e48e2375ae32088cc955d25ef223/overlay_preview.gif b/8b38e48e2375ae32088cc955d25ef223/overlay_preview.gif new file mode 100644 index 000000000..6a2769cf0 Binary files /dev/null and b/8b38e48e2375ae32088cc955d25ef223/overlay_preview.gif differ diff --git a/98f70955c11277801562c3e63b068968/git-branching-head-pointer-2.svg b/98f70955c11277801562c3e63b068968/git-branching-head-pointer-2.svg new file mode 100644 index 000000000..e70a5c436 --- /dev/null +++ b/98f70955c11277801562c3e63b068968/git-branching-head-pointer-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/9a806b145b1b87ee22950bb562df02af/catch_5_state_flow.gif b/9a806b145b1b87ee22950bb562df02af/catch_5_state_flow.gif new file mode 100644 index 000000000..2f0153949 Binary files /dev/null and b/9a806b145b1b87ee22950bb562df02af/catch_5_state_flow.gif differ diff --git a/9e1929ff8a274bad3ef09aebd3610f07/animation_preview.gif b/9e1929ff8a274bad3ef09aebd3610f07/animation_preview.gif new file mode 100644 index 000000000..9e452bb7d Binary files /dev/null and b/9e1929ff8a274bad3ef09aebd3610f07/animation_preview.gif differ diff --git a/9f0fd18e6f0ea759062f1317f6f9d73f/git-detached-head-3.svg b/9f0fd18e6f0ea759062f1317f6f9d73f/git-detached-head-3.svg new file mode 100644 index 000000000..ebdcb786c --- /dev/null +++ b/9f0fd18e6f0ea759062f1317f6f9d73f/git-detached-head-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..d8010bade --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +blog.thoughtram.io diff --git a/a77dcff322d4e61495ce23c050433bf8/i18n-process.svg b/a77dcff322d4e61495ce23c050433bf8/i18n-process.svg new file mode 100644 index 000000000..47ee1accb --- /dev/null +++ b/a77dcff322d4e61495ce23c050433bf8/i18n-process.svg @@ -0,0 +1,4 @@ + + + + diff --git a/all/index.html b/all/index.html new file mode 100644 index 000000000..ebe355019 --- /dev/null +++ b/all/index.html @@ -0,0 +1,585 @@ +Articles by thoughtram | Articles by thoughtram

All Stories

  / Announcements

Farewell

  / Rust

Lifetimes in Rust

  / Rust

Iterators in Rust

  / Rust

References in Rust

  / Rust

String vs &str in Rust

  / Git

Announcing the REBASE ebook

  / Rust

A closer look at Ownership in Rust

  / Announcements

More GDE power at thoughtram

  / Announcements

Angular Master Class coming to Málaga

  / Angular

Dynamic Angular components inside custom widgets

  / Announcements

RxJS Master Class and courseware updates

  / Angular

Advanced caching with RxJS

  / Machine-learning

A simple guide to Reinforcement Learning

  / Announcements

Announcing Angular Master Class at Shopware

  / Announcements

Machine Learning Jump Start - Online Course

  / Angular

Custom Overlays with Angular's CDK - Part 2

  / Angular

Custom Overlays with Angular's CDK

  / Angular

Easy Dialogs with Angular Material

  / Rxjs

Taming snakes with reactive streams

  / Company

Company offsite in Spain 2017

  / Angular

A web animations deep dive with Angular

  / Announcements

Announcing Angular Master Class in Houston

  / Announcements

Announcing Angular Master Class in Berlin

  / Announcements

Join our free meetup in Denmark

  / Angular

Custom themes with Angular Material

  / Angular

Angular Master Class - Redux and ngrx

  / Announcements

Announcing Angular Master Class in Denmark

  / Angular

Three things you didn't know about the AsyncPipe

  / Angular

Using Zones in Angular for better performance

  / Announcements

Dominic joins thoughtram

  / Angular

Making your Angular apps fast

  / Announcements

Announcing Angular Master Class in Freiburg

  / Angular

A revamped Angular Master Class

  / Angular

Testing Angular Directives with Custom Matchers

  / Angular

Testing Services with Http in Angular

  / Machine-learning

Understanding XOR with Keras and TensorFlow

  / Announcements

Announcing Angular 2 Master Class in Sydney

  / Angular

Two-way Data Binding in Angular

  / Angular

Resolving route data in Angular

  / Machine-learning

Beginning Machine Learning with Keras and TensorFlow

  / Angular

Angular Animations - Foundation Concepts

  / Angular

Angular 2 is out - Get started here

  / Angular

Bypassing Providers in Angular

  / Announcements

Announcing Angular 2 Master Class in NYC

  / Rx

Exploring Rx Operators: flatMap

  / Angular

Custom Form Controls in Angular

  / Angular

Protecting Routes using Guards in Angular

  / Announcements

Updates and announcements

  / Angular

Reactive Forms in Angular

  / Angular

Cold vs Hot Observables

  / Angular

Routing in Angular revisited

  / Angular

Component-Relative Paths in Angular

  / Angular

How to prevent name collisions in Angular providers

  / Announcements

Thomas joins thoughtram

  / Angular

Exploring Rx Operators: map

  / Angular

Angular Providers using Map Literals

  / Announcements

Updates and announcements

  / Angularjs

Exploring Angular 1.5: Lifecycle Hooks

  / Angular

Template-driven Forms in Angular

  / Angular

Custom Validators in Angular

  / Angular

Angular Change Detection Explained

  / Announcements

Sponsoring AngularConnect. Again.

  / Angular

Zones in Angular

  / Angular

Understanding Zones

  / Angular

Taking advantage of Observables in Angular 2 - Part 2

  / Angular

Taking advantage of Observables in Angular

  / Announcements

Angular 2 Jump Start at NG-NL 2016

  / Announcements

Angular 2 Master Class: Jump Start

  / Angular

ngMessageFormat - Angular's unheard feature

  / Angular2

Multi Providers in Angular

  / Announcements

How we run trainings

  / Angular

Multiple Transclusion and named Slots

  / Announcements

Angular Master Class Extended: ngUpgrade

  / Angular

Upgrading Angular apps using ngUpgrade

  / Announcements

Pascal becomes a GDE

  / Angular

Understanding @Injectable in Angular

  / Announcements

Angular Training Day Bangkok

  / Angular

Forward references in Angular

  / Announcements

Going full-time

  / Angular

Host and Visibility in Angular's Dependency Injection

  / Angular

Angular Template Syntax Demystified - Part 1

  / Rust

A web app with Nickel: From first line to Heroku deployment

  / Angular

Service vs Factory - Once and for all

  / Angular

Even better ES5 code for Angular

  / Angular

View Encapsulation in Angular

  / Angular

Taking Angular Master Class to the next level

  / Angular

Styling Angular components

  / Annoucements

Anouncing Hanover's second Rust meetup

  / Angular

Routing in Angular

ngMessages revisited

  / Angular

Dependency Injection in Angular

  / Announcements

Sponsoring AngularConnect

  / Rust

Rust's Ownership model for JavaScript developers

  / Angular

Writing Angular code in ES5

  / Announcements

Speaking at code.talks

  / Angular

The difference between Annotations and Decorators

  / Announcements

Upcoming events in 2015

  / Angular

Developing a tabs component in Angular

  / Angular

Developing a zippy component in Angular

  / Angular

Angular and i18n - The new world

  / Angularjs

Joining betahaus education

  / Angularjs

Around the globe

  / Angularjs

Futuristic Routing in Angular

  / Git

Understanding branches in Git

  / Angularjs

Using ES2015 with Angular today

  / Angularjs

ngMessages in Angular 1.3

  / Angularjs

Go fast with $applyAsync in Angular 1.3

  / Angularjs

Validators Pipeline in Angular 1.3

  / Angularjs

Binding to Directive Controllers in Angular 1.3

  / Angularjs

Disabling Debug Info in Angular 1.3

  / Angularjs

ES6 Style Promises in Angular 1.3

  / Angularjs

Stateful filters in Angular 1.3

  / Git

The anatomy of a Git commit

  / Angularjs

Angular-hint in Angular 1.3

  / Announcements

Git Ninja Class in Amsterdam

  / Angularjs

ng-model-options in Angular 1.3

  / Angularjs

One-time bindings in Angular 1.3

  / Announcements

clog - A conventional changelog generator for the rest of us

  / Git

Going back in time to split older commits

  / Announcements

Git Ninja Class comes to Istanbul

  / Announcements

Tickets are on sale now!

  / Announcements

Organizing Hanovers first Rust meetup

  / Announcements

Announcing our first workshop

  / Announcements

We are thoughtram

\ No newline at end of file diff --git a/angular/2015/03/21/angular-and-i18n-the-new-world.html b/angular/2015/03/21/angular-and-i18n-the-new-world.html new file mode 100644 index 000000000..d435989e0 --- /dev/null +++ b/angular/2015/03/21/angular-and-i18n-the-new-world.html @@ -0,0 +1,139 @@ +Angular and i18n - The new world | Articles by thoughtram

Angular

Angular and i18n - The new world

We all know that internationalization is important when it comes to bigger apps, or just the ones that are simply used across countries. Angular itself comes with very poor i18n support, which is why the community has built their own solutions to extend the framework’s functionalities to their needs. However, there’s finally a first-class solution evolving that will be baked right into the core.

+

In fact, I was honoured to give a talk on that topic at this year’s ng-conf with Chirayu Krishnappa and you can watch the recording of it right here. This article is a detailed write-up based on the talk and I hope it will answer all the questions it raised.

+

Understanding the process of i18n

+

When I started working on angular-translate two years ago, i18n to me, was really just about making it possible that the user of an application is able to change the locale through the user interface. So what is needed? Well, we have our application, we replace all strings with an abstraction that takes care of displaying the actual messages of a certain locale later at runtime, tokenize all our existing messages and write them into JSON so we can easily add new messages - Done!

+

Even if this approach works quite well (that’s the one angular-translate uses and it’s used in a lot Angular apps), it turned out that it also comes with it’s pitfalls. In addition to that, there are even some things left out when it comes to the whole i18n process.

+

You wonder what these things are? Just ask yourself: Who defines the tokens for each message that needs to be translated? In which file format does your translator receive your message bundles? What if your translator needs context for your messages in order to translate them properly, how do you provide it? And once the translator translated all your messages, how do you get them back into your existing application?

+

Right, all of a sudden we realise, that there’s much more required than just translating messages from one locale to another. In order to make i18n as a process a bit more clear, here’s a graphic that visualises a good i18n solution that covers all the problems we’re facing with existing solutions.

+

i18n as a process visualised

+

Let’s go through this step by step:

+
    +
  • Developers write HTML templates - This should be super natural. In a good i18n solution, we as developers just write our HTML templates as usual, either manually or maybe dynamically generated by a server. In an Angular app those could be our routing views or templates for custom directives. Also important to notice: we should be able to write our templates in any language that we prefer, it shouldn’t be required to introduce tokens for every single message that needs to be translated. In fact, a simple HTML template that just contains plain text, is a valid template that can be translated.
  • +
  • Messages are extracted and bundled - In order to translate all of our application messages, we somehow need to extract them from our existing templates, bundle them together and hand them over to our translator in an expected file format (e.g. PO or XLIFF). The message extraction as well as the file format transformation should be done by automated tooling. We should be able to re-extract message any time as we’re developing new features in our application.
  • +
  • Translator translates messages - Once all messages are bundled as common translation file formats, the translator can take those files, import them in translation software of choice and use it’s graphical user interface to translate all messages to a different locale. The software then again, exports new translation files containing the new locale, which are handed over to us so we can process them.
  • +
  • Template/JSON generation - At this point, we need to decide in what way we want to implement i18n. We either want to generate new templates for each new locale so we can serve them accordingly from a web server, or we decide to use a client-side solution that e.g. consumes a certain JSON structure with the locale information and takes care of the locale change in the web browser. We might even need both solutions combined depending on our use case. However, whatever we decide to do, what we always need is tooling that allows us to do all these things.
  • +
+

As you can see, there’s really much more involved when it comes to i18n. What we also notice in this graphic, is that it’s an iterative process. No matter for which way we decide to get the new locale back into our application, we need to repeat that process over and over again as new features or changes happen in our application.

+

Okay cool, now we know what i18n is all about, but what is it with the new i18n solution that comes to the Angular core?

+

The new i18n story in Angular

+

Internationalization support in Angular has been very poor so far. You might know that there’s an ngLocale module you need to include, which is used by a couple components, like ngPluralize, date and currency filter to name a few, and that’s pretty much it. As we’ve already discussed, there’s so much more that comes into play when internationalizing an application, which is why there’s finally a new solution evolving that will bring first-class i18n support to the Angular framework.

+

Here’s a what the new solution will cover:

+
    +
  • Tooling - As we’ve learned earlier, there’s a lot of tooling required in order to implement a smooth i18n experience for both, developers and translators. We work on all the tools that are needed to realise that process, which includes message extraction, file transformation, template generation and more. We’ll also provide APIs so you can write your own plugins to extend the pipeline to your personal needs.
  • +
  • Plural and Gender select - Pluralization and gender selection is a rather isolated topic that can and has to be done whether you want to support your apps in other languages or not. With the new i18n solution we’ll extend the existing string interpolation and support ICU Messageformat inside curlies in any template.
  • +
  • HTML Annotations - In order to get high quality translations, we need to provide our translators with context, descriptions and meanings. There’ll be a new syntax to annotate existing templates with meta information that will be extracted together with all messages. This syntax is and will always be backwards compatible, in fact, we’ll be able to use it in any website or application without breaking anything.
  • +
  • Server and Client - The new i18n solution will work for both scenarios, generate templates on the server side and dynamic locale evaluation on the client side. This is a very powerful fact, since we don’t force anybody to use a certain strategy.
  • +
  • Pseudotranslation - Since the process of translating messages from one locale to another can take very long and only once we get those translations back we actually see and realise that our application UI is completely broken with the new locale, the new solution will support something called pseudotranslation. With pseudotranslation we can generate gibberish languages in order to test our application user interfaces as soon as possible.
  • +
+

Oh my, that’s a lot right? Yes indeed it is! But that just confirms that there’s a lot of processing and tooling involved when it comes to i18n. As we can see, the planned solution solves all the problems described above, which is awesome.

+

Let’s take a closer look at some of those topics to get an idea of how we plan to implement them.

+

HTML Annotations

+

One of the most important things when it comes to i18n, is to provide our translators context along the messages that get extracted from our templates. Imagine we have a simple template as the following one:

+
<p>Limit is 255 characters.</p>
+

There’s nothing special about this template. In fact, a simple HTML template that just contains plain text is content to be extracted by the message extraction tool. However, if this particular message gets extracted and handed to our translator, how does he or she ever know what this message is about? In the end, all the translator gets is this:

+
Limit is 255 characters.
+

What is the “Limit” we are talking about? Also, “characters” can have different meaning depending on the context of the message. In case this example doesn’t make it clear enough, just think about the word “crane”. Without context it could be the bird, or the machine, right?

+

In order to get high quality translations back, we need to provide enough information to the translator and this is where HTML Annotations come in. The message extraction tool knows about annotatoins via an i18n attribute which we can use to annotate existing templates with meta information.

+

Coming back to our example, what is the information we want to provide to the translator? Let’s say it’s a message to show the maximum length of characters allowed in a comment. With the i18n attribute we can add this information to our message:

+
<p i18n="Label to show the maximum length of characters
+   allowed in comments.">Limit is 255 characters.</p>
+

Now when the message gets extracted, the description is extracted as well which ends up at our translator’s software and would look something like this (Note that translator’s usually have a proper graphical user interface):

+
Limit is 255 characters.
+
+DESCRIPTION: Label to show the maximum length of 
+             characters allowed in comments.
+

Now it makes much more sense to our translator and he or she can actually reason about the given messages. The cool thing about the annotation is, that it doesn’t require a DOM element with an i18n attribute. There’s another comment based syntax annotation we can use if we don’t have that surrounding DOM element.

+
<!--i18n: Label to show the maximum length of
+          characters allowed in comments.-->
+Limit is 255 characters.
+<!--/i18n-->
+

Another use case were messages are extracted and meta information needed is when HTML attributes are used. Just take a look at this simple snippet:

+
<input placeholder="Firstname Lastname">
+

placeholder is a standard HTML attribute for input elements. For attributes like this, our new tools will provide our translator with a default description in case none is provided.

+
Firstname Lastname
+
+DESCRIPTION: HTML input control placeholder text
+

However, in case we do want to be more specific, all we need to do is to introduce a new i18n-[ATTRNAME] attribute that gets the description:

+
<input 
+  placeholder="Firstname Lastname"
+  i18n-placeholder="Text input placeholder for the full
+                    name of a friend.">
+

Yes, this works with all attributes! It doesn’t matter if you have a directive that introduces it’s own attribute APIs or even a Web Component, as long as you prefix your attribute with i18n- the message extractor will take care of extracting the message together with it’s description.

+

Placeholders

+

Even if what we’ve seen so far is pretty powerful, you might have noticed that we’ve only seen simple text messages that get extracted. But usually, in an Angular application, we have interpolation directives in our messages. Let’s say we replace the limit value in our example with something dynamic that is interpolated later at runtime.

+
<p i18n="Label to show the maximum length of characters
+   allowed in comments.">Limit is {{count}} characters.</p>
+

What will the translator see now? Well, it turns out that the Message Extraction tool will extract those messages as so called Placeholders. Our translator would see something like this:

+
Limit is EXPRESSION characters.
+
+DESCRIPTION: Label to show the maximum length of 
+             characters allowed in comments.
+
+PLACEHOLDERS
+
+- EXPRESSION
+    Description: Angular Expression
+

A placeholder cannot be changed by a translator. However, they are able to move them around inside the message in case it’s needed for the locale messages are translated to. We also notice that we get some meta information on what the placeholder EXPRESSION is about.

+

Of course, our translator probably doesn’t know about Angular at all and even EXPRESSION doesn’t really tell anything. In such cases we can explicitly define the name of a placeholder that ends up in a message and we’re even able to provide and example to give even more information.

+

Something like this would be more useful to the translator:

+
Limit is MAXCHARS characters.
+
+DESCRIPTION: Label to show the maximum length of 
+             characters allowed in comments.
+
+PLACEHOLDERS
+
+- MAXCHARS
+    Description: Angular Expression
+    Example: 255
+

The annotation syntax to override default placeholder names and add examples is still in discussion.

+

Pluralization and Gender

+

Using HTML annotations, we’re able to provide our translators with meta information about our messages that live in our templates. This technique is actually already everything we’d need to kick off the process of getting high quality translations.

+

However, there’s one topic that we haven’t talked about yet, which is Pruralization and Gender selection. Pluralization and Gender selection is rather decoupled from the actual i18n process, even if it can be part of it though. But we don’t have to internationalize our applications in order to implement plural and gender selection in the first place. The new i18n solution will come with a nicer and easier to use story for plural and gender, that works seamlessly with the rest of the process.

+

Wait, don’t we have ngPluralize for this? Yes we do and it will always be there. It turns out that there are some issues with ngPluralize though. For instance, how would you implement pluralization with that directive if you want to pass a pluralized message into an attribute? Right, it’s not possible. Also, with different locales, different plural categories might be needed and added by our translator, since they vary dramatically across languages.

+

With the new i18n solution, Angular’s interpolation syntax gets extended with the ICU Messageformat syntax. There are many Messageformat implementations for different programming languages and libraries out there (even angular-translate supports it) and in fact, it’s used by a lot people.

+

Alright, but how can we pluralize declaratively with that support? Well, take a look at the following snippet.

+
{{numMessages, plural,
+     =0 { You have no new messages }
+     =1 { You have one new message }
+  other { You have # new messages }
+}}
+

It’s that easy. If you’re familiar with Messageformat then you know this syntax already. We’re able to overload a simple Angular interpolation by defining the selection type (plural or gender) as second parameter and a “list” of categories to match the selection based on the given expression numMessages. The # symbol is a placeholder for value that the expression returns.

+

Why is that cool? We can use that syntax inside HTML attributes, we can use all interpolations inside selection messages (incl. filters), and even more important, our translators are likely to know this syntax. Because what ends up in their software, once extracted, is a message that looks something like this:

+
{numMessages, plural,
+     =0 { You have no new messages }
+     =1 { You have one new message }
+  other { You have # new messages }
+}
+

Yeap. It’s pretty much the same, except that they see single curlies instead of double curlies. Now, if a language has different categories than 0, 1 and other, maybe few, many and more, translators can easily at those and it gets especially easy if their software comes up with corresponding suggestions based on the locale.

+

Just to give you an idea what the gender selection syntax looks like, here’s an example.

+
{{friendGender, gender,
+    male { Invite him }
+  female { Invite her }
+   other { Invite them }
+}}
+

We can nest plural and gender messages as well, which makes this feature super powerful.

+

Angular core pre-compilation step

+

You might wonder how things like HTML annotations and pluralization and gender syntax are supported if we happen to choose the client side solution, right? In order to make all those nice features available to our Angular apps, without requiring a server, Angular performs a pre-compilation step which will manipulate the DOM before the actual compilation kicks in. This happens once initially so messages won’t be translated over and over again.

+

The pre-compilation also takes care of rewriting our plural and gender messages in order to make them work with the categories of a different locale. The same happens with all other messages that need to be translated as well, which means we can still just write our templates in one locale.

+

Okay, awesome! When can we use it?

+

This article detailed the overall picture of the new i18n solution that comes to the Angular core. While it’s planned to bring all mentioned features and tools to both versions of Angular, we need take one step at a time. Angular 1.4 will ship with support for Messageformat syntax inside Angular interpolations, so we can start using proper pluralization and gender selection throughout our applications right away.

+

Prototypes for tooling are already developed and production ready implementations are in the making, which will enable us to use and extract HTML annotations.

+

Work on support for Angular 2.0.0 is likely to start a few weeks after Angular 1.4 is out.

+

And of course, contributions are always welcome!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/03/27/building-a-zippy-component-in-angular-2.html b/angular/2015/03/27/building-a-zippy-component-in-angular-2.html new file mode 100644 index 000000000..cd1d48f48 --- /dev/null +++ b/angular/2015/03/27/building-a-zippy-component-in-angular-2.html @@ -0,0 +1,235 @@ +Developing a zippy component in Angular | Articles by thoughtram

Angular

Developing a zippy component in Angular

We are following the development of Angular 2.0.0 since the beginning on and are also contributing to the project. Just recently we’ve built a simple zippy component in Angular and in this article we want to show you how.

+

Getting started with Angular 2.0.0

+

There are several options today to get started with Angular. For instance, we can go to angular.io and use the quickstart guide. Or, we can install the Angular CLI, which takes care of scaffolding, building and serving Angular applications. In this article we will use Pawel Kozlowski’s ng2-play repository the Angular CLI, but again, you can use whatever suits you.

+

We start by installing Angular CLI as a global command on our local machine using npm.

+
$ npm install -g angular-cli
+

Once that is done, we can scaffold a new Angular project by running ng new <PROJECT_NAME>. Note that the project is scaffolded in the directory where we’re in at this moment.

+
$ ng new zippy-app
+

Next, we navigate into the project and run ng serve, which will essentially build and serve a hello world app on http://localhost:4200.

+
$ cd zippy-app
+$ ng serve
+

We open a browser tab on localhost://4200 and what we see is the text “zippy-app works!“. Cool, we’re all set up to build a zippy component in Angular!

+

Building the zippy component

+

Before we start building the zippy component with Angular, we need to clarify what we’re talking about when using the term “zippy”. It turns out that a lot of people think they don’t know what a zippy is, even if they do, just because of the naming.

+

Also known as “accordion”. You can click the summary text and the actual content toggles accordingly. If you take a look at this particular plunk, you’ll see that we actually don’t need to do any special implementation to get this working. We have the <details> element that does the job for us. But how can we implement such a thing in Angular?

+

We start off by adding a new file src/app/my-zippy.component.ts and creating a class in ES2015 that we export, so it can be imported by other consumers of this class, by using the ES2015 module system. If you’re not familiar with modules in ES2015 you might want to read our article on using ES2015 with Angular today.

+
+
+

Special Tip: We would normaly use Angular CLI to generate a component for us, instead of creating the files manually, but this articles focuses on understanding the building blocks of creating a custom component.

+
+
+
export class ZippyComponent {
+
+}
+

The next thing we want to do, is to make our ZippyComponent class an actual component and give it a template so that we can see that it is ready to be used. In order to tell Angular that this particular class is a component, we use something called “Decorators”.

+

Decorators are a way to add metadata to our existing code. Those decorators are actually not supported by ES2015 but have been developed as language extension of the TypeScript transpiler, which is used in this project. We’re not required to use decorators though. As mentioned, those are just transpiled to ES5 and then simply used by the framework. However, for simplicity sake we’ll use them in this article.

+

Angular provides us with a couple of decorators so we can express our code in a much more elegant way. In order to build a component, we need the @Component() decorator. Decorators can be imported just like classes or other symbols, by using ES2015 module syntax. If you heard about annotations in traceur before and wonder how they relate to decorators, you might want to read our article on the difference between annotations and decorators.

+
import { Component } from '@angular/core';
+
+export class ZippyComponent {
+
+}
+

The Component decorator adds information about what our component’s element name will be, what input properties it has and more. We can also add information about the component’s view and template.

+

We want our zippy component to be usable as <my-zippy> element. So all we need to do, is to add a @Component() decorator with that particular information. To specify the element name, or rather CSS selector, we need to add a selector property that matches a CSS selector.

+
import { Component } from '@angular/core';
+
+@Component({
+  selector: 'my-zippy'
+})
+export class ZippyComponent {
+
+}
+

Next, our component needs a template. We add information about the component’s view. templateUrl tells Angular where to load the component template from. To make templateUrl work with relative paths, we add another property moduleId with a value module.id. To get more information on moduleId, make sure to check out our article on Component-Relative Paths in Angular

+
import { Component } from '@angular/core';
+
+@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html'
+})
+export class ZippyComponent {
+
+}
+

Later at runtime, when Angular compiles this component, it’ll fetch my-zippy.component.html asynchronously. Let’s create a file src/app/my-zippy.component.html with the following contents:

+
<div class="zippy">
+  <div class="zippy__title">
+    &blacktriangledown; Details
+  </div>
+  <div class="zippy__content">
+    This is some content.
+  </div>
+</div>
+

CSS classes can be ignored for now. They just give us some semantics throughout our template.

+

Alright, believe it or not, that’s basically all we need to do to create a component. Let’s use our zippy component inside the application. In order to do that, we need to do things:

+
    +
  • Add our new component to the application module
  • +
  • Use ZippyComponent in ZippyAppComponent’s template
  • +
+

Angular comes with a module system that allows us to register directives, components, service and many other things in a single place, so we can use them throughout our application. If we take a look at the src/app/app.module.ts file, we see that Angular CLI already created a module for us. To register ZippyComponent on AppModule, we import it and add it to the list AppModule’s declarations:

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { ZippyAppComponent } from './zippy-app.component';
+import { ZippyComponent } from './my-zippy.component';
+
+@NgModule({
+  imports: [BrowserModule],
+  declarations: [ZippyAppComponent, ZippyComponent], // we're adding ZippyComponent here
+  bootstrap: [ZippyAppComponent]
+})
+export class AppModule {}
+

We don’t worry too much about the imports for now, but we acknowledge that Angular needs BrowserModule to make our app run in the browser. The declarations property defines all directives and pipes that are used in this module and bootstrap tells Angular, which component should be bootstrapped to run the application. ZippyAppComponent is our root component and has been generated by Angular CLI as well, ZippyComponent is our own custom component that we’ve just created.

+

Now, to actually render our zippy component in our application, we need to use it in ZippyAppComponent’s template. Let’s do that right away:

+
import { Component } from '@angular/core';
+
+@Component({
+  moduleId: module.id,
+  selector: 'zippy-app',
+  template: '<my-zippy></my-zippy>'
+})
+export class ZippyAppComponent {
+
+}
+

Nice! Running this in the browser gives us at least something that looks like a zippy component. The next step is to bring our component to life.

+

Bringing the component to life

+

In order to bring this component to life, let’s recap quickly what we need:

+
    +
  • Clicking on the zippy title should toggle the content
  • +
  • The title of the should be configurable from the outside world, currently hard-coded in the template
  • +
  • DOM that is used inside the <my-zippy> element should be projected in the zippy content
  • +
+

Let’s start with the first one: when clicking on the zippy title, the content should toggle. How do we implement that in Angular?

+

We know, in Angular 1.x, we’d probably add an ngClick directive to the title and set a scope property to true or false and toggle the zippy content respectively by using either ngHide or ngShow. We can do pretty much the same in Angular >= 2.x as well, just that we have a bit different semantics.

+

Instead of adding an ngClick directive (which we don’t have in Angular 2.x), to call for instance a method toggle(), we bind to the click event directly using the following template syntax.

+
...
+<div class="zippy__title" (click)="toggle()">
+  &blacktriangledown; Details
+</div>
+...
+

If you’re not familiar with this syntax I recommend you either reading this article on integrating Web Components with Angular, or this article about Angular’s template syntax demystified. Misko’s keynote from this year’s ng-conf is also a nice resource.

+

Now we’re basically listening on a click event and execute a statement. But where does toggle() come from? We can access component methods directly in our template. There’s no $scope service or controller that provides those methods. Which means, toggle() is just a method defined in ZippyComponent.

+

Here’s what the implementation of this method could look like:

+
export class ZippyComponent {
+  toggle() {
+    this.visible = !this.visible;
+  }
+}
+

We simply invert the value of the component’s visible property. In order to get a decent default state, we set visible to true when the component is loaded.

+
export class ZippyComponent {
+
+  visible = true;
+
+  toggle() {
+    this.visible = !this.visible;
+  }
+}
+

Now that we have a property that represents the visibility state of the content, we can use it in our template accordingly. Instead of ngHide or ngShow (which we also don’t have in Angular >= 2.x), we can simply bind the value of our visible property to our zippy content’s hidden property, which every DOM element has by default.

+
...
+<div class="zippy__content" [hidden]="!visible">
+  This is some content.
+</div>
+...
+

Again, what we see here is part of the new template syntax in Angular. Angular >= 2.x binds to properties rather than attributes in order to work with Web Components, and this is how you do it. We can now click on the zippy title and the content toggles!

+

Oh! The little arrow in the title still points down, even if the zippy is closed. We can fix that easily with Angular’s interpolation like this:

+
...
+<div class="zippy__title" (click)="toggle()">
+  {{ visible ? '&blacktriangledown;' : '&blacktriangleright;' }} Details
+</div>
+...
+

Okay, we’re almost there. Let’s make the zippy title configurable. We want that consumers of our component can define how they pass a title to it. Here’s what our consumer will be able to do:

+
<zippy title="Details"></zippy>
+<zippy [title]="'Details'"></zippy>
+<zippy [title]="evaluatesToTitle"></zippy>
+

In Angular >= 2.x, we don’t need to specify how scope properties are bound in our component, the consumer does. That means, this gets a lot easier in Angular too, because all we need to do is to import the @Input() decorator and teach our component about an input property, like this:

+
import { Component, Input } from '@angular/core';
+
+@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html'
+})
+export class ZippyComponent {
+  @Input() title;
+  ...
+}
+

Basically what we’re doing here, is telling Angular that the value of the title attribute is projected to the title property. Input data that flows into the component. If we want to map the title property to a different attribute name, we can do so by passing the attribute name to @Input():

+
@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html'
+})
+export class ZippyComponent {
+  @Input('zippyTitle') title;
+  ...
+}
+

But for simplicity’s sake, we stick with the shorthand syntax. There’s nothing more to do to make the title configurable, let’s update the template for ZippyAppComponent app.

+
@Component({
+  moduleId: module.id,
+  selector: 'zippy-app',
+  template: '<my-zippy title="Details"></zippy>',
+})
+...
+

Now we need to change the template of zippy to make title to appear at correct place, let’s udpate the template for zippy title.

+
...
+<div class="zippy__title" (click)="toggle()">
+  {{ visible ? '&blacktriangledown;' : '&blacktriangleright;' }} {{title}}
+</div>
+...
+

Insertion Points instead of Transclusion

+

Our component’s title is configurable. But what we really want to enable, is that a consumer can decide what goes into the component and what not, right?

+

We could for example use our component like this:

+
<my-zippy title="Details">
+  <p>Here's some detailed content.</p>
+</my-zippy>
+

In order to make this work, we’ve used transclusion in Angular 1. We don’t need transclusion anymore, since Angular 2.x makes use of Shadow DOM (Emulation) which is part of the Web Components specification. Shadow DOM comes with something called “Content Insertion Points” or “Content Projection”, which lets us specify, where DOM from the outside world is projected in the Shadow DOM or view of the component.

+

I know, it’s hard to believe, but all we need to do is adding a <ng-content> tag to our component template.

+
...
+<div class="zippy__content" [hidden]="!visible">
+  <ng-content></ng-content>
+</div>
+...
+

Angular uses Shadow DOM (Emulation) since 2.x by default, so we can just take advantage of that technology. It turns out that insertion points in Shadow DOM are even more powerful than transclusion in Angular. Angular 1.5 introduces multiple transclusion slots, so we can explicitly “pick” which DOM is going to be projected into our directive’s template. The <ng-content> tag lets us define which DOM elements are projected too. If you want to learn more about Shadow DOM, I recommend the articles on html5rocks.com or watch this talk from ng-europe.

+

Putting it all together

+

Yay, this is how we build a zippy component in Angular. Just to make sure we’re on the same page, here’s the complete zippy component code we’ve written throughout this article:

+
import { Component, Input } from '@angular/core';
+
+@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html'
+})
+export class ZippyComponent {
+
+  @Input() title;
+  visible = true;
+
+  toggle() {
+    this.visible = !this.visible;
+  }
+}
+

And here’s the template:

+
<div class="zippy">
+  <div (click)="toggle()" class="zippy__title">
+    {{ visible ? '&blacktriangledown;' : '&blacktriangleright;' }} {{title}}
+  </div>
+  <div [hidden]="!visible" class="zippy__content">
+    <ng-content></ng-content>
+  </div>
+</div>
+

I’ve set up a repository so you can play with the code here. In fact, I’ve also added this component to the Angular project. The pull request is pending merged here and likely to be merged the next few days. At this point I’d like to say thank you to Victor and Misko for helping me out on getting this implemented.

+

You might notice that it also comes with e2e tests. The component itself even emits it’s own events using EventEmitter, which we haven’t covered in this article. Check out the demos to see event emitters in action!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/04/09/developing-a-tabs-component-in-angular-2.html b/angular/2015/04/09/developing-a-tabs-component-in-angular-2.html new file mode 100644 index 000000000..95d094752 --- /dev/null +++ b/angular/2015/04/09/developing-a-tabs-component-in-angular-2.html @@ -0,0 +1,244 @@ +Developing a tabs component in Angular | Articles by thoughtram

Angular

Developing a tabs component in Angular

Just recently, we wrote about how to build a zippy component in Angular. We explored how to get started with the framework and learned about some concepts that it comes with to build a very simple component. If you haven’t read the article, you might want to check it out.

+

As a follow up, we now want to build yet another component that is widely used in a lot of applications: Tabs. Building tabs has always been the de facto example when it comes to explaining directive controllers in Angular. Angular >= 2.x does not have the concept of directive controllers, because the component itself is the execution context. It also makes it much easier to access other directives and components through dependency injection. However, you do want to use directive controllers in Angular 1.x in order to make the migration process to Angular >= 2.x easier.

+

Let’s start right away and learn how easy it is to build a tabs component in Angular without the confusing relationship between directive link functions and controllers. We’ll skip the installation part, since that was explored in the other article.

+

What it should look like

+

Before we start implementing the actual component, let’s first clarify what we want to achieve from a consumer point of view. Building tabs with web technologies usually ends up with having a HTML list, that represents the tabs, and container elements per each tab that display the content of a tab.

+

Of course, in Angular, those implementation details are hidden behind some nice readable and declarative elements that we all know as directives. Having a tool like Angular (and also Web Components) allows us to create custom elements, so that a consumer could use something like the following snippet to add tabs to an application:

+
<tabs>
+  <tab tabTitle="Tab 1">
+    Here's some content.
+  </tab>
+  <tab tabTitle="Tab 2">
+    And here's more in another tab.
+  </tab>
+</tabs>
+

We have a <tab> element that simply represents a single tab which has a title, and we have a <tabs> element that takes care of making those <tab> elements actually “tabbable”.

+

If you’ve been following the development and concepts of Angular, you probably learned that, since Angular 2.x, the consumer of a component is in charge of deciding how a value is passed to a component. Whereas in Angular 1.x, the directive defines how a value is bound to it’s scope, so the consumer needs to know about the inner workings of a directive.

+

This means, talking about the tabTitle attribute that we have in the code above, consumers can either write to the component attribute (if it exists), or to the component property. The latter would allow the consumer to pass expressions to the component that first get evaluated. Here’s what it could look like:

+
<tab tabTitle="This is just a String">
+  ...
+</tab>
+<tab [tabTitle]="thisIsAnExpression">
+  ...
+</tab>
+

Alright, now that we know what we want to build, let’s get our hands dirty with some Angular code.

+

Building the components

+

We start off by implementing a rather static version of the <tabs> element. If you’ve read our article on building a zippy component in Angular, +you know that we need the @Component() decorator to tell Angular what the selector and template for our component should be.

+
@Component({
+  selector: 'tabs',
+  template: `
+    <ul>
+      <li>Tab 1</li>
+      <li>Tab 2</li>
+    </ul>
+  `
+})
+export class Tabs {
+
+}
+

When using the Component decorator, we can specify the template using the template property. The back tick syntax comes with ES2015 and allows us to do multi-line string definition without using concatenation operators like +.

+

As you can see, the component template already comes with a static list of tabs. This list will be replaced with a dynamic directive later, for now we keep it like this so we get a better picture of the direction we’re going. We also need a place where the tab contents will go. Let’s add an insertion point to our template. This will project the outer light DOM into the Shadow DOM (Emulation).

+
@Component({
+  selector: 'tabs',
+  template: `
+    <ul>
+      <li>Tab 1</li>
+      <li>Tab 2</li>
+    </ul>
+    <ng-content></ng-content>
+  `
+})
+

Cool, we can already start using our <tabs> component and write HTML into it like this:

+
<tabs>
+  <p>Some random HTML with some random content</p>
+</tabs>
+

Of course, we do want to use <tab> elements inside our <tabs> component, so let’s build that one. It turns out that the <tab> element is actually quite primitive. It’s basically just a container element that has an insertion point to project light DOM. We shouldn’t forget the configurable tabTitle. Here’s how we do it.

+
@Component({
+  selector: 'tab',
+  template: `
+    <div>
+      <ng-content></ng-content>
+    </div>
+  `
+})
+export class Tab {
+  @Input() tabTitle;
+}
+

The element should be named <tab> so we set the selector property accordingly. We bind the tabTitle input to the component’s tabTitle property. Last but not least we add a template that is just a div with an insertion point.

+

Wait, that’s it? Well, sort of. There’s a tiny bit more we need to do, but let’s just use our new <tab> component in our <tabs> component.

+
<tabs>
+  <tab tabTitle="Foo">
+    Content of tab Foo
+  </tab>
+  <tab tabTitle="Bar">
+    Content of tab Bar
+  </tab>
+</tabs>
+

Executing this in the browser, we notice that we still get a list with Tab 1 and Tab 2 and in addition, we see the projected contents of both tabs at the same time. That’s not quite a tabs component, right?

+

Making the components dynamic

+

We’re getting there. Let’s first make our <tabs> component to actually use the correct titles instead of a hard-coded list. In order to generate a dynamic list, we need a collection. We can use our component’s constructor to initialize a collection that holds all tabs like this:

+
export class Tabs {
+
+  // typescript needs to know what properties will exist on class instances
+  tabs: Tab[] = [];
+}
+

Okay cool, but how do we get our tab titles into that collection? This is where, in Angular 1, directive controllers come in. However, since Angular 2.x it’s much easier. First we need an interface so that the outside world can actually add items to our internal collection. Let’s add a method addTab(tab: Tab), that takes a Tab object and does exactly what we need.

+
export class Tabs {
+  ...
+  addTab(tab:Tab) {
+    this.tabs.push(tab);
+  }
+}
+

The method just simply pushes the given object into our collection and we’re good. Next we update the template so that the list is generated dynamically based on our collection. In Angular 1 we have a ngRepeat directive that lets us iterate over a collection to repeat DOM. Since Angular 2.x there a a ngFor directive that pretty much solves the exact same problem. We use the directive to iterate over our tabs collection to generate a dynamic list of tab titles in the component’s template.

+
@Component({
+  ...
+  template: `
+    <ul>
+      <li *ngFor="let tab of tabs">{{ tab.tabTitle }}</li>
+    </ul>
+  `
+})
+

If the templating syntax doesn’t make sense to you at all, you might want to check out this design doc.

+

Alright, we have a collection, we have an API to extend the collection and we have a list in our template that is generated dynamically based on that collection. Since the collection is empty by default, the generated list is empty and no tab title is shown. Somebody needs to call this addTab() method!

+

That’s where the <tab> component comes into play (again). Inside the component we can simply ask for a parent Tabs dependency by using the new, much more powerful dependency injection system and get an instance of it. This allows us to simply use given APIs inside a child component. Let’s take a look at what that looks like.

+
class Tab {
+  constructor(tabs: Tabs) {
+    tabs.addTab(this)
+  }
+}
+

Wait, what happens here? tabs: Tabs is just Typescript type annotation which Angular uses for Dependency Injection. +Please check out our article where we go deeper inside Angular DI

+

tl;dr

+
+

Angular Hierarchical Injector knows, that we want first Tabs instance that it can get, +when traversing upwards from current host. In our case the actual host is our <tab> component. +Injector ask on tab for Tabs, if there is none, Injector will ask Parent Injector for Tabs. In our case parent Injector is on <tabs> component +and it has indeed Tabs instance, so it will return the correct instance of Tabs.

+
+

For now it’s just important to understand that this particular type annotation gives you access to a parent component dependency, which in our case is <tabs>. +Using that instance, we can simply call the addTab() method with a Tab object and we are good to go.

+

Making it tabbable

+

Using the components as they are right now, we do get a tabs list generated by our tab titles, but we still see all the contents of each tab at the same time. What we want, is to make that component actually “tabbable”, so that only one tab content is shown. How do we achieve that?

+

Well, first we need a property that activates or deactivates a tab and depending on that value, we either show or hide it. We can simply extend our <tab> template accordingly like this:

+
@Component({
+  template: `
+    <div [hidden]="!active">
+      <ng-content></ng-content>
+    </div>
+  `
+})
+class Tab { ... }
+

If a tab is not active, we simply hide it. We haven’t specified this property anywhere, which means it’s undefined which evaluates to false in that condition. So every tab is deactivated by default. In order to have at least one tab active, we can extend the addTab() method accordingly. The following code for instance, activates the very first tab that is added to the collection.

+
export class Tabs {
+  ...
+  addTab(tab:Tab) {
+    if (this.tabs.length === 0) {
+      tab.active = true;
+    }
+    this.tabs.push(tab);
+  }
+}
+

Awesome! The only thing that is missing, is to activate other tabs when a user clicks on a tab. In order to make this possible, we just need a function that sets the activate property when a tab is clicked. Here’s what such a function could look like.

+
export class Tabs {
+  ...
+  selectTab(tab:Tab) {
+    this.tabs.forEach((tab) => {
+      tab.active = false;
+    });
+    tab.active = true
+  }
+}
+```js
+
+We simply iterate over all tabs we have, deactivate them, and activate the one that is passed to that function. Then we just add this function to the template, so it is executed whenever a user clicks a tab, like this.
+
+```js
+@Component({
+  ...
+  template: `
+    <ul>
+      <li *ngFor="let tab of tabs" (click)="selectTab(tab)">
+        {{tab.tabTitle}}
+      </li>
+    </ul>
+  `
+})
+

Again, if this template syntax is new to you, check out the mentioned design document. What happens here is, whenever a click event is fired selectTab() is executed with the iterator tab instance. Try it out!

+

Here’s the complete source in case you ran into any problems.

+
@Component({
+  selector: 'tabs',
+  template: `
+    <ul>
+      <li *ngFor="let tab of tabs" (click)="selectTab(tab)">
+        {{tab.tabTitle}}
+      </li>
+    </ul>
+    <ng-content></ng-content>
+  `,
+})
+export class Tabs {
+  tabs: Tab[] = [];
+
+  selectTab(tab: Tab) {
+    this.tabs.forEach((tab) => {
+      tab.active = false;
+    });
+    tab.active = true;
+  }
+
+  addTab(tab: Tab) {
+    if (this.tabs.length === 0) {
+      tab.active = true;
+    }
+    this.tabs.push(tab);
+  }
+}
+
+@Component({
+  selector: 'tab',
+  template: `
+    <div [hidden]="!active">
+      <ng-content></ng-content>
+    </div>
+  `
+})
+export class Tab {
+
+  @Input() tabTitle: string;
+
+  constructor(tabs:Tabs) {
+    tabs.addTab(this);
+  }
+}
+

And we shouldn’t forget that these components need to be declared on our application module:

+
@NgModule({
+  imports: [BrowserModule],
+  declarations: [Tab, Tabs, AppComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

Where to go from here

+

This is a very rudimentary implementation of a tabs component. We can use that as a starting point to make it better over time. For example, we haven’t done anything in terms of accessibility. It would also be nice if the component emits some custom events when a tab is activated. We’ll cover working with events in Angular in another article.

+

Bonus

+

Angular is so awesome that there is not just one way how to do things!

+

We can take a totally different approach how to implement our simple tabs ( which isn’t so easily possible in Angular 1.x ), +leveraging special Angular @ContentChildren property decorator with QueryList type and AfterContentInit life cycle hook. +Those are more advanced concepts, which are covered in more details by Juri Strumpflohner in his follow-up article.

+

If you’re just curious what it looks like, check out the demos below!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/05/03/the-difference-between-annotations-and-decorators.html b/angular/2015/05/03/the-difference-between-annotations-and-decorators.html new file mode 100644 index 000000000..ecabe0361 --- /dev/null +++ b/angular/2015/05/03/the-difference-between-annotations-and-decorators.html @@ -0,0 +1,105 @@ +The difference between Annotations and Decorators | Articles by thoughtram

Angular

The difference between Annotations and Decorators

Last year, the Angular team announced it’s ECMAScript language extension AtScript, which adds types and annotations to the language in order to enable better tooling, debugging and overall development experience. Half a year later at ng-conf, the team announced that AtScript becomes TypeScript, which supports annotations and another feature called “decorators”.

+

But how do those annotations actually work? And what are decorators then? This article details the translation of annotations and how they differ from decorators.

+

Annotations

+

Let’s start off with annotations. As mentioned, the Angular team announced AtScript as their language extension to JavaScript. AtScript comes with features like Type Annotations, Field Annotations and MetaData Annotations. We’re going to focus on metadata annotations. Let’s take a look at the following Angular component to get an idea of what metadata annotations can look like:

+
@Component({
+  selector: 'tabs',
+  template: `
+    <ul>
+      <li>Tab 1</li>
+      <li>Tab 2</li>
+    </ul>
+  `
+})
+export class Tabs {
+
+}
+

We have a class Tabs that is basically empty. The class has one annotation @Component. If we’d remove all annotations, what would be left is just an empty class that doesn’t have any special meaning right? So it seems that @Component add some metadata to the class in order to give it a specific meaning. This is what annotations are all about. They are a declarative way to add metadata to code.

+

@Component is an annotation that tells Angular, that the class, which the annotation is attached to, is a component.

+

Okay, even if that seems to be quite clear, there are a few questions coming up:

+
    +
  • Where do those annotations come from? This is nothing that JavaScript gives us out of the box right?
  • +
  • Who defined this annotations called @Component?
  • +
  • If this is part of AtScript, what does that translate to, so we can use it in today’s browsers?
  • +
+

Let’s answer these one by one. Where do those annotations come from? To answer that question, we need to complete the code sample. @Component is something we need to import from the Angular framework like this:

+
import { 
+  ComponentMetadata as Component,
+} from '@angular/core';
+

This pretty much answers our first question. Both annotations are provided by the framework. Let’s take a look at what the implementation of those annotations look like:

+
export class ComponentMetadata extends DirectiveMetadata {
+
+  constructor() {
+    ...
+  }
+}
+

We can see that ComponentMetadata is in fact an implementation detail of the Angular framework. This answers our second question.

+

But wait. It’s just yet another class? How can just a simple class change the way how other classes behave? And why are we able to use those classes as annotations by just prefixing them with an @ sign? Well, actually we can’t. Annotations are not available in browser’s of today, which means we need to transpile it to something that does run in current browsers.

+

Even though we have a couple of transpilers we can choose from. Babel, Traceur, TypeScript, … It turns out there’s only one that actually implements annotations as we know them from AtScript: Traceur. Taking the component code from above, this is what it translates to using Traceur:

+
var Tabs = (function () {
+  function Tabs() {}
+
+  Tabs.annotations = [
+    new ComponentMetadata({...}),
+  ];
+
+  return Tabs;
+})
+

In the end, a class is just a function, which is also just an object, and all annotations end up as instance calls on the annotations property of the class. When I said “all” annotations end up there, I actually lied a bit. We can have parameter annotations as well and whose will be assigned to a class’ parameters property. So if we have code like this:

+
class MyClass {
+
+  constructor(@Annotation() foo) {
+    ...
+  }
+}
+

This would translate to something like this:

+
var MyClass = (function () {
+  function MyClass() {}
+
+  MyClass.parameters = [[new Annotation()]];
+
+  return MyClass;
+})
+

The reason why this translate to a nested array, is because a parameter can have more than one annotation.

+

Okay, so now we know what those metadata annotations are and what they translate to, but we still don’t know how something like @Component makes a normal class actually a component in Angular. It turns out that Angular itself takes care of that. Annotations are really just metadata added to code. That’s why @Component is a very specific implementation detail of Angular. In fact, there are a couple of other annotations that the framework comes with. But also only the framework knows what to do with that information.

+

Another very interesting learning is that Angular expects the metadata on annotations and parameters properties of classes. If Traceur would not translate them to those particular properties, Angular wouldn’t know from where to get the metadata. Which makes AtScript Annotations just a very specific implementation of what annotations could actually be.

+

Wouldn’t it be nicer if you as a consumer could decide where your metadata is attached to in your code? Yes! And this is where decorators come into play.

+

Decorators

+

Decorators are a proposed standard for ECMAScript 2016 by Yehuda Katz, to annotate and modify classes and properties at design time. This sounds pretty much like what annotations do right? Well… sort of. Let’s take a look at what a decorator looks like:

+
// A simple decorator
+@decoratorExpression
+class MyClass { }
+

Wait. This looks exactly like an AtScript annotation! That’s right. But it isn’t. From a consumer perspective, a decorator indeed looks like the thing that we know as “AtScript Annotation”. There is a significant difference though. We are in charge of what our decorator does to our code. Taking the code above, a corresponding decorator implementation for @decoratorExpression could look like this:

+
function decoratorExpression(target) {
+   // Add a property on target
+   target.annotated = true;
+}
+

Right. A decorator is just a function that gives you access to the target that needs to be decorated. Get the idea? Instead of having a transpiler that decides where your annotations go, we are in charge of defining what a specific decoration/annotation does.

+

This, of course, also enables us to implement a decorator that adds metadata to our code the same way AtScript annotations do (I keep referring to “AtScript annotations” because what they do, is really an AtScript specific thing). Or in other words: with decorators, we can build annotations.

+

There’s a lot more to explore about decorators, but that is out of the scope of this article. I recommend checking out Yehuda’s proposal to learn more about the feature.

+

Does TypeScript support Annotations or Decorators?

+

As you might know, the Angular team announced earlier this year that they’re going to drop the term “AtScript” in favour of TypeScript, since both languages seem to solve the same problems. In addition, there were announcements that TypeScript will support annotations and decorators once version 1.5 alpha is out.

+

It turns out that it actually doesn’t. TypeScript supports decorators, but doesn’t know about Angular specific annotations. Which makes sense, because they are an implementation detail of Angular. That also means that either we as consumers, or the framework needs to provide those decorators in order to make the code compile. Only the latter really makes sense. Luckily, generators for both, annotation and parameter decorators, have landed in the Angular code base lately. So what the framework behind the scenes does is, it comes with metadata annotation implementations, which are then passed to the decorator generator to make decorators out of them. That’s also why we have to write the following code when transpiling with traceur:

+
import {
+  ComponentMetadata as Component
+} from '@angular/core';
+

As we can see, we’re actually importing the metadata rather than the decorator. This is simply just because traceur doesn’t understand decorators, but does understand @Component annotations. Which is why we’re also importing them with these namespaces respectively.

+

Conclusion

+

“AtScript Annotations” and decorators are nearly the same thing. From a consumer perspective we have exactly the same syntax. The only thing that differs is that we don’t have control over how AtScript annotations are added as metadata to our code. Whereas decorators are rather an interface to build something that ends up as annotation. Over a long term, however, we can just focus on decorators, since those are a real proposed standard. AtScript is deprecated, and TypeScript implements decorators.

+

I hope this article made some things clear though.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/05/09/writing-angular-2-code-in-es5.html b/angular/2015/05/09/writing-angular-2-code-in-es5.html new file mode 100644 index 000000000..cfe476308 --- /dev/null +++ b/angular/2015/05/09/writing-angular-2-code-in-es5.html @@ -0,0 +1,100 @@ +Writing Angular code in ES5 | Articles by thoughtram

Angular

Writing Angular code in ES5

It’s no news anymore that Angular 2.x was written in TypeScript in order to take advantage of language features like types and meta data annotations through decorators. Taking a first look at Angular examples that are written in TypeScript, can feel a bit unfamiliar and unclear to developers that don’t have experience with that language. Even constructs like classes that ECMAScript 2015 brings to the table can be scary enough to keep developers from learning Angular.

+

That’s why developers with more experience will tell us that we don’t have to write TypeScript or just ES6 if we don’t want to. We can just stick with ES5. Cool, fine. But how do we do that? In one of our last articles we’ve explored the difference between annotations and decorators and to what they translate to in ES5.

+

In this article, we will use that information, to actually write Angular code in ES5 with the latest version released at the time of writing (2.x).

+

Getting started with Angular in ES5

+

If you’ve read our article on building a zippy component in Angular, you might know that nowadays, there’s quite a bit of work to do, in order to get started if you want to write your application in ES6/TypeScript and take advantage of it’s module system.

+

In ES5 we don’t have a module system yet. So ideally, we should be able to just take a JavaScript file from somewhere, that has all the Angular code in it, so we can embed it in our website. We don’t have to care about transpiling, concatenating, deciding on a module system (AMD, Common, System, …), or anything else. We can just fetch a bundled file that comes with the ready-to-use code.

+

The easiest way to get hold of Angular ES5 bundles is npmcdn. Here’s what we need to embed to get started with ES5 and Angular:

+
<script src="https://npmcdn.com/@angular/core@2.0.0-rc.5/bundles/core.umd.js"></script>
+<script src="https://npmcdn.com/@angular/common@2.0.0-rc.5/bundles/common.umd.js"></script>
+<script src="https://npmcdn.com/@angular/compiler@2.0.0-rc.5/bundles/compiler.umd.js"></script>
+<script src="https://npmcdn.com/@angular/platform-browser@2.0.0-rc.5/bundles/platform-browser.umd.js"></script>
+<script src="https://npmcdn.com/@angular/platform-browser-dynamic@2.0.0-rc.5/bundles/platform-browser-dynamic.umd.js"></script>
+

Whereas @2.0.0-rc.5 is the version number we specify. So that part might change depending on what we want to do.

+

Now the next question comes up: How can we access and use given annotations and/or decorators? Usually, in ES2015, we would import them from the framework but now there’s no way for us to import them.

+

Well, it turns out that the bundled version exposes an ng.core object on the current global scope or reuses an existing one, which has all annotations added to it. In our last article we’ve learned that annotations are just classes, which in the end are just functions. And those functions are called as constructor functions to add meta data to our components. That means, all we have to do is to call those annotation constructors manually and assign them to our component’s annotations property.

+

Let’s start off with a simple component that has a template:

+
var HelloComponent = function () {
+
+};
+
+HelloComponent.annotations = [
+  new ng.core.Component({
+    selector: 'hello-cmp',
+    template: 'Hello World!'
+  })
+];
+

That’s it. We have a constructor function that has a component annotation and a view annotation. The TypeScript equivalent would look something like this:

+
@Component({
+  selector: 'hello-cmp',
+  template: 'Hello World!'
+})
+class HelloComponent {
+
+}
+

Bootstrapping an Angular app in ES5

+

When we come to the point that we want to bootstrap our application, we need to define an NgModule that has everything attached to it that is needed to make our app run, and bootstrap it on a dedicated platform (e.g. browser, webworker or server).

+

Let’s go ahead and create such a module first. ng.core.NgModule can be used to create the needed metadata on a constructor function. Just like with our HelloComponent, we create an AppModule function like this:

+
var AppModule = function () {
+
+};
+

Next, we add NgModule annotations to it:

+
AppModule.annotations = [
+  new ng.core.NgModule({
+    imports: [ng.platformBrowser.BrowserModule],
+    declarations: [HelloComponent],
+    bootstrap: [HelloComponent]
+  })
+];
+

Similar to TypeScript world, we have to import the BrowserModule from the ng.platformBrowser package, so we can bootstrap our module in the browser environment. Next, we declare all directives and components that are used inside this module, which in our case, is only the HelloComponent. Last but not least, we tell Angular which component to bootstrap when the module is bootstrapped.

+

We need to make sure that all of the DOM is loaded before we bootstrap our module. Adding an event listener for the DOMContentLoaded event and call bootstrap once triggered, will help here. When the event is fired, we can call platformBrowserDynamic().bootstrapModule(AppModule) to bootstrap our app:

+
document.addEventListener('DOMContentLoaded', function () {
+  ng.platformBrowserDynamic
+    .platformBrowserDynamic().bootstrapModule(AppModule);
+});
+

And of course, the corresponding application template looks like this:

+
<body>
+  <hello-component></hello-component>
+</body>
+

Great we’ve just bootstrapped our Angular application written in ES5! Was it that hard?

+

Injecting services in ES5

+

Let’s say we want to add a GreetingService to our component. The @Component annotation takes a property viewProviders to define injectable types for this particular component. This is easy to add. First we build the service. A service in Angular can be just a class, which translates to just a function, which is also just an object in JavaScript.

+
var GreetingService = function () {}
+
+GreetingService.prototype.greeting = function () {
+  return 'Hello';
+};
+

Next we tell our component about it’s injectable types:

+
HelloComponent.annotations = [
+  new ng.Component({
+    selector: 'hello-cmp',
+    providers: [GreetingService]
+  }),
+  ...
+];
+

This basically tells our component that it should return an instance of GreetingService when somebody asks for GreetingService. Nobody asked for it yet, so let’s change that. First we need something we want to inject into our component’s constructor:

+
var HelloComponent = function (greetingService) {
+  this.greeting = greetingService.greeting();
+};
+

To make our component explicitly ask for something that is a GreetingService, or in other words, to tell the injector that our greetingService parameter should be an instance of GreetingService, we need to add a parameter annotation accordingly:

+
HelloComponent.parameters = [[new ng.core.Inject(GreetingService)]];
+

If you wonder why we define a nested array, this is because one constructor parameter can have more than one associated annotation.

+

Cool, so it turns out that writing Angular code is actually not weird at all. In addition to that, it kind of gets clear that writing Angular code in ES5 requires more typing. But again, in the end it’s up to the application author which language or transpiler to use.

+

There’s even a better syntax, that makes writing and reading Angular code a breeze.

+

Check out the demos below!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/05/18/dependency-injection-in-angular-2.html b/angular/2015/05/18/dependency-injection-in-angular-2.html new file mode 100644 index 000000000..3babbc97d --- /dev/null +++ b/angular/2015/05/18/dependency-injection-in-angular-2.html @@ -0,0 +1,298 @@ +Dependency Injection in Angular | Articles by thoughtram

Angular

Dependency Injection in Angular

Dependency injection has always been one of Angular’s biggest features and selling points. It allows us to inject dependencies in different components across our applications, without needing to know, how those dependencies are created, or what dependencies they need themselves. However, it turns out that the current dependency injection system in Angular 1.x has some problems that need to be solved in Angular 2.x, in order to build the next generation framework. In this article, we’re going to explore the new dependency injection system for future generations.

+

Before we jump right into the new stuff, lets first understand what dependency injection is, and what the problems with the DI in Angular 1 are.

+
+

TLDR;

+

An injector creates dependencies using providers. Providers are recipes that know how to create dependencies. Type annotations in TypeScript can be used to ask for dependencies and Every component has its own injector, resulting in an injector tree. The injector tree enables transient dependencies.

+

How to inject a service in Angular?

+
    +
  1. Create a provider either on your @NgModule, @Component, or @Directive using a type or a string as provider token.
  2. +
  3. +

    Inject the service in the component’s constructor where it’s needed using that configured token.

    +
+ + +

Dependency Injection as a pattern

+

Vojta Jina gave a great talk on dependency injection at ng-conf 2014. In this talk, he presented the story and ideas of the new DI system that will be developed for Angular. He also made very clear, that we can see DI as two things: As a design pattern and as a framework. Whereas the former explains the pattern that DI is all about, the latter can be a system that helps us out maintaining and assembling dependencies. I’d like to do the same in this article as it helps us understanding the concept first.

+

We start by taking a look at the following code and analysing the problems it introduces.

+
class Car {
+  constructor() {
+    this.engine = new Engine();
+    this.tires = Tires.getInstance();
+    this.doors = app.get('doors');
+  }
+}
+

Nothing special here. We have a class Car that has a constructor in which we set up everything we need in order to construct a car object once needed. What’s the problem with this code? Well, as you can see, the constructor not only assigns needed dependencies to internal properties, it also knows how those object are created. For example the engine object is created using the Engine constructor, Tires seems to be a singleton interface and the doors are requested via a global object that acts as a service locator.

+

This leads to code that is hard to maintain and even harder to test. Just imagine you’d like to test this class. How would you replace Engine with a MockEngine dependency in that code? When writing tests, we want to test different scenarios that our code is used in, hence each scenario needs its own configuration. If we want to write testable code, we need to write reusable code. Our code should work in any environment as long as all dependencies are satisfied. Which brings us to the conclusion that testable code is reusable code and vise versa.

+

So how can we write this code better and make it more testable? It’s super easy and you probably already know what to do. We change our code to this:

+
class Car {
+  constructor(engine, tires, doors) {
+    this.engine = engine;
+    this.tires = tires;
+    this.doors = doors;
+  }
+}
+

All we did is we moved the dependency creation out of the constructor and extended the constructor function to expect all needed dependencies. There are no concrete implementations anymore in this code, we literally moved the responsibility of creating those dependencies to a higher level. If we want to create a car object now, all we have to do is to pass all needed dependencies to the constructor:

+
var car = new Car(
+  new Engine(),
+  new Tires(),
+  new Doors()
+);
+

How cool is that? The dependencies are now decoupled from our class, which allows us to pass in mocked dependencies in case we’re writing tests:

+
var car = new Car(
+  new MockEngine(),
+  new MockTires(),
+  new MockDoors()
+);
+

And guess what, this is dependency injection. To be a bit more specific, this particular pattern is also called constructor injection. There are two other injection patterns, setter injection and interface injection, but we won’t cover them in this article.

+

Okay cool, now we use DI, but when comes a DI system into play? As mentioned before, we literally moved the responsibility of dependency creation to a higher level. And this is exactly what our new problem is. Who takes care of assembling all those dependencies for us? It’s us.

+
function main() {
+  var engine = new Engine();
+  var tires = new Tires();
+  var doors = new Doors();
+  var car = new Car(engine, tires, doors);
+
+  car.drive();
+}
+

We need to maintain a main function now. Doing that manually can be quite hairy, especially when the application gets bigger and bigger. Wouldn’t it be nice if we could do something like this?

+
function main() {
+  var injector = new Injector(...)
+  var car = injector.get(Car);
+
+  car.drive();
+}
+

Dependency Injection as a framework

+

This is where dependency injection as a framework comes in. As we all know, Angular 1 has it’s own DI system which allows us to annotate services and other components and let the injector find out, what dependencies need to be instantiated. For example, the following code shows how we can annotate our Car class in Angular 1:

+
class Car {
+  ...
+}
+
+Car.$inject = ['Engine', 'Tires', 'Doors'];
+

Then, we register our Car as a service and whenever we ask for it, we get a singleton instance of it without needing to care about creating needed dependencies for the car.

+
var app = angular.module('myApp', []);
+
+app.service('Car', Car);
+
+app.service('OtherService', function (Car) { 
+  // instance of Car available
+});
+

This is all cool but it turns out, that the existing DI has some problem though:

+
    +
  • Internal cache - Dependencies are served as singletons. Whenever we ask for a service, it is created only once per application lifecycle. Creating factory machinery is quite hairy.
  • +
  • Namespace collision - There can only be one token of a “type” in an application. If we have a car service, and there’s a third-party extension that also introduces a service with the same name, we have a problem.
  • +
  • Built into the framework - Angular 1’s DI is baked right into the framework. There’s no way for us to use it decoupled as a standalone system.
  • +
+

These problems need to be solved in order to take the DI of Angular to the next level.

+

Dependency Injection in Angular

+
+

Attention

+

The ReflectiveInjector APIs discussed in this article are deprecated as of this commit.

+

To learn about the newer StaticInjector API, head over to this section. However, concepts are still exactly the same, so it’s worthwhile to keep on reading.

+
+

Before we take a look at actual code, let’s first understand the concept behind the new DI in Angular. The following graphic illustrates required components in the new DI system:

+

DI in Angular

+

The DI in Angular basically consists of three things:

+
    +
  • Injector - The injector object that exposes APIs to us to create instances of dependencies.
  • +
  • Provider - A provider is like a recipe that tells the injector how to create an instance of a dependency. A provider takes a token and maps that to a factory function that creates an object.
  • +
  • Dependency - A dependency is the type of which an object should be created.
  • +
+

Okay, now that we have an idea of what the concept looks like, lets see how this is translated to code. We stick with our Car class and it’s dependencies. Here’s how we can use Angular’s DI to get an instance of Car:

+
import { ReflectiveInjector } from '@angular/core';
+
+var injector = ReflectiveInjector.resolveAndCreate([
+  Car,
+  Engine,
+  Tires,
+  Doors
+]);
+          
+var car = injector.get(Car);
+

We import ReflectiveInjector from Angular which is an injector implementation that exposes some static APIs to create injectors. resolveAndCreate() is basically a factory function that creates an injector and takes a list of providers. We’ll explore how those classes are supposed to be providers in a second, but for now we focus on injector.get(). See how we ask for an instance of Car in the last line? How does our injector know, which dependencies need to be created in order to instantiate a car? A look at our Car class will explain…

+
import { Inject } from '@angular/core';
+
+class Car {
+  constructor(
+    @Inject(Engine) engine,
+    @Inject(Tires) tires,
+    @Inject(Doors) doors
+  ) {
+    ...
+  }
+}
+

We import something called Inject from the framework and apply it as decorator to our constructor parameters. If you don’t know what decorators are, you might want to read our articles on the difference between decorators and annotations and how to write Angular code in ES5.

+

The Inject decorator attaches meta data to our Car class, that is then consumed by the DI system afterwards. So basically what we’re doing here, is that we tell the DI that the first constructor parameter should be an instance of type Engine, the second of type Tires and the third of type Doors. We can rewrite this code to TypeScript, which feels a bit more natural:

+
class Car {
+  constructor(engine: Engine, tires: Tires, doors: Doors) {
+    ...
+  }
+}
+

Nice, our class declares it’s own dependencies and the DI can read that information to instantiate whatever is needed to create an object of Car. But how does the injector know how to create such an object? This is where the providers come into play. Remember the resolveAndCreate() method in which we passed a list of classes?

+
var injector = ReflectiveInjector.resolveAndCreate([
+  Car,
+  Engine,
+  Tires,
+  Doors
+]);
+

Again, you might wonder how this list of classes is supposed to be a list of providers. Well, it turns out that this is actually a shorthand syntax. If we translate this to the longer, more verbose, syntax, things might become a bit more clear.

+
var injector = RelfectiveInjector.resolveAndCreate([
+  { provide: Car, useClass: Car },
+  { provide: Engine, useClass: Engine },
+  { provide: Tires, useClass: Tires },
+  { provide: Doors, useClass: Doors }
+]);
+

We have an object with a provide property, that maps a token to a configuration object. The token can either be a type or a string. If you read those providers now, it’s much easier to understand what’s happening. We provide an instance of type Car via the class Car, type Engine via the class Engine and so on and so forth. This is the recipe mechanism we were talking about earlier. So with the providers we not only let the injector know which dependencies are used across the application, we also configure how objects of these dependencies are created.

+

Now the next question comes up: When do we want to use the longer instead of the shorthand syntax? There’s no reason to write { provide: Foo, useClass: Foo} if we could just stick with Foo, right? Yes, that’s correct. That’s why we started with the shorthand syntax in the first place. However, the longer syntax enables us to do something very very powerful. Take a look at the next code snippet.

+
{ provide: Engine, useClass: OtherEngine }
+

Right. We can map a token to pretty much what ever we want. Here we’re mapping the token Engine to the class OtherEngine. Which means, when we now ask for an object of type Engine, we get an instance of class OtherEngine.

+

This is super powerful, because this allows us not only to prevent name collisions, we can also create a type as interface and bind it to a concrete implementation. In addition to that, we can swap out the actual dependency for a token in a single place without touching any other code.

+

Angular’s DI introduces a couple of other provider recipes which we explore in the next section.

+

Other provider configurations

+

Sometimes, we don’t want to get an instance of a class, but rather just a single value of something or a factory function where more configuration is needed. That’s why the provider mechanism of Angular’s DI comes with more than just one recipe. Lets take a quick look at them.

+

Providing values

+

We can provide a simple value using useValue: value

+
{ provide: 'some value', useValue: 'Hello World' }
+

This comes in handy when we want to provide simple configuration values.

+

Providing aliases

+

We can map an alias token to another token like this:

+
{ provide: Engine, useClass: Engine }
+{ provide: V8, useExisting: Engine }
+

Providing factories

+

Yes, our beloved factories.

+
{ 
+  provide: Engine,
+  useFactory: () => {
+    if (IS_V8) {
+      return new V8Engine();
+    } else {
+      return new V6Engine();
+    }
+  }
+}
+

Of course, a factory might have its own dependencies. Passing dependencies to factories is as easy as adding a list of tokens to the factory:

+
{
+  provide: Engine,
+  useFactory: (car, engine) => {
+
+  },
+  deps: [Car, Engine]
+}
+

Optional Dependencies

+

The @Optional decorator lets us declare dependencies as optional. This comes in handy if, for example, our application expects a third-party library, and in case it’s not available, it can fallback.

+
class Car {
+  constructor(@Optional(jQuery) $) {
+    if (!$) {
+    // set up fallback
+    }
+  }
+}
+

As you can see, Angular’s DI solves pretty much all issues we have with Angular 1’s DI. But there’s still one thing we haven’t talked about yet. Does the new DI still create singletons? The answer is yes.

+

Transient Dependencies and Child Injectors

+

If we need a transient dependency, something that we want a new instance every time we ask for a dependency, we have two options:

+

Factories can return instances of classes. Those won’t be singletons. Note that in the following code we’re creating a factory.

+
{ 
+  provide: Engine,
+  useFactory: () => {
+    return () => {
+      return new Engine();
+    }
+  }
+}
+

We can create a child injector using Injector.resolveAndCreateChild(). A child injector introduces its own bindings and an instance of an object will be different from the parent injector’s instance.

+
var injector = ReflectiveInjector.resolveAndCreate([Engine]);
+var childInjector = injector.resolveAndCreateChild([Engine]);
+
+injector.get(Engine) !== childInjector.get(Engine);
+

Child injectors are even more interesting. It turns out that a child injector will look up a token binding on it’s parent injector if no binding for the given token is registered on the child injector. The following graphic visualises what happens:

+

Child injectors

+

The graphic shows three injectors where two of them are child injectors. Each injector gets its own configuration of providers. Now, if we ask the second child injector for an instance of type Car, the car object will be created by that child injector. However, the engine will be created by the first child injector and the tires and doors will be created by the outer most parent injector. It kind of works like a prototype chain.

+

We can even configure the visibility of dependencies, and also until where a child injector should look things up. However, this will be covered in another article.

+

New StaticInjector APIs

+

Since version 5.0.0, ReflectiveInjector has been deprecated in favour of StaticInjector to not depend on reflection to get hold of dependencies. We don’t really have to worry about this if we aren’t dealing with creating injectors manually (because Angular does it for us). However, if we are, for whatever reason, relying on lower level injector APIs, here’s what’s different:

+
    +
  • Creating injectors is done via Injector.create()
  • +
  • Dependencies tokens have to be listed explicitly using deps property
  • +
+

StaticInjector doesn’t really need to resolve tokens anymore as everything is supposed to be static and passed right to the configuration. That’s why there’s not resolveAndCreate(), but a create() method. Also, instead of importing ReflectiveInjector, we’re importing Injector.

+
import { Injector } from '@angular/core';
+
+let injector = Injector.create([
+  { provide: Car, deps: [Engine, Tires, Doors] },
+  { provide: Engine, deps: [] },
+  { provide: Tires, deps: [] },
+  { provide: Doors, deps: [] }
+]);
+
+let car = injector.get(Car);
+

Notice in the following section we discuss how DI is used within Angular. There we don’t have to specificy deps explicitly as long as we’re defining providers in @Component() or @NgModule().

+

How is it used in Angular then?

+

Now that we’ve learned how the DI in Angular works, you might wonder how it is used in the framework itself. Do we have to create injectors manually when we build Angular components? Luckily, the Angular team spent a lot of energy and time to find a nice API that hides all the injector machinery when building components in Angular.

+

Lets take a look at the following simple Angular component.

+
@Component({
+  selector: 'app',
+  template: '<h1>Hello {{name}}!</h1>'
+})
+class App {
+  name = 'World';
+}
+

Nothing special here. If this is entirely new to you, you might want to read our article on building a zippy component in Angular. Lets say we want to extend this component by using a NameService that is used in the component’s constructor. Such a service could look something like this:

+
class NameService {
+  name = 'Pascal';;
+
+  getName() {
+    return this.name;
+  }
+}
+

Again, nothing special here. We just create a class. Now, to make it available in our application as an injectable, we need to pass some provider configurations to our application’s injector. But how do we do that? We haven’t even created one.

+

To boostrap an application, we define an NgModule. The @NgModule() decorator creates metadata that can include providers, just like this:

+
@NgModule({
+  imports: [BrowserModule],
+  providers: [NameService],
+  declarations: [App],
+  bootstrap: [App]
+})
+export class AppModule {}
+

That’s it. Now, to actually inject it, we just use the tools we’ve already learned about - @Inject decorators.

+
class App {
+  constructor(@Inject(NameService) NameService) {
+    this.name = NameService.getName();
+  }
+}
+

Or, if we prefer TypeScript, we can just add type annotations to our constructor:

+
class App {
+  constructor(NameService: NameService) {
+    this.name = NameService.getName();
+  }
+}
+

Awesome! All of a sudden, we don’t have any Angular machinery at all anymore. But there’s one last thing: What if we want a different binding configuration in a specific component?

+

Lets say we have NameService as application wide injectable for the type NameService, but one particular component should get a different one? This is where the @Component decorators’ providers property comes in. It allows us to add providers to a specific component (and its child components).

+
@Component({
+  selector: 'app',
+  providers: [
+    { provide: NameService, useValue: 'Thomas' }
+  ],
+  template: '<h1>Hello {{name}}!</h1>'
+})
+class App {
+  ...
+}
+

To make things clear: providers doesn’t configure the instances that will be injected. It configures a child injector that is created for that component. As mentioned earlier, we can also configure the visibility of our bindings, to be even more specific which component can inject what. E.g. the viewProviders property allows to make dependencies only available to a component’s view, but not its children. We’re going to cover that in another article. Dependency injection host and visibility are covered in this article.

+

Conclusion

+

The new dependency injection system in Angular solves all the problems that we have with the current DI in Angular 1. No name collisions anymore. It’s an isolated component of the framework that can be used as standalone system, without Angular itself.

+

I gave a talk about that topic at JSConf Budapest 2015, you can find the slides here. An updated version of the slide deck is here. I would like to thank Merrick for letting me use some ideas of his talk at ng-vegas, and Vojta who built the original version of the new dependency injection system for Angular.

+

Check out the demos below!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/06/16/routing-in-angular-2.html b/angular/2015/06/16/routing-in-angular-2.html new file mode 100644 index 000000000..f69ea316c --- /dev/null +++ b/angular/2015/06/16/routing-in-angular-2.html @@ -0,0 +1,214 @@ +Routing in Angular | Articles by thoughtram

Angular

Routing in Angular

{% include deprecated.html %}

+

Please read our new article Routing in Angular revisited

+

If you’re following our articles on Angular you might know that, a couple of months ago, we’ve written about the new router, how it can be used, and how it differs from the ngRoute standard router. Whereas we mostly touched on using the router in Angular 1 applications, this article discusses how it can be used in Angular 2 applications.

+

We won’t talk about how to get started with Angular 2. We assume that we’re all familiar with the installation process and how to bootstrap a small Angular 2 application. If this is entirely new to you, I recommend checking out our articles on developing a zippy component or developing a tabs component in Angular 2. Another great starting point is the starter kit by our friends over at AngularClass.

+

Bootstrapping the router

+

In order to use the new router, we need to import all needed components from the Angular framework and bind them to our component’s injector. Note that, even if in this article we’re importing straight from Angular, this might change in the future, since the router source has only temporarily been moved to the core.

+

To avoid additional typing, and also to hide some boilerplate logic, the router module exports a variable routerInjectables which contains all injector bindings to get going. To give you an idea of what that looks like, here’s a small snippet from the router’s source:

+
export var routerInjectables: List<any> = [
+  RouteRegistry,
+  Pipeline,
+  BrowserLocation,
+  Location,
+  bind(Router).toFactory((registry, pipeline, location, appRoot) => {
+    return new RootRouter(registry, pipeline, location, appRoot);
+  },[
+    RouteRegistry,
+    Pipeline,
+    Location,
+    appComponentTypeToken
+  ])
+];
+

It’s really just a collection of binding declarations. If you see those kind of bindings for the very first time, you might want to read our article on dependency injection in Angular 2. The short version is that each item in this collection describes how to create an object of a specific type when our application asks for an object of that type.

+

For example the following binding tells the injector to create an instance of RootRouter whenever someone asks for an object of type Router.

+
bind(Router).toFactory((registry, pipeline, location, appRoot) => {
+  return new RootRouter(registry, pipeline, location, appRoot);
+},[
+  RouteRegistry,
+  Pipeline,
+  Location,
+  appComponentTypeToken
+])
+

The router exports a couple of other components that are needed thoughout our application, but we’ll cover them later in this article.

+

Alright, now that we know what routerInjectables are, we can import and use them when bootstrapping our application, to make the router components available in our component’s dependency injector.

+
import { bootstrap } from 'angular2/angular2';
+import { routerInjectables } from 'angular2/router';
+
+... // App is defined here
+
+bootstrap(App, [routerInjectables]);
+

bootstrap takes a list of injector bindings as second argument. Those bindings are used when an injector is created. which means, passing routerInjectables here basically makes all the bindings application-wide available.

+

For now we want to implement basic routing with a couple of components. All we need to do is to create a component that has a template with a navigation, and some router specific template logic. Let’s start small - here’s what our App component could look like (without router logic):

+
...
+import { Component, View } from 'angular2/angular2';
+
+@Component({
+  selector: 'app'
+})
+@View({
+  template: `
+    <nav>
+      <ul>
+        <li>Start</li>
+        <li>About</li>
+        <li>Contact</li>
+      </ul>
+    </nav>
+    <main>
+      // components go here
+    </main>
+  `
+})
+class App {
+
+}
+...
+

Pretty much an empty component with a template. We also take advantage of decorators, that’s why we need to import Component and View. You can read more about decorators (and also annotations) in our article on the difference between annotations and decorators. Annotations can be used in ES5 code as well. In case this is more interesting to you, this article shows you how.

+

Configuring routes

+

According to our application’s template, we seem to have a Start, an About and a Contact component. In order to be able to navigate to those components, we first need to configure the router in our application. As mentioned earlier, next to routerInjectables, there are other components that the router module exports. One of them is the RouteConfig class which can be used to decorate a component with routing capabilities.

+

RouteConfig takes a collection of route configurations. A route configuration is an object with a path and a component, so pretty much the same we have in Angular 1 too. Here’s what the route configuration for our Start component could look like:

+
...
+import { Start } from './components/start';
+
+@RouteConfig([
+  { path: '/', component: Start }
+])
+class App {
+
+}
+

Easy right? One small difference compared to the Angular 1 version we see here, is that a component is provided as a class (or constructor function in ES5) instead of a string that represents the name of a component.

+

That’s why we need to import our Start component first. Let’s take a quick look at what that component looks like:

+
@Component({
+  selector: 'start'
+})
+@View({
+  template: '<h1>Start</h1>'
+})
+export class Start {
+
+}
+

Nothing special here. Let’s make sure that our loaded component is actually displayed when the router activates it.

+

Displaying the component

+

In order to display the loaded component in our application, we need to specify the location in our application’s template. If you’ve read our first article on the new router, you know that there’s a directive called RouterOutlet that lets us define a place in our template to display the loaded component.

+

Let’s do that. All we need to do is to import the directive, extend our application’s @View annotation configuration and use the directive in the template:

+
...
+import { RouterOutlet } from 'angular2/router';
+
+@Component({
+  selector: 'app'
+})
+@View({
+  directives: [RouterOutlet],
+  template: `
+    <nav>
+      <ul>
+        <li>Start</li>
+        <li>About</li>
+        <li>Contact</li>
+      </ul>
+    </nav>
+    <main>
+      <router-outlet></router-outlet>
+    </main>
+  `
+})
+class App {
+
+}
+...
+

That’s it! Since our route configuration says that / is mapped to our Start component, we don’t have to do anything special for now to activate that component - it is loaded by default.

+

Linking to other components

+

Of course, setting up a single route that is displayed by default, isn’t really what we’re looking for. Our application can have multiple route configurations and we also want to be able to navigate to each of them. But how can we achieve this? In ngRoute we just use normal links with href attributes and the module is smart enough to intercept accordingly. In UI-Router, we have a directive uiSref that takes a state name or a relative path to navigate to. What does the new router provide?

+

Well, as you might know, the new router shares a code base for both, the Angular 1 and Angular 2 version. In our last article we explored the router-link directive, which allows us to navigate to components. Guess what, we can use the same in Angular 2!

+

In Angular 2, the RouterLink directive is exported by the router module. In order to use it, we need to import it first and declare it as directive dependency of our component’s template, just like RouterOutlet.

+
...
+import { RouterOutlet, RouterLink } from 'angular2/router';
+
+@Component({
+  selector: 'app'
+})
+@View({
+  directives: [RouterOutlet, RouterLink],
+  ...
+})
+class App {
+
+}
+...
+

Awesome, now it can be used in the template. Let’s extend our navigation with proper links that make use of RouterLink. The directive takes a component name and if the underlying DOM element is an anchor element, it generates a URL based on the component’s route configuration, which is then added to it as part of the href attribute.

+
<nav>
+  <ul>
+    <li><a router-link="start">Start</a></li>
+    <li><a router-link="about">About</a></li>
+    <li><a router-link="contact">Contact</a></li>
+  </ul>
+</nav>
+

You might wonder, how the directive knows that "start" should generate a URL for our Start component. In fact, it can’t until we extend our router configuration. Next to path and component there’s another configuration property as that allows us to expose a router configuration under a given name.

+

E.g. if we want to use start as an “alias” for the route configuration for the Start component, we simply have to add the configuration accordingly. While we are at it, we do the same for the other two components, and just assume that we’ve built them already:

+
import { Start } from './components/start';
+import { About } from './components/about';
+import { Contact } from './components/contact';
+
+...
+
+@RouteConfig([
+  { path: '/', component: Start, as: 'start'},
+  { path: '/about', component: About, as: 'about'},
+  { path: '/contact', component: Contact, as: 'contact'},
+])
+

Now the router actually knows for what to generate URLs when using RouterLink. Cool, we can navigate to single components throughout our application, but what if we have components that use more than one RouterOutlet?

+

Routes with sibling outlets

+

It’s very common to have sibling outlets in a state (or component). In Angular 1, the UI-Router comes with a very powerful mechanism to support sibling states. Of course, such a feature is supported in the new component router too. At the time of writing this article, this feature was still in development and didn’t work yet, but we can still take a look at what the APIs will look like.

+

Having a component with multiple outlets is easy. Let’s say we have a dashboard component component that needs to load a navigation component and a statistics component. First we would of course setup a route config to be able to navigate to the dashboard component.

+
import { Dashboard } from './components/dashboard';
+
+@RouteConfig([
+  { path: '/dashboard', component: Dashboard, as: 'dashboard'}
+])
+class App {
+
+}
+

Alright, nothing new here. Let’s jump over to the dashboard component. The dashboard defines two sibling outlets. In order to tell the router what to load into those siblings, we can give them names and reference them to components accordingly in our route configuration. Here’ what the configuration of the dashboard component could look like:

+
import { Navigation } from './components/navigation';
+import { Statistics } from './components/statistics';
+
+@RouteConfig([
+  {
+    path: '/dashboard',
+    components: {
+      navigation: Navigation,
+      statistics: Statistics
+    },
+    as: 'dashboard'
+  }
+])
+class Dashboard {
+
+}
+```js
+
+<p style="background: #F2AEAE; border-radius: 0.3em; margin-top: 1.4em; padding: 1em; border: #F56B6B 1px solid;"><strong>Attention</strong>: <br>
+The <code>components</code> property has been removed as of <a href="https://github.com/angular/angular/issues/2329">#2329</a> but the feature will be reintroduced with a different mechanism.
+</p>
+
+Again, `Navigation` and `Statistics` are really just two components that don't even have to know that they are part of a route configuration. We can also see how the `as` property in the route configuration can be nicely used as an alias for component combinations.
+
+## There's so much more coming
+
+This article gives us just a little picture of how the new router can be used in Angular 2 compared to Angular 1. As you can see a lot of things are very similar, **which will surely help when migrating to Angular 2**. Of course, there are more things to cover, like how to deal with nested components and how to do dynamic routing using `Router`. Those things will be part of future articles.
Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/06/25/styling-angular-2-components.html b/angular/2015/06/25/styling-angular-2-components.html new file mode 100644 index 000000000..8c511209f --- /dev/null +++ b/angular/2015/06/25/styling-angular-2-components.html @@ -0,0 +1,117 @@ +Styling Angular components | Articles by thoughtram

Angular

Styling Angular components

Until now, we mostly talked about how to create simple components in Angular, like a zippy or a tabs component, and we also covered some isolated parts of the framework like the new dependency injection. In this article we are going to discuss another essential part when it comes to building components: Styling.

+

A component in Angular is basically a controller class with a template. But as all of us know, a component also needs it’s own styles, especially when it comes to sharing reusable components across applications, which is what we want to achieve in the modern web anyways, right?

+

We can always write our CSS code in a way, that it is modular and easily extensible at the same time. However, if we don’t rely on technologies like Web Components, our styles all end up concatenated and minified in the head of our HTML document, without our components actually knowing that they exist. This is actually good when we think in separation of concerns, on the other hand, if we build a component and want to share it, it should come packaged with all the needed styles, scoped to that component.

+

Angular components are designed with exactly that in mind. A component comes with HTML, JavaScript but also has it’s own styles that belong to it. All we need to do is to define the styles in our component, or at least declare, where to get those from. In fact, there are three ways to associate CSS styles to a component in Angular: Component inline styles, style urls and template inline styles. Let’s explore them one by one.

+

Component inline styles

+

The easiest way to add styles to a component is taking advantage of the @Component decorators that allow us to define component inline styles. All we need to do is to add a styles property to the decorator and define the styles. To see what that looks like, here’s a snippet of our zippy component that we’ve built a while ago.

+
@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html',
+  styles: [`
+    .zippy {
+      background: green;
+    }
+  `]
+})
+class ZippyComponent {
+  @Input() title: string;
+}
+

This is pretty straight forward. You might wonder though, why the value of that property is a list and not just a (multi-line) string. Well, I wonder too. That’s why I asked the question right away.

+

Okay, so defining styles on the component is pretty clear, but where did those end up in the DOM? If we run this code in our browser, we see that there’s something very interesting happening. It turns out that Angular takes the defined styles, and writes them into the head of the HTML document. Here’s what that looks like:

+
<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .zippy { 
+        background: green;
+      }
+    </style>
+  </head>
+  <body>
+  ...
+  </body>
+</html>
+

What’s going on there? The reason why Angular takes our styles and puts them up there, is because of the View Encapsulation that we are using. Since Angular 2.x, it comes with three different view encapsulation types in order to support both, browsers that don’t support Shadow DOM, and also the ones that do support it. The view encapsulations will be explored in another article are covered in this article, but we have to touch on this though in order to understand why this is happening.

+

Angular currently uses the Emulated View Encapsulation by default. Which basically means, there’s no usage of any Shadow DOM at all. One of the nice features of Shadow DOM is style encapsulation. It allows us to scope styles to a specific component without affecting the outer world.

+

To take advantage of style encapsulation, styles have to be put into the shadowRoot of a component. Due to the Shadow DOM strategy that is used, there is no shadowRoot to put our styles into. That’s why Angular writes them into the head. But as mentioned, there’s another article that explains all three view encapsulations.

+

Let’s take a look at another way of adding styles to our component.

+

Styles urls

+

In an ideal world, we don’t have to mix our styles with our application code. That’s why we have the <link> tag, that allows us to fetch and embed a stylesheet from a server. Angular components allow us to define styleUrls, so that styles don’t have to be written into the component. Pretty straight forward, here’s an example:

+
@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html',
+  styleUrls: ['my-zippy.component.css']
+})
+class ZippyComponent {
+  @Input() title: string;
+}
+

Where do those end up in the DOM? Well, for the same reason as explained earlier, they are written into the head of the document. But not only that, when Angular fetches the style resources, it takes the text response, inlines and appends them after all component inline styles. So if we would have a configuration like this:

+
@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html',
+  styles: ['.zippy { background: green; }'],
+  styleUrls: ['my-zippy.component.css']
+})
+class ZippyComponent {
+  @Input() title: string;
+}
+

And the my-zippy.component.css content would look like this:

+
.zippy {
+  background: blue;
+}
+

We will end up with a document head that looks something like this:

+
<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .zippy { 
+        background: green;
+      }
+    </style>
+    <style>.zippy {
+      background: blue;
+    }
+    </style>
+  </head>
+  <body>
+  ...
+  </body>
+</html>
+

This also brings us to the next conclusion that styles defined in style urls will always be appended and therefore override styles defined in the component, unless the inline styles don’t have a higher specificity.

+

Last but not least, we have template inline styles.

+

Template inline styles

+

We can for sure always write our styles directly into the DOM, nobody can prevent us from doing that. In fact, when thinking in Web Components it’s quite common to put styles directly into the template of a component, since they will be encapsulated when Shadow DOM is used.

+

Translating the styles used above to template inline styles would look something like this (in case of our zippy component):

+
<style>
+  .zippy {
+    background: red;
+  }
+</style>
+<div class="zippy">
+  <div (click)="toggle()" class="zippy__title">
+    {{ visible ? '&blacktriangledown;' : '&blacktriangleright;' }} {{title}}
+  </div>
+  <div [hidden]="!visible" class="zippy__content">
+    <content></content>
+  </div>
+</div>
+

Guess what, also those will be appended in the head of our document, after the ones defined in the component or as style urls. Template inline styles always have the highest priority, which sounds pretty straight forward to me.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/06/25/taking-angular-master-class-to-the-next-level.html b/angular/2015/06/25/taking-angular-master-class-to-the-next-level.html new file mode 100644 index 000000000..740dc44c9 --- /dev/null +++ b/angular/2015/06/25/taking-angular-master-class-to-the-next-level.html @@ -0,0 +1,59 @@ +Taking Angular Master Class to the next level | Articles by thoughtram

Angular

Taking Angular Master Class to the next level

We’ve been running our Angular Master Class since a couple of months now and travelled to many different countries and cities to meet you in person and exchange our experience. If you’ve attended one of our workshops, you know that we always ask for feedback so we can improve our material and of course, also your experience as an attendee.

+

So far we always got very positive feedback. Our attendees are very happy and satisfied with our material, and also the way we run our workshops. Of course, this makes us very happy and we don’t want to stop here.

+

In our workshops we try to give a very deep understanding of what happens under the hood, whether it is Angular or Git. Sometimes however, it happens that one or the other attendee would have liked to dig even deeper into certain topics, or learn about additional topics that are not covered in our Angular Master Class per se.

+

Angular Master Class extended

+

Guess what, we listened! Today we are very happy to announce that we’ve extended our material with additional and advanced topics.

+

+ + + angular master class extended + +

+

Next to our existing material, we now offer enough slides and exercises for everything about Forms, the UI-Router and server communication. We also talk about migration and how you can prepare your applications for Angular 2.x today. You can find more information at our dedicated website.

+

And of course, that’s not enough. We’ve been frequently asked about a training on everything about testing your Angular applications. It’s on the way, so stay tuned!

+

You might wonder how this fits into our current schedule when running our workshop. At public events, the Angular Master Class is still a 2-day training. However, attendees can decide if they want to swap out topics to their needs so everyone gets the best experience possible.

+

What about in-house trainings?

+

Next to our public events we also do in-house trainings at companies that want to build their next application with Angular, or deepen their knowledge about the framework. With the extended material, companies have even more options to choose from when putting their package together.

+

If your company has very specific needs, don’t hesitate to ask!

+

I want Angular 2.x!

+

We too! Since a lot of people ask us if we also do workshops on Angular 2.x, the answer is yes! Angular 2.x is not really around the corner, but actively developed so it can be shipped as soon as possible. We’re following the development every day, contribute to the framework and write about isolated parts of it so you can already start learning.

+

We also want to be able to offer you a very good training experience on Angular 2.x as soon as it is stable, that’s why we are already working on material. If this is interesting to you, make sure to follow us on twitter where we regularly post updates on our trainings.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/06/29/shadow-dom-strategies-in-angular2.html b/angular/2015/06/29/shadow-dom-strategies-in-angular2.html new file mode 100644 index 000000000..43bae127d --- /dev/null +++ b/angular/2015/06/29/shadow-dom-strategies-in-angular2.html @@ -0,0 +1,146 @@ +View Encapsulation in Angular | Articles by thoughtram

Angular

View Encapsulation in Angular

In our article on styling Angular components we learned how styles are applied to our component when defining them in different ways. We mentioned that all our component styles are appended to the document head, but usually would end up in the component’s template, in case we use native Shadow DOM. This article explains not only how we can tell Angular to use native Shadow DOM, but also what the other view encapsulation solutions are, that the framework comes with and why they exist.

+

Understanding Shadow DOM

+

Before we get started and take a look at how to use Angular’s different view encapsulation types, we need to understand what Shadow DOM actually is, what it makes so awesome and why we want to use it. We won’t have a super deep dive here, since there are a lot of great resources out there already. If you want to start from scratch and learn Shadow DOM 101, which you really should in case this is new to you, Eric Bidelman has written one of the best guides over at html5rocks.com.

+

In one sentence, Shadow DOM is part of the Web Components standard and enables DOM tree and style encapsulation. DOM tree and style encapsulation? What does that even mean? Well, it basically means that Shadow DOM allows us to hide DOM logic behind other elements. Addition to that, it enables us to apply scoped styles to elements without them bleeding out to the outer world.

+

Why is that great? We can finally build components that expose a single (custom) element with hidden DOM logic under the hood, and styles that only apply to that element - a web component. Just think of an <input type="date"> element. Isn’t it nice that we can just use a single tag and the browser renders a whole date picker for us? Guess with what you can achieve that…

+

Shadow DOM in Angular

+

Now that we got an idea of what Shadow DOM is (and trust me, there is so much more to cover), we can take a look at how Angular actually uses it.

+

As we know, in Angular we build components. A component is a controller class with a template and styles that belong to it. Those components can be shared across applications if they are general enough. That means, Angular already embraces the idea of building applications in components and making them reusable. However, components in Angular are not web components per se but they take advantage of them as mentioned earlier.

+

Whenever we create a component, Angular puts it’s template into a shadowRoot, which is the Shadow DOM of that particular component. Doing that, we get DOM tree and style encapsulation for free, right? But what if we don’t have Shadow DOM in the browser? Does that mean we can’t use Angular in those environments? We can. In fact, Angular doesn’t use native Shadow DOM by default, it uses an emulation. To be technically correct, it also doesn’t create a shadowRoot for our components in case no native Shadow DOM is used.

+

The main reason for that is that most browsers simply don’t support Shadow DOM yet, but we should still be able to use the framework. Even better, we can easily tell Angular to use the native Shadow DOM if we want. So how is that implemented and what do we need to do?

+

View Encapsulation Types

+

Angular comes with view encapsulation built in, which enables us to use Shadow DOM or even emulate it. There are three view encapsulation types:

+
    +
  • ViewEncapsulation.None - No Shadow DOM at all. Therefore, also no style encapsulation.
  • +
  • ViewEncapsulation.Emulated - No Shadow DOM but style encapsulation emulation.
  • +
  • ViewEncapsulation.Native - Native Shadow DOM with all it’s goodness.
  • +
+

You might wonder why we have three types. Why not just one for native Shadow DOM support and another one that doesn’t use Shadow DOM? Things become more clear when we explore how they affect the way Angular applies styles to components. Let’s try them out one by one.

+

ViewEncapsulation.None

+

Angular doesn’t use Shadow DOM at all. Styles applied to our component are written to the document head. We talked about that in a more detail in styling Angular components, but to make a quick recap, having a zippy component with styles like this (note that we set the encapsulation property in our @Component decorator):

+
import {ViewEncapsulation} from '@angular/core';
+
+@Component({
+  moduleId: module.id,
+  selector: 'my-zippy',
+  templateUrl: 'my-zippy.component.html',
+  styles: [`
+    .zippy {
+      background: green;
+    }
+  `],
+  encapsulation: ViewEncapsulation.None
+})
+class ZippyComponent {
+  @Input() title: string;
+}
+

And a template like this:

+
<div class="zippy">
+  <div (click)="toggle()" class="zippy__title">
+    {{ visible ? '&blacktriangledown;' : '&blacktriangleright;' }} {{title}}
+  </div>
+  <div [hidden]="!visible" class="zippy__content">
+    <ng-content></ng-content>
+  </div>
+</div>
+

Will make Angular creating a DOM like this:

+
<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .zippy { 
+        background: green;
+      }
+    </style>
+  </head>
+  <body>
+    <my-zippy title="Details">
+      <div class="zippy">
+        <div (click)="toggle()" class="zippy__title">
+          ▾ Details
+        </div>
+        <div [hidden]="!visible" class="zippy__content">
+          <script type="ng/contentStart"></script>
+            ...
+          <script type="ng/contentEnd"></script>
+        </div>
+      </div>
+    </my-zippy>
+  </body>
+</html>
+

Again, this is due to the fact that there’s no Shadow DOM. This also means that all the styles apply to the entire document. Or in other words, a component could overwrite styles from another component because its styles are applied to the document head later. That’s why this is the unscoped strategy. If there was Shadow DOM, Angular could just write all the styles into the shadowRoot which will enable style encapsulation.

+

Also note that the <ng-content> tag has been replaced with <script> tags that basically act as markers to emulate content insertion points.

+

ViewEncapsulation.Emulated

+

This view encapsulation is used by default. ViewEncapsulation.Emulated emulates style encapsulation, even if no Shadow DOM is available. This is a very powerful feature in case you want to use a third-party component that comes with styles that might affect your application. What happens to our components, and especially to the styles, when this view encapsulation is used? Well, let’s first check if the styles are still written to the document head. Here’s what the head looks like with the exact same component but different strategy:

+
<head>
+  <style>.zippy[_ngcontent-1] {
+  background: green;
+  }</style>
+</head>
+

Looks like styles are still written to the document head. But wait, what’s that? Instead of the simple .zippy selector that we used, Angular creates a .zippy[_ngcontent-1] selector. So it seems like Angular rewrote our component’s styles. Let’s see what the component’s template looks like:

+
<my-zippy title="Details" _ngcontent-0 _nghost-1>
+  <div class="zippy" _ngcontent-1>
+    <div (click)="toggle()" class="zippy__title" _ngcontent-1>
+      ▾ Details
+    </div>
+    <div [hidden]="!visible" class="zippy__content" _ngcontent-1>
+      <script type="ng/contentStart" class="ng-binding"></script>
+        ...
+      <script type="ng/contentEnd"></script>
+    </div>
+  </div>
+</my-zippy>
+

Ha! Angular added some attributes to our component’s template as well! We see the _ngcontent-1 attribute which is also used in our rewritten CSS, but we also have _ngcontent-0 and _nghost-1. So what the hell is going on there?

+

Actually it’s quite simple. We want scoped styles without Shadow DOM right? And that’s exactly what happens. Since there’s no Shadow DOM, Angular has to write the styles to the head of the document. Okay that’s nothing new, we know that from the unscoped strategy. But in order to enable scoped styles, Angular has to make sure that the component’s style selectors only match this particlar component and nothing else on the page. That’s why it extends the CSS selectors, so they have a higher specificity and don’t collide with other selectors defined before at the same. And of course, to make those selectors actually match, the elements in the template need to be extended as well. That’s why we see all those _ngcontent-* and _nghost-* attributes.

+

Okay cool, now we know how Angular emulates scoped styles, but still, why are those attributes called _ngcontent-* and _nghost-* and what does the number in the attributes mean? If we take a closer look at the generated template, we can actually see a pattern. The number in the attribute matches the Shadow DOM, or content insertion point, level.

+

The app that we bootstrap is a component that already uses Shadow DOM (emulation) and therefore has a content insertion point. That means, our root component is already a host element. However, it doesn’t get any additional attribute, because Angular is not in charge of rewriting it. We’ve written it (maybe) as <app></app> into our index.html and that’s it. Our zippy component is also a host element, which is why it get’s the _nghost-1 attribute. Why not _nghost-0? Well, that’s the one our root component would get. At the same time, the zippy component also gets a _ngcontent-0 attribute. That’s because it is part of the very first content insertion point level in the application, which is the one of our root component. We can confirm that pattern by taking a look at what’s inside the zippy element. Every direct child element inside the zippy element is part of the next content insertion point level, which is why they get the _ngcontent-1 attribute. And so on and so forth.

+

Not sure what you think, but in my opinion, this is a very smart approach.

+

ViewEncapsulation.Native

+

Last but not least, we have the native Shadow DOM view encapsulation. This one is super simple to understand since it basically just makes Angular using native Shadow DOM. We can activate it the same way we did with the other types. Here’s what that looks like:

+
@Component({
+  moduleId: module.id,
+  templateUrl: 'my-zippy.component.html',
+  styles: [`
+    .zippy {
+      background: green;
+    }
+  `],
+  encapsulation: ViewEncapsulation.Native
+})
+...
+

Okay that was easy. If we run our code in the browser, we see that no styles are written to the document head anymore. However, styles do now end up in the component’s template inside the shadow root. Here’s what that looks like:

+
<my-zippy title="Details">
+  #shadow-root
+  | <style>
+  |   .zippy {
+  |     background: green;
+  |   }
+  | </style>
+  | <div class="zippy">
+  |   <div (click)="toggle()" class="zippy__title">
+  |     ▾ Details
+  |   </div>
+  |   <div [hidden]="!visible" class="zippy__content">
+  |     <content></content>
+  |   </div>
+  | </div>
+  "This is some content"
+</my-zippy>
+

In order to get an output like this, we need to tell our browser dev tools to display Shadow DOM when inspecting element. No weird attributes anymore. Instead we get a nice shadow root and we can see very nicely how the styles are written into it.

+

From here on, all the rules that apply to plain Shadow DOM, apply to our Angular component as well.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/07/06/even-better-es5-code-for-angular-2.html b/angular/2015/07/06/even-better-es5-code-for-angular-2.html new file mode 100644 index 000000000..729ecf360 --- /dev/null +++ b/angular/2015/07/06/even-better-es5-code-for-angular-2.html @@ -0,0 +1,104 @@ +Even better ES5 code for Angular | Articles by thoughtram

Angular

Even better ES5 code for Angular

A couple of weeks ago we wrote about how to write Angular code in ES5 and took a closer look at what annotations and decorators translate to. While it is nice that we can all write Angular applications without the hassle of setting up a development environment for TypeScript, Babel or SystemJS, it turns out that the syntax is still quite wordy. Of course, this isn’t really a big problem, because it is just the syntax after all. That’s why the Angular team works hard on making even the ES5 experience much better. All improvements that land in the ES5 world shrink the gap between Angular 1.x and Angular >= 2.x syntax, in fact, upgrading will be rather boring.

+

In this article we’re going to take a closer look at the ES5 syntax improvements and how they make upgrading even easier.

+

Angular in ES5 before syntactical improvements

+

In order to understand the syntactical improvements in ES5 when building Angular applications, we have to understand what the code looked like before. Angular uses decorators to add meta data to it’s application code, which in TypeScript (or ES7) looks something like this:

+
@Component({
+  selector: 'hello-cmp',
+  template: 'Hello World!'
+})
+class HelloComponent {
+
+}
+

ES5 doesn’t have the concept of annotations or decorators. That’s why the code above translates to something like this, if we’d write it in ES5:

+
var HelloComponent = function () {
+
+};
+
+HelloComponent.annotations = [
+  new ng.core.Component({
+    selector: 'hello-cmp',
+    template: 'Hello World!'
+  })
+];
+

If this is entirely new to you, you might want to read our article on Angular code in ES5 which gives a more detailed explanation of this code. This particular ES5 code totally does it’s job, but as mentioned earlier, the syntax is quite wordy. We won’t get around the fact that we have to assemble annotations for our components ourselves. However, this can easily be fixed since it’s really just syntax after all.

+

Let’s take a look at how this can be done better with the more improved syntax for ES5.

+

Angular in ES5 after syntactical improvements

+

Angular comes with helper functions to create components and services right out of the box. Let’s take our HelloComponent and refactor it with the better syntax:

+
var HelloComponent = ng.core
+  .Component({
+    selector: 'hello-cmp',
+    template: 'Hello World!'
+  })
+  .Class({
+    constructor: function () { 
+
+    }
+  });
+

As we can see, Component() is pretty much the equivalent of @Component decorator. It takes care of creating annotations on our component, while we pass ComponentArgs to it accordingly. What about Class()? It’s pretty obvious that Class() takes the constructor function of our component, but can it also extend other “classes” or consume prototype methods as we would like to create?

+

Yeap, all that is possible let’s quickly go through all possible properties of Angular’s Class() method.

+

constructor

+

Nothing special here. constructor is a constructor function which internally gets called with Object.create().

+

extends

+

The extends property allows us to extend existing classes or components. Here’s a small example that shows what that could look like:

+
var OtherComponent = ng.core
+  .Component({
+    selector: 'other-cmp',
+    template: '<p>Other</p>'
+  })
+  .Class({
+    constructor: function () {
+
+    }
+  })
+
+var HelloComponent = ng.
+  Component({
+    selector: 'hello-cmp',
+    template: 'Hello World!'
+  })
+  .Class({
+    extends: OtherComponent,
+    constructor: function () { 
+
+    }
+  });
+

Prototype methods

+

We can define methods on our class simply by adding a property to it that is a function. In fact, every applied property in Class() has to be either a function or an array.

+
  ...
+  .Class({
+    constructor: function () { 
+      this.name = 'thoughtram';
+    },
+    getName: function () {
+      return this.name;
+    }
+  });
+

That’s pretty straight forward. But why do properties have to be arrays otherwise? Remember dependency injection in Angular? We can inject services and factories using @Inject decorators, but as we know, there are no decorators nor annotations in ES5. That’s where the array syntax comes in.

+
var HelloComponent = ng.core
+  Component({
+    selector: 'hello-cmp',
+    template: 'Hello World!',
+    viewProviders: [Service]
+  .Class({
+    constructor: [Service, function (service) { 
+      ...
+    },
+  });
+

Conclusion

+

As we can see, the gap between Angular 1.x and Angular 2.x is not so big anymore. The ES5 syntax for Angular 2.x applications is getting closer to the ES2016 or TypeScript equivalent, which hopefully helps people to not be scared anymore. It’s really just syntax.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/07/07/service-vs-factory-once-and-for-all.html b/angular/2015/07/07/service-vs-factory-once-and-for-all.html new file mode 100644 index 000000000..5e3e2de2f --- /dev/null +++ b/angular/2015/07/07/service-vs-factory-once-and-for-all.html @@ -0,0 +1,112 @@ +Service vs Factory - Once and for all | Articles by thoughtram

Angular

Service vs Factory - Once and for all

Wait, what? Yet another article that answers the big question: Service vs Factory, what should I use? Yes, it seems that this is not needed anymore, since there are a ton of resources in the internet that discuss that topic. It turns out that this question still pops up every week or so on different channels, and even after reading the top ten answers on StackOverflow, it’s still not very clear. Despite that, it also appears that the current resources on the web don’t really promote the actual best practice, especially if we consider the recent movements of the web platform. ES6 I’m looking at you!

+

This article explains once and for all the difference between services and factories and why we want to prefer services over factories.

+

The difference between services and factories

+

Okay, so what is the difference between a service and a factory in AngularJS? As we all know, we can define a service like this:

+
app.service('MyService', function () {
+  this.sayHello = function () {
+    console.log('hello');
+  };
+});
+

.service() is a method on our module that takes a name and a function that defines the service. Pretty straight forward. Once defined, we can inject and use that particular service in other components, like controllers, directives and filters, like this:

+
app.controller('AppController', function (MyService) {
+  MyService.sayHello(); // logs 'hello'
+});
+

Okay, clear. Now the same thing as a factory:

+
app.factory('MyService', function () {
+  return {
+    sayHello: function () {
+      console.log('hello');
+    }
+  }
+});
+

Again, .factory() is a method on our module and it also takes a name and a function, that defines the factory. We can inject and use that thing exactly the same way we did with the service. Now what is the difference here?

+

Well, you might see that instead of working with this in the factory, we’re returning an object literal. Why is that? It turns out, a service is a constructor function whereas a factory is not. Somewhere deep inside of this Angular world, there’s this code that calls Object.create() with the service constructor function, when it gets instantiated. However, a factory function is really just a function that gets called, which is why we have to return an object explicitly.

+

To make that a bit more clear, we can simply take a look at the Angular source code. Here’s what the factory() function looks like:

+
function factory(name, factoryFn, enforce) {
+  return provider(name, {
+    $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
+  });
+}
+

It takes the name and the factory function that is passed and basically returns a provider with the same name, that has a $get method which is our factory function. So what is it with this provider thing? Well, whenever you ask the injector for a specific dependency, it basically asks the corresponding provider for an instance of that service, by calling the $get() method. That’s why $get() is required, when creating providers.

+

In other words, if we inject MyService somewhere, what happens behind the scenes is:

+
MyServiceProvider.$get(); // return the instance of the service
+

Alright, factory functions just get called, what about the service code? Here’s another snippet:

+
function service(name, constructor) {
+  return factory(name, ['$injector', function($injector) {
+    return $injector.instantiate(constructor);
+  }]);
+}
+

Oh look, it turns out that when we call service() it actually calls factory(). But it doesn’t just pass our service constructor function to the factory as it is. It passes a function that asks the injector to instantiate and object by the given constructor. In other words: a service calls a predefined factory, which ends up as $get() method on the corresponding provider. $injector.instantiate() is the method that ultimately calls Object.create() with the constructor function. That’s why we use this in services.

+

Okay, so it turns out that, no matter what we use, service() or factory(), it’s always a factory that is called which creates a provider for our service. Which brings us to the mostly asked question in the Angular history: Which one should I use?

+

Which one to use?

+

Asking that question on the internet takes us to a couple of articles and StackOverflow answers. The first is this answer. It says:

+

“Basically the difference between the service and factory is as follows:”

+
app.service('myService', function() {
+
+  // service is just a constructor function
+  // that will be called with 'new'
+
+  this.sayHello = function(name) {
+     return "Hi " + name + "!";
+  };
+});
+
+app.factory('myFactory', function() {
+
+  // factory returns an object
+  // you can run some code before
+
+  return {
+    sayHello : function(name) {
+      return "Hi " + name + "!";
+    }
+  }
+});
+

We now already know what happens behind the scenes, but this answer adds another comment. It says we can run code before we return our object literal. That basically allows us to do some configuration stuff or conditionally create an object or not, which doesn’t seem to be possible when creating a service directly, which is why most resources recommend to use factories over services, but the reasoning is inappreciable.

+

What if I told you, we can do the exact same thing with services too?

+

Yeap, correct. A service is a constructor function, however, that doesn’t prevent us from doing additional work and return object literals. In fact, constructor functions in JavaScript can return whatever they want. So we can take our service code and write it in a way that it basically does the exact same thing as our factory:

+
app.service('MyService', function () {
+
+  // we could do additional work here too
+  return {
+    sayHello: function () {
+      console.log('hello');
+    };
+  }
+});
+

Hoppla, so what now? We just realised that, depending on how we write our services, there’s no difference between the two at all anymore. The big question remains: Which one should we use?

+

Services allow us to use ES6 classes

+

Of course, writing services in that way is kind of contra productive, since it’s called as a constructor function, so it should also be used like one. Is there any advantage over the other at all then? Yes, there is. It turns out that it’s actually better to use services where possible, when it comes to migrating to ES6. The reason for that is simply that a service is a constructor function and a factory is not. Working with constructor functions in ES5 allows us to easily use ES6 classes when we migrate to ES6.

+

For example, we can take our code and rewrite it in ES6 like this:

+
class MyService {
+  sayHello() {
+    console.log('hello');
+  }
+}
+
+app.service('MyService', MyService);
+

An ES6 class is really just a constructor function in ES5. We wrote about that in Using ES6 with Angular today, if you haven’t read that article yet, I’d recommend checking that out.

+

With factories, this is not possible because they are simply called as functions. I hope this article made everything clear and encourages people to not use factories over services, if they don’t know what to use.

+

This and more you learn in our Angular Master Class!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/08/11/angular-2-template-syntax-demystified-part-1.html b/angular/2015/08/11/angular-2-template-syntax-demystified-part-1.html new file mode 100644 index 000000000..af60360c2 --- /dev/null +++ b/angular/2015/08/11/angular-2-template-syntax-demystified-part-1.html @@ -0,0 +1,137 @@ +Angular Template Syntax Demystified - Part 1 | Articles by thoughtram

Angular

Angular Template Syntax Demystified - Part 1

I think we’ve been all through this. We see Angular template code the very first time and all of a sudden we have these weird brackets and parentheses spread all over our HTML. Around a year ago I’ve written an article about how to integrate Web Components with AngularJS, which explains how we can use, or not use, Web Components in AngularJS applications today. If you haven’t read it yet, I highly recommend you doing so. It’s old but the content is still true.

+

It also touches on the new template syntax in Angular and how it tends to solve the existing issues. However, it still seems a mystery for a lot of people and that’s why we’re going to explore the Angular’s template syntax in this article. Please note that this is the first part of “Angular Template Syntax Demystified”. There’s going to be another article soon.

+

What it looks like

+

Just to make sure that everyone knows what we are talking about when mentioning “weird” brackets and parentheses in our HTML, we’re going to make a quick recap on what this template syntax looks like.

+

If you’ve read the step-by-step guide you’ve probably seen the following syntaxes:

+
<p>My name: {{ myName}}</p>
+<ul>
+  <li *ngFor="let name of names">
+    ...
+  </li>
+</ul>
+<input #myname (keyup)="myControllerMethod()">
+

And that’s not all, in fact we could add some more:

+
<todo [todo]="todo"></todo>
+<input [(ngModel)]="foo">
+

What we see here is Event Binding, Property Binding, Local Variables and Template Directives. In this article we’re going to focus on property, event and two-way binding. But before we explore all of these different syntaxes, let’s answer the first question that comes to our mind when seeing this code:

+

Is that valid HTML?

+

The answer is yes. Here’s an excerpt of the HTML Syntax Spec:

+
+ Attribute names must consist of one or more characters other than the space characters, U+0000 NULL, """, "'", ">", "/", "=", the control characters, and any characters that are not defined by Unicode. +
+

Which basically means, any other characters than the ones listed can be used in HTML attribute names. Let’s start off by taking a look at the most important syntax for property binding.

+

Property Binding

+

Property binding is the syntax where we use brackets to bind values to an element’s property. But what does that mean? Why do we want to bind to an element’s property? Well, in order to understand why we want to do that, we have to go back to the basics and understand the APIs of a DOM element.

+

The APIs of a DOM element are:

+
    +
  • Attributes - Attributes are the things we use in HTML to provide elements with data. In fact, attributes are the only way in plain HTML to put values into an element. However, the type of an attribute value in HTML is always String, which is not always what we want especially when building applications with frameworks like Angular.
  • +
  • Properties - Properties are simply the properties of a DOM object. E.g. if we query a DOM element with document.querySelector(), we get a DOM object back which simply has its own properties and methods. Those properties are no special in any way, they behave like any other object properties in JavaScript. That also means we can assign any kind of value to a property, not just strings.
  • +
  • Methods - As already mentioned, methods are just the functions on a DOM object that we can call and execute in JavaScript. setAttribute() for example is such a method.
  • +
  • Events - Of course, last but not least, we have events. The bread and butter when it comes to notifying subscribers that something happened. Like click, focus or input. DOM elements can also fire their own custom events.
  • +
+

The most interesting part here, is how attributes and properties behave on a DOM object. Especially when we realize, that they behave differently across elements.

+

Let’s just take this simple input example here:

+
<input value="thoughtram">
+

We use the value attribute to set the initial value of the input element. Let’s see what happens when we access the DOM object and it’s value property:

+
var input = document.querySelector('input');
+input.value // 'thoughtram'
+

Alright, we access the value property and as expected, it returns the string thoughtram. But what happens when we change that value and read from its attribute?

+
input.value = 'Angular Master Class';
+input.getAttribute('value'); // 'thoughtram'
+

As we can see, the property value is not reflected back to the attribute. There are very few elements that actually reflect their property value back to its attribute. For example the src property of an img element. Changing its property will also change its attribute. In addition to that, a property can really get any value, whereas an attribute is always a string.

+

So how could we pass objects to directives in AngularJS? Well, as most of us know, there’s this directive definition object (DDO), which allows us to specify how directives’ scope properties (or controller properties) are bound to the outside world. The following code for example, allows us to pass an expression to a directive via attributes, that actually results in an object once evaluated.

+
angular.module('app', []).directive('widget', function () {
+  return {
+    scope: {},
+    bindToController: {
+      obj: '='
+    },
+    controller: function () {},
+    controllerAs: 'ctrl',
+    template: 'Value: ctrl.obj.foo'
+  }
+});
+

If bindToController is new to you, you might want to read this article.

+

From the outside world, we can then pass an object to our widget directive simply with an expression. Let’s say we have a controller like this:

+
angular.module('app', []).controller('AppController', function () {
+  this.objOnCtrl = {
+    foo: 'Hello World!'
+  };
+});
+

And a template like this:

+
<widget ng-controller="AppController as ctrl" obj="ctrl.objOnCtrl">
+

Angular takes the expression passed to the attribute and parses and evaluates it against the corresponding scope, which allows us to pass other values than strings to directives.

+

Why can’t we just continue like that in Angular? There are a couple of reasons:

+
    +
  • Predictability - Looking at our template, we can’t tell what happens to the value of the attribute inside our directive, without knowing the internals of it. We need a way to decide from the outside world how values are bound.
  • +
  • Compatibility - This whole mechanism only works with Angular directives. As soon as we use custom elements that aren’t directives but vanilla Web Components, ctrl.objOnCtrl would just be that string, unless our custom element would come with a similar parsing and evaluating strategy that Angular directives use. Angular should work with any element, no matter if it’s a custom element, a Web Component built with Polymer, or a directive.
  • +
+

That’s why since version 2.x, Angular always binds to properties rather than attributes (as AngularJS does). Every DOM element has properties, regardless of being a native element or a Web Component. In order to tell Angular that we want to bind to a property, we use the brackets syntax. If we’d build our widget directive in Angular, we would need to bind to it’s obj property to assign an object value:

+
<widget [obj]="objOnComponent">
+

Another feature that property binding brings to the table is escaping. Just think about the img tag and its src attribute. What happens when the browser ecounters an img tag with a source when parsing the HTML? Right, it tries to request the source of that image. If we’d have HTML like this:

+
<img src="{{someExpression}}">
+

It would result in a 404 error, because the source isn’t a valid URL. That’s why AngularJS has this ng-src directive. It takes an expression, evaluates it and once evaluated, it adds the actual src attribute to the image element, which results in a correct request. Binding to properties escapes automatically, because value will also only be assigned once their expressions are evaluated.

+

We can still use normal attributes without brackets though. Just keep in mind that normal attributes behave like normal attributes.

+

What about interpolations?

+

Good question! Interpolation in Angular has always been one of the biggest features of the framework. If there’s only property binding, how does something like the following work then?

+
<p>Hello {{name}}</p>
+

It turns out, that interpolation syntax is just a shorthand syntax for property binding as well. The code above could be translated to something like this:

+
<p [textContent]="interpolate(['Hello'], [name])"></p>
+

Canonical syntax

+

Okay cool, now we know what property binding is and why we want to use it, but there’s still a problem we haven’t talked about yet. What if we generate our templates with an HTML preprocessor that doesn’t like the brackets syntax? Guess what, the brackets syntax itself is really just a shorthand for us developers so we can save some key strokes. All property bindings inside a template can be written as:

+
<ANY bind-{PROPERTY_NAME}="{EXPRESSION}"></ANY>
+

Which makes it an alpha numeric version that all preprocessors should be able to deal with.

+

Event Binding

+

Binding to properties through HTML is already super powerful. But sometimes we need to be able to react to certain things that happen during runtime of our application. This is what DOM events are for and they are part of our browser platforms since years. Just think of click, input and focus events. However, nowadays it’s also very common for elements to fire their own custom events. A Web Component <date-picker> for example, could fire a dateChanged event.

+

In AngularJS, we have a lot of directives that help us out notifying the framework when an event is fired. That’s why we have things like ng-click, ng-focus etc. They simply notify Angular that an event has been fired and application state could have changed. Without these directives, Angular’s two-way data binding wouldn’t work out-of-the-box. Unfortunately, that doesn’t really scale. How can our <date-picker> Web Component notify Angular that a change has happened? Correct, we would need to create a directive for each and every event and it’s not uncommon for elements to fire more than just one event.

+

That’s why since version 2.x, in Angular we have the parenthesis syntax where we can bind to any event like this:

+
<date-picker (dateChanged)="statement()"></date-picker>
+

The parentheses simply tell Angular that the expression inside the symbols is an event name that it needs to add an event listener for. statement() is simply the statement expression that gets executed whenever such an event is fired on that element. Having such a generic binding, all directives that intercepted for us in AngularJS will go away in Angular. This framework is getting simpler and simpler.

+

Event Bubbling

+

When binding to events in Angular, events are caught on the same element as well as from events that bubble up from child elements. E.g. if we have a DOM structure like this:

+
<div (click)="statement()">
+  <div></div>
+</div>
+

This will execute the given statement, if the first div or any of it’s child elements are clicked.

+

Canonical syntax

+

Of course, using parenthesis is also just a shorthand syntax, so we as developers can save some key strokes. We can always use the canonical syntax, which is on-*.

+
<ANY on-{EVENT_NAME}="{STATEMENT}"></ANY>
+

Two-way Data Binding

+

We talked about property binding and event binding but what about the biggest selling point of AngularJS - two-way data binding?

+

Since 2.x, Angular doesn’t come with two-way data binding by default. This feature has been removed intentionally from the framework, because it comes with various drawbacks. But how is something like ng-model in Angular implemented then? ng-model in Angular version >= 2.x gives us two-way data binding, even though under the hood, it simply uses a combination of property and event binding.

+

Let’s take a look at how that works with the following snippet:

+
<input [value]="name">
+
+<p>Hello {{name}}</p>
+

We already learned what’s happening here. We’re simply binding the value of a name expression to the value property of an input element. In addition we want to output the same value in our view and update it when someone changes the value by typing into the input. This, however, doesn’t work because we’re just writing to the elements property. There’s nothing that tells the expression to update when the value changes through typing.

+

This can be easily fixed by adding an event binding accordingly:

+
<input [value]="name" (input)="name = $event.target.value">
+
+<p>Hello {{name}}</p>
+

We listen to the input event and whenever it is fired, we update the value of our name expression by pulling the new value out of the event object.

+

Since this is quite a lot of typing to implement such a common scenario, Angular comes with an ngModel directive that saves some key strokes for us, by unifying the property and event binding, and also the extraction of the target’s value. Here’s the exact same code using ngModel:

+
<input [ngModel]="name" (ngModelChange)="name = $event">
+
+<p>Hello {{name}}</p>
+

It gets even better! We can shorten that syntax even further by merging both bindings like this:

+
<input [(ngModel)]="name">
+
+<p>Hello {{name}}</p>
+

Two-way data binding is back in town! We can easily build our own directives that support that syntax, but that we’re going to explore in another article.

+

This was the first part of “Angular Template Syntax Demystified”. We’re going to cover local variables and template directives in a second part, which will be published very soon.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html b/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html new file mode 100644 index 000000000..0e0adfc19 --- /dev/null +++ b/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html @@ -0,0 +1,135 @@ +Host and Visibility in Angular's Dependency Injection | Articles by thoughtram

Angular

Host and Visibility in Angular's Dependency Injection

In our article on Dependency Injection in Angular we explored what dependency injection actually is, and how it is implemented in the Angular framework. If you haven’t read that article yet, I highly recommend you doing so, since this article is based on it.

+

Even though we learned that Angular’s new dependency injection is very flexible and solves pretty much all the problems we have with the dependency injection in AngularJS, there are still a couple of topics that we haven’t discussed yet. One of them is how Angular treats the relationship between host and child injectors, and the other one is how the visibility of dependencies are handled. In this article we’re going to explore exactly these two topics.

+

Understanding host relationships

+

Host and visibility are both features in Angular’s dependency injection system, that are very specific to Angular and throughout this article we’ll learn why. For now just keep in mind that we probably don’t need any of these features when using Angular’s DI not in the context of Angular itself. However, once we understood the context and why this feature exist, we’ll also take a look at how this is implemented under the hood, so we all know what’s going on.

+

Let’s start off by imagining the following scenario. We have three nested components that all do their own thing (because that’s what components do in Angular):

+
<component-one>
+  <component-two>
+    <component-three></component-three>
+  </component-two>
+</component-one>
+

As we learned in our article on dependency injection in Angular, each component in Angular creates its own injector. Which means the code above can be translated to something like this:

+
injector (<component-one>)
+      ^
+      |
+child injector (<component-two>)
+      ^
+      |
+grand child injector (<component-three>)
+

The ^ symbol just signalises that a child injector is created from it’s parent. To come back to our nice and cozy JavaScript world, we could also translate it to this:

+
var injector = Injector.create();
+var childInjector = Injector.create([], injector);
+var grandChildInjector = Injector.create([], childInjector);
+

Of course, this code is very simplified and as we can see, there are also no providers passed to any of the injectors. Usually, when injectors are created, there are providers passed to them so we can ask for specific dependencies in our code. Let’s add some actual providers, to see how the relationships between the injectors affect dependency instantiation.

+
var injector = Injector.create([
+  { provide: Car, deps: [Engine] },
+  { provide: Engine, deps: [] }
+]);
+var childInjector = Injector.create([], injector);
+var grandChildInjector = Injector.create([
+  { provide: Car, useClass: Convertible, deps: [] }
+], childInjector);
+

The injector tree allows us to define injector providers for a specific component and its children. With the code above, if we ask grandChild for a dependency of type Car we’ll get back an instance of type Convertible, because it defines it’s own provider for that type. However, if we ask for a dependency of type Engine, we simply get an instance of the class Engine, because grandChild will ask it’s parent injector (recursively) until an injector has providers defined for that type. If this is entirely new to you, all this has been covered in our last article on DI.

+

Okay, this sounds all very powerful but where does this host thing come into play? Let’s get back to the original code with our three nested components. <component-two> and <component-three> are both children of <component-one>. However, we don’t know yet what’s inside of our components themselves. In Angular, a component always has a view. A component’s view can be in a way encapsulated, this is due to the fact that, since 2.x, Angular supports Shadow DOM.

+

For example, here’s what the view of <component-one> could look like:

+
<h1>Component 1</h1>
+
+<ng-content></ng-content>
+

As we can see, the view of a component is just yet another DOM tree. If we configure Angular accordingly, this DOM tree can be Shadow DOM. That’s also why we have an <ng-content> tag there. It’s Angular’s implementation of content insertion points, which is another Shadow DOM feature.

+

Even though we don’t use Shadow DOM, a component still comes with it’s own view that is kind of hidden behind the component itself. This is what makes every component in Angular a host of a view. In fact, when speaking just about Shadow DOM, we always need a host element to create a shadow dom for it.

+

Okay, but how is that related to DI?

+

That’s a good question! We’ve now seen a couple of times that an injector is always looking up a dependency on it’s parent injector in case it doesn’t have providers for the requested type. That parent injector does pretty much the same until we finally get our dependency. When we think in components, that means that a component’s injector will lookup up a dependency even across boundaries.

+

To make things a bit more clear, let’s say we have a component <video-player> which comes with the following view.

+
<video-screen></video-screen>
+<video-controls>
+  <play-button></play-button>
+  <pause-button></pause-button>
+</video-controls>
+

Our <video-player> component consists of a couple of other components. Let’s say that the injector of <video-player> comes with providers for a PlayerService:

+
@Component({
+  selector: 'video-player',
+  providers: [
+    PlayerService // shorthand for { provide: PlayerService, deps: [] }
+  ]
+})
+class VideoPlayer {
+  ...
+}
+

PlayerService is used by the component’s view components (<play-button>, <pause-button>), to play and pause a video respectively. In order to get an instance of PlayerService, we’d need to inject it like this:

+
@Component({ ... })
+class PlayButton {
+  constructor(playerService: PlayerService) {
+
+  }
+}
+

Since we have a provider for PlayerService defined in VideoPlayer, its injector will return an instance accordingly and everything works as expected, even if <play-button> doesn’t know anything about that provider. However, in case VideoPlayer wouldn’t define that provider, the lookup will go on and on (even outside the <video-player> component) until either some other component has such a provider, or an error is thrown.

+

This can be problematic. Just imagine someone uses our code with another <awesome-player> component instead, and it doesn’t have that provider. Our code could end up getting an instance of PlayerService that it actually shouldn’t get. What we need is a way to somehow make sure, that we always get an instance of PlayerService provided by the host video component (wether it’s <video-player>, <awesome-player> or anything else).

+

Restricting dependency lookup

+

Luckily, this is covered by Angular’s dependency injection system. If we need to ask for a dependency and want to make sure that the lookup ends with the current component’s host, we can use the @Host decorator. Here’s our <play-button> component rewritten with the lookup constraint:

+
@Component({ ... })
+class PlayButton {
+  constructor(@Host() playerService: PlayerService) {
+
+  }
+}
+

If you don’t know what it’s about with these decorators, you might want to read our article on annotations and decorators. Now we ensured that PlayerService instance is always instatiated by our component’s host, which is currently our VideoPlayer component.

+

Dependency Visibility

+

Okay cool, we now know what @Host is and why we need it. But we didn’t talk about the other thing that Angular’s DI introduces yet - dependency visibility. So what is this visibility we’re talking about here? Well, as we learned, we can use the providers property in a @Component decorator to define providers for its injector. However, it turns out that there’s another property viewProviders that basically allows us to do the same thing. What’s the difference between those two then?

+

viewProviders allows us to define injector providers that are only available for a component’s view. Let’s take a closer look at what that means by using our <video-player> component. Our <video-player> component has its own view with its own components. So usually, we would use that component just like this:

+
<video-player><video-player>
+

But let’s imagine, we change the API and <video-player> expects a child element, that implements a video component (whatever that looks like). So we go ahead an build a <custom-video> component that does exactly that and we use it as a child of <video-player> so it can do it’s job with it:

+
<video-player>
+  <custom-video></custom-video>
+</video-player>
+

Now <video-player> has a child element (with it’s own injector) that it needs to work. Note that this child is part of the Light DOM rather than the video player component’s Shadow DOM (emulation). Next, we realise that <custom-video> needs something of type VideoService in order to work correctly, so we inject it accordingly:

+
@Component({ ... })
+class CustomVideo {
+  constructor(videoService: VideoService) {
+
+  }
+}
+

We know that, if <custom-video> ask its injector for a dependency, the injector will look up the dependency in it’s injector tree if it doesn’t have a provider for that type, until it gets the requested instance. This is quite cool, but now imagine that <video-player> has its own provider for the type VideoService, because it needs a very specific instance for it’s view, in order to work:

+
@Component({
+  selector: 'video-player',
+  providers: [
+    PlayerService,
+    { provide : VideoService, useClass : SpecificVideoService, deps: [] }
+  ]
+})
+class VideoPlayer {
+  ...
+}
+

What now happens is, that <custom-video> would get an instance of SpecificVideoService but it actually needs an instance of VideoService. However, due to the lookup that happens in the injector tree, the provider defined in <video-player> is the next one that is available. How can we get around that? This is exactly where viewProviders come in. With viewProviders we can tell the DI system very specifically, which providers are available to which child injectors (Light DOM or Shadow DOM).

+

To make our code work as expected, all we have to do is to make the VideoService provider of <video-player> explicitly available only for its view:

+
@Component({
+  selector: 'video-player',
+  providers: [
+    PlayerService
+  ],
+  viewProviders: [
+    { provide: VideoService, useClass: SpecificVideoService, deps: [] }
+  ]
+})
+class VideoPlayer {
+  ...
+}
+

Now, whenever a component of <video-player>’s view asks for something of type VideoService, it’ll get an instance of SpecificVideoService as expected. Other child components from the outside world that ask for the same type however, won’t see this provider and will continue with the lookup in the injector tree. Which means <custom-video> now gets an expected instance from another parent injector without even knowing that <video-player> actually introduces its own provider.

+

View Providers are also only available in components, not in directives. That’s simply because a directive doesn’t have its own view.

+

Conclusion

+

Angular’s DI is very powerful and doesn’t only cover the common needs when it comes to a decent dependency injection system. It even implements specific use cases for injector trees that are used in conjunction with DOM trees, which could also be encapsulated. I hope this article made clear why we have providers, viewProviders and @Host.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/09/03/forward-references-in-angular-2.html b/angular/2015/09/03/forward-references-in-angular-2.html new file mode 100644 index 000000000..211ce9baf --- /dev/null +++ b/angular/2015/09/03/forward-references-in-angular-2.html @@ -0,0 +1,211 @@ +Forward references in Angular | Articles by thoughtram

Angular

Forward references in Angular

In our article on Dependency Injection in Angular we explored what dependency injection actually is, and how it is implemented in the Angular framework. If you haven’t read that article yet, I highly recommend you doing so, since this article is based on it.

+

In a another article we even learned about host and visibility of dependencies as another aspect of Angular’s DI system. But that doesn’t mean that we’ve already discovered all features of the machinery yet. In this article we’ll take a look at forward references. Another tiny, yet useful feature of the DI system in Angular.

+

Understanding the problem

+

As a small recap, here we have an AppComponent that relies on DI to get a NameService injected. As we are using TypeScript, all we need to do is to annotate our constructor parameter nameService with the NameService type. This gives Angular all the relevant info to correctly resolve the dependency at runtime.

+

app.ts

+
import { Component } from '@angular/core';
+import { NameService } from './name.service';
+
+@Component({
+  selector: 'my-app',
+  template: '<h1>Favourite framework: {{ name }}</h1>'
+})
+class AppComponent {
+  name: string;
+
+  constructor(nameService: NameService) {
+    this.name = nameService.getName();
+  }
+}
+

nameService.ts

+
export class NameService {
+  getName () {
+    return "Angular";
+  }
+}
+

This works well, but let’s see what happens when we inline the contents of nameService.ts directly in app.ts. In this case, you probably wouldn’t want to do that but bear with me as I’m trying to make my point.

+
import { Component } from '@angular/core';
+
+@Component({
+  selector: 'my-app',
+  template: '<h1>Favourite framework: {{ name }}</h1>'
+})
+class AppComponent {
+  name: string;
+
+  constructor(nameService: NameService) {
+    this.name = nameService.getName();
+  }
+}
+
+class NameService {
+  getName () {
+    return "Angular";
+  }
+}
+

When we try to run this code we notice that it stopped working. In my case, I wasn’t even able to get an error reported to the console which I assume boils down to some glitch with debugging TypeScript code with source maps. Anyways, when we use the debuggers “Pause on exceptions” feature we can follow the rabbit into it’s hole somewhere deep down inside the Angular framework.

+

Cannot resolve all parameters for AppComponent(undefined). Make sure they all have valid type or annotations.

+

Ok, this gives us a little hint. It seems NameService is undefined in the constructor of AppComponent. This makes sense if you look at the flow of the code because we already used NameService in the constructor of AppComponent before we actually declared it. But on the other hand, using regular ES5 constructor functions that would be totally valid because function declarations get hoisted to the top by the JavaScript interpreter behind the scenes. And then, aren’t ES2015 classes just sugar on top of regular ES5 functions after all?

+

Let’s see what happens when we move NameService to the top so that it’s declared before it’s first usage.

+
import { Component } from '@angular/core';
+
+class NameService {
+  getName () {
+    return "Angular";
+  }
+}
+
+@Component({
+  selector: 'my-app',
+  template: '<h1>Favourite framework: {{ name }}</h1>'
+})
+class AppComponent {
+  name: string;
+
+  constructor(nameService: NameService) {
+    this.name = nameService.getName();
+  }
+}
+

Ok, this seems to work just fine. But why doesn’t the JavaScript interpreter do that for us in the first place as it does for regular ES5 constructor functions?

+

Classes aren’t hoisted for a good reason

+

Let’s step back from Angular for a moment in order to understand the bare mechanics of the JavaScript language in this regard.

+

The JavaScript interpreter doesn’t hoist class declarations because it may lead to unsound behavior when we have a class that uses the extend keyword to inherit from something. In particular, when it inherits from an expression which is absolutely valid.

+

Consider this ES6 code:

+
class Dog extends Animal {
+
+}
+
+function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+var defaultMove = "moving";
+
+var dog = new Dog();
+dog.move();
+

This alerts moving just fine because what happens behind the scenes is that the JavaScript interpreter restructures the code to this.

+
var defaultMove, dog;
+
+function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+class Dog extends Animal {
+
+}
+
+defaultMove = "moving";
+
+dog = new Dog();
+dog.move();
+

However, try making Animal an expression rather than a function declaration.

+
class Dog extends Animal {
+
+}
+
+var Animal = function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+var defaultMove = "moving";
+
+var dog = new Dog();
+dog.move();
+

Again, this will be hoisted but now it becomes this.

+
var Animal, defaultMove, dog;
+
+class Dog extends Animal {
+
+}
+
+Animal = function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+defaultMove = "moving";
+
+dog = new Dog();
+dog.move();
+

At the point where class Dog extends Animal is interpreted Animal is actually undefined and we get an error. We can easily fix that by moving the Animal expression before the declaration of Dog.

+
var Animal = function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+class Dog extends Animal {
+
+}
+
+var defaultMove = "moving";
+
+var dog = new Dog();
+dog.move();
+

This works just fine again. Now think about what would actually happen if the JavaScript interpreter hoisted Dog just like a regular ES5 constructor function? We would end up with this code:

+
var Animal, defaultMove, dog;
+
+// Dog is now hoisted above `Animal = function Anim...`
+class Dog extends Animal{
+
+}
+
+Animal = function Animal() {
+  this.move = function () {
+    alert(defaultMove);
+  }
+}
+
+defaultMove = "moving";
+
+dog = new Dog();
+dog.move();
+

Now that Dog is hoisted to the top the code breaks at the moment where the extends Animal is interpreted because Animal is undefined at that moment. The important thing to note here is that the extends part has to be evaluated at the right point in time. Therefore classes aren’t hoisted.

+

So the class must always be declared before it’s usage?

+

Ok, now that we understood why classes aren’t hoisted what does that mean for our earlier Angular example where we had to move the NameService to the very top? Is this the only way to get things working?

+

Turns out there is a solution we can reach for. Instead of annotating our nameService parameter with the NameService type which we learned evaluates to undefined at this point in time, we can use the @Inject annotation in conjunction with the forwardRef function as demonstrated here.

+
import {Component, Inject, forwardRef} from '@angular/core';
+
+@Component({
+  selector: 'my-app',
+  template: '<h1>Favourite framework: {{ name }}</h1>'
+})
+class AppComponent {
+  name: string;
+
+  constructor(@Inject(forwardRef(() => NameService)) nameService) {
+    this.name = nameService.getName();
+  }
+}
+
+class NameService {
+  getName () {
+    return "Angular";
+  }
+}
+

What forwardRef does is, it takes a function as a parameter that returns a class. And because this function isn’t immediately called but instead is called after NameService is declared it is safe to return NameService from it. In other words: At the point where () => NameService runs NameService isn’t undefined anymore.

+

Conclusion

+

The described scenario isn’t something that one has to deal with too often. This only becomes a problem when we want to have a class injected that we created in the same file. Most of the time we have one class per file and import the classes that we need at the very top of the file so we won’t actually suffer from the fact that classes aren’t hoisted.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html b/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html new file mode 100644 index 000000000..4639b265e --- /dev/null +++ b/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html @@ -0,0 +1,147 @@ +Understanding @Injectable in Angular | Articles by thoughtram

Angular

Understanding @Injectable in Angular

If you’re following our articles on Dependency Injection in Angular, you know how the DI system in Angular works. It takes advantage of metadata on our code, added through annotations, to get all the information it needs so it can resolve dependencies for us.

+

Angular applications can basically be written in any language, as long as it compiles to JavaScript in some way. When writing our application in TypeScript, we use decorators to add metadata to our code. Sometimes, we can even omit some decorators and simply rely on type annotations. However, it turns out that, when it comes to DI, we might run into unexpected behaviour when injecting dependencies into services.

+

This article discusses what this unexpected problem is, why it exists and how it can be solved.

+

Injecting Service Dependencies

+

Let’s say we have a simple Angular component which has a DataService dependency. It could look something like this:

+
@Component({
+  selector: 'my-app',
+  template: `
+    <ul>
+      <li *ngFor="let item of items">{{item.name}}</li>
+    </ul>
+  `
+})
+class AppComponent {
+  items:Array<any>;
+  constructor(dataService: DataService) {
+    this.items = dataService.getItems();
+  }
+}
+

DataService on the other hand is a simple class (because that’s what a service in Angular is), that provides a method to return some items.

+
class DataService {
+  items:Array<any>;
+
+  constructor() {
+    this.items = [
+      { name: 'Christoph Burgdorf' },
+      { name: 'Pascal Precht' },
+      { name: 'thoughtram' }
+    ];
+  }
+
+  getItems() {
+    return this.items;
+  }
+}
+

Of course, in order to actually be able to ask for something of type DataService, we have to add a provider for our injector. We can do that by adding a provider to our component.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <ul>
+      <li *ngFor="let item of items">{{item.name}}</li>
+    </ul>
+  `,
+  providers: [DataService]
+})
+...
+

Until now there’s nothing new here. If this is new to you, you might want to read our article on Dependency Injection in Angular first.

+

So where is the problem? Well, the problem occurs as soon as we try to inject a dependency into our service. We could for example use Http in our DataService to fetch our data from a remote server. Let’s quickly do that. First, we need to import Angular’s HttpModule into our application module.

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { HttpModule } from '@angular/http';
+
+@NgModule({
+  imports: [BrowserModule, HttpModule],
+  declarations: [AppComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

Angular’s http module comes with all the providers we need to hook up some http action in our service. Next, we need to inject an instance of Http in our service to actually use it.

+
import { Http } from '@angular/http';
+
+class DataService {
+  items:Array<any>;
+
+  constructor(http:Http) {
+    ...
+  }
+  ...
+}
+

Boom. This thing is going to explode. As soon as we run this code in the browser, we’ll get the following error:

+
Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.
+

It basically says that it can’t resolve the Http dependency of DataService because Angular doesn’t know the type and therefore, no provider that can be used to resolve the dependency. Uhm.. wait what? Didn’t we put the type in the constructor?

+

Yea, we did. Unfortunately it turns out this is not enough. However, obviously it does work when we inject DataService in our AppComponent. So what’s the problem here? Let’s take a step back and recap real quick where the metadata, that Angular’s DI need, comes from.

+

In our article on the difference between decorators and annotations we learned that decorators simply add metadata to our code. If we take our AppComponent, once decorated and transpiled, it looks something like this (simplified):

+
function AppComponent(myService) {
+  ...
+}
+
+AppComponent = __decorate([
+  Component({...}),
+  __metadata('design:paramtypes', [DataService])
+], AppComponent);
+

We can clearly see that AppComponent is decorated with Component, and some additional metadata for paramtypes. The paramtypes metadata is the one that is needed by Angular’s DI to figure out, for what type it has to return an instance.

+

This looks good. Let’s take a look at the transpiled DataService and see what’s going on there (also simplified).

+
DataService = (function () {
+  function DataService(http) {
+    ...
+  }
+  return DataService;
+})();
+

Oops. Apparently we don’t have any metadata at all here. Why is that?

+

TypeScript generates metadata when the emitDecoratorMetadata option is set. However, that doesn’t mean that it generates metadata blindly for each and every class or method of our code. TypeScript only generates metadata for a class, method, property or method/constructor parameter when a decorator is actually attached to that particular code. Otherwise, a huge amount of unused metadata code would be generated, which not only affects file size, but it’d also have an impact on our application runtime.

+

That’s also why the metadata is generated for AppComponent, but not for DataService. Our AppComponent does have decorators, otherwhise it’s not a component.

+

Enforcing Metadata Generation

+

So how can we enforce TypeScript to emit metadata for us accordingly? One thing we could do, is to use DI decorators provided by the framework. As we learned in our other articles on DI, the @Inject decorator is used to ask for a dependency of a certain type.

+

We could change our DataService to something like this:

+
import { Inject } from '@angular/core';
+import { Http } from '@angular/http';
+
+class DataService {
+  items:Array<any>;
+
+  constructor(@Inject(Http) http:Http) {
+    ...
+  }
+  ...
+}
+

Problem solved. In fact, this is exactly what @Inject is for when not transpiling with TypeScript. If we take a look at the transpiled code now, we see that all the needed metadata is generated (yeap simplified).

+
function DataService(http) {
+}
+DataService = __decorate([
+  __param(0, angular2_1.Inject(Http)), 
+  __metadata('design:paramtypes', [Http])
+], DataService);
+

However, now we have this Angular machinery in our code and unfortunately, we won’t entirely get rid of it. We can do a little bit better though. Remember that we said metadata is generated, if decorators are attached to our code?

+

We can basically put any decorator on our code, as long as it’s either attached to the class declaration, or to the constructor parameter. In other words, we could remove @Inject again and use something else that we put on the class, because that will cause TypeScript to emit metadata for the constructor parameters too.

+

Of course, putting just anything that is a decorator on a class doesn’t sound really appropiate. Luckily, Angular comes with yet another decorator we can use. @Injectable is normally used for Dart metadata generation. It doesn’t have any special meaning in TypeScript-land, however, it turns out to be a perfect fit for our use case. We don’t have to build a new one ourselves, and the name also kind of makes sense.

+

All we have to do is to import it and put it on our DataService like this:

+
import { Injectable } from '@angular/core';
+import { Http } from '@angular/http';
+
+@Injectable()
+class DataService {
+  items:Array<any>;
+
+  constructor(http:Http) {
+    ...
+  }
+  ...
+}
+

Again, this will just enforce TypeScript to emit the needed metadata, the decorator itself doesn’t have any special meaning here. This seems to be currently the best option we have to solve the illustrated problem.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/10/24/upgrading-apps-to-angular-2-using-ngupgrade.html b/angular/2015/10/24/upgrading-apps-to-angular-2-using-ngupgrade.html new file mode 100644 index 000000000..21f2b5d44 --- /dev/null +++ b/angular/2015/10/24/upgrading-apps-to-angular-2-using-ngupgrade.html @@ -0,0 +1,235 @@ +Upgrading Angular apps using ngUpgrade | Articles by thoughtram

Angular

Upgrading Angular apps using ngUpgrade

Upgrading an existing AngularJS application to Angular >= 2.x is surely one of the most interesting topics when it comes to Angular. A long time it has been unclear what a dedicated upgrade path will actually look like, since Angular was still in alpha state and APIs weren’t stable yet, which makes it hard to “assume” where things will go and what’s the best way to get there.

+

Earlier this year however, the Angular team has made an official announcement in which they talk about what are the available upgrade strategies and what things of both frameworks have to interoperate in order to run them side-by-side on the same website. While the blog post is rather a kind of birds-eye view where no code is shown, a dedicated design document has been created that gives a more concrete idea on what the APIs will look like.

+

Meanwhile, first implementations of ngUpgrade have landed in the code base and it’s time to start digging into it. In this article we’re going to explore what we can do to prepare for an upgrade, and of course how we eventually use ngUpgrade to upgrade our Angular application.

+

Why upgrade?

+

One thing that seems to be a bit left out when people get scared that they can’t upgrade to Angular >= 2.x for various reasons, is to think about if an upgrade is needed in the first place. Of course, Angular >= 2.x is the next major version of the framework and it will surely be the version we want to go with when building web applications in the future.

+

However, that doesn’t mean that our existing Angular 1 applications aren’t good enough anymore to survive the next generation of the web. It’s not that once Angular 2.x is out, our Angular 1 applications immediately stop working. We still have a massive amount of websites out there that seem to be outdated, old and slow. Believe it or not, even if those websites are over 10 years old, they still work. That’s the nice thing about the web, nothing is as backwards compatible because no one wants to break the web, right?

+

So before we think about going through the process upgrading we should really ask ourselves why we want to do it and if the applications we’ve built so far are really in a state that they need this upgrade.

+

Nonetheless there are some very strong arguments why one wants to upgrade and here are just a few of them:

+
    +
  • Better Performance - Angular comes with a way faster change detection, template precompilation, faster bootstrap time, view caching and plenty other things that make the framework freakin’ fast.
  • +
  • Server-side Rendering - The next version of Angular has been split up into two parts, an application layer and a render layer. This enables us to run Angular in other environments than the browser like Web Workers or even servers.
  • +
  • More powerful Templating - The new template syntax is statically analyzable as discussed here, removes many directives and integrates better with Web Components and other elements.
  • +
  • Better Ecosystem - Of course, at the time of writing this article, this is not true. But the Angular ecosystem will eventually be better and more interesting to us in the future.
  • +
+

There are surely more reasons why Angular >= 2.x is better than Angular 1.x, but keep in mind that we’re talking about the motivation for upgrade here. Let’s talk about how we can prepare for an actual upgrade.

+

Changes in Angular

+

In order to upgrade, we need to understand in what ways Angular >= 2.x is different. Unfortunately, covering the bigger differences between both version of the framework is out of the scope of this article. However, if you’re entirely new to Angular, you might want to checkout our project Exploring Angular and get your feet wet.

+

Here’s a list of changes that are crucial when thinking about upgrading:

+
    +
  • Components - Components are the new building blocks when creating applications with Angular. Almost everything is a component, even our application itself.
  • +
  • Inputs/Outputs - Components communicate via inputs and outputs, if they run in the Browser, these are element properties and events. Our article on demystifying Angular’s Template syntax explains how they work.
  • +
  • Content Projection - Basically the new transclusion, but more aligned with the Web Components standard.
  • +
  • Dependency Injection - Instead of having a single injector for our entire application, in Angular each component comes with its own injector. We have a dedicated article on that too.
  • +
+

Of course, there are way more things in Angular that will change or be added to the framework, such as Routing, Forms, the Http layer and more.

+

How do we get there?

+

After doing tons of research at thoughtram with Christoph we realised, that the entire upgrade process can basically be categorized in two phases: Preparation and Upgrade.

+

+

Preparation

+

This is the phase that we could start off today. What can we do today to make the upgrade process later on easier? This includes several things like how we structure our application, which tools can we use or maybe even a language upgrade.

+

Upgrade

+

The phase of running both frameworks side-by-side. This is where ngUpgrade comes into play to make A1 and A2 components interoperable. Keep in mind that the goal of this phase is to stay in it as little as possible, since running both frameworks on the same website is surely not ideal.

+

Preparing for upgrade

+

Let’s discuss what we can do today to prepare for an actual upgrade.

+

Layer application by feature or component

+

Oldie but goldie. We still see applications using a project structure where all directives go into app/directives, services go into app/services, controllers into app/controllers and so on and so forth. You get the idea. While this totally works in smaller applications, it doesn’t really take us far when it comes to upgrading. We might want to upgrade component by component. Having an application layered by type rather than by feature/component, makes it harder to extract parts of the code base to upgrade it.

+

We should rather go with something like:

+
app
+|- components
+  |- productDetail
+  | |- productDetail.js
+  | |- productDetail.css
+  | |- productDetail.html
+  | |- productDetail.spec.js
+  |- productList
+  |- checkout
+  |- wishlist
+

This structure allows us to take e.g. productDetail and upgrade it to Angular >= 2.x to integrate it back into the application later on. We don’t have to worry if there’s anything else related to productDetail in the code base that we might forget, because everything is in one place.

+

Use .service() instead of .factory()

+

If we plan to not only upgrade the framework, but also the language in which we’re writing our application, we should definitely consider to use .service() instead of .factory() in all the cases where a service can be potentially a class. In Angular, a service is also just a class, so this seems like a logical thing to do. For more information on why .service() might be a better fit, read this article.

+

Write new components in ES2015 or TypeScript

+

This is an interesting one. When we saw Angular code the very first time, some of us were scared because all of a sudden there were classes and decorators. Despite the fact that we don’t have to write our Angular apps in TypeScript (as explained in this article), ES2015 is the next standardized version, which means we will write it sooner or later anyways.

+

We wrote about how to write Angular in ES2015 today and if we do plan to upgrade but can’t do it right now, we should definitely write our new components in ES2015 or TypeScript.

+

That being said, it doesn’t really make sense to upgrade existing code to ES2015 or TypeScript. Even though it seems to be a logical step in preparation for upgrade, it doesn’t help us in any way to take the existing and large code base and upgrade it to ES2015 or TypeScript first, before we upgrade the application to Angular >=2.x.

+

If we have to upgrade to Angular 2.x, it probably makes more sense to just rewrite component by component, without touching the existing code base. But this of course depends on how big our application is.

+

Again, this is just a language upgrade and it doesn’t really help with the upgrade itself, however, it helps us and our team to get used to the new languages features as we’re building components with it.

+

Use decorators in Angular 1?

+

Of course, we can take our code base closer to what Angular 2.x code would look like, by upgrading our language to TypeScript and use e.g. Decorators that have specifically been created for Angular 1. There are plenty community projects out there, some of them are a1atscript, angular2-now, angular-decorators and ng-classy.

+

They try solve the same problem, which is adding semantically useful decorators to Angular 1. But do they really help? I don’t think so. They might improve the developer experience because all of a sudden we can use nice decorators that generate code for us, however, they don’t help when it comes to upgrading an application to Angular >= 2.x. One project, ng-forward, tries to make the exact same Angular syntax available in Angular 1.x.

+

This can be helpful to some extent since you and your team are getting familiar with how to write apps in Angular 2.x while writing Angular 1.x code. On the other hand, it could also be confusing when trying to squeeze Angular 2.x concepts and syntax into the Angular 1.x world. We’ll see how practical it is once projects are starting to use it.

+

Upgrade Strategies

+

Now that we know what we can do to prepare for an upgrade, let’s take a look at the different upgrade strategies available to see which one makes more sense to us.

+

There are basically two strategies:

+
    +
  • Big Bang - Start a spike in Angular >= 2.x and replace entire app once done
  • +
  • Incremental - Upgrade existing app once service or component at a time
  • +
+

Which one should we use?

+

This really depends! If our application is rather small a big bang rewrite is probably the easiest and fastest way to upgrade. If our application is rather large and it’s deployed continuesly, we can’t just upgrade the whole thing at once. We need a way to do it step by step, component by component, service by service. This is where the incremental upgrade comes into play.

+

In the end it’s really a matter of how much time we have available to process the upgrade. We will focus on incremental upgrade, since this is what the majority of developers want to understand and see how it works.

+

Upgrading using ngUpgrade

+

In order to run both frameworks side-by-side and make components interoperable, the Angular projects comes with a module ngUpgrade. The module basically acts as an adapter facade, so we don’t really feel that there are two frameworks running side-by-side.

+

For this to work, four things need to interoperate:

+
    +
  • Dependency Injection - Exposing Angular services into Angular 1.x components and vice-versa.
  • +
  • Component Nesting - Angular 1 directives can be used in Angular 2.x components and Angular 2.x components can used Angular 1 directives
  • +
  • Content Projection / Transclusion - Angular 1 components transclude Angular 2.x components and Angular 2.x component project Angular 1 directives
  • +
  • Change Detection - Angular 1 scope digest and change detectors in Angular >= 2.x are interleaved
  • +
+

With these four things being interoperable, we can already start upgrading our applications component by component. Routing is another part that can help but is not necessarily mandatory, since we can totally stick with any Angular 1 routing system while upgrading.

+

The typical upgrade process

+

Here’s what a typical upgrade process would look like:

+
    +
  • Include Angular and upgrade module
  • +
  • Pick component to upgrade and change its controller and template Angular 2.x syntax (this is now an Angular 2.x component)
  • +
  • Downgrade Angular 2.x component to make it run in Angular 1.x app
  • +
  • Pick a service to upgrade, this usually requires little amount of change (especially if we’re on ES2015)
  • +
  • Repeat step 2 and 3 (and 4)
  • +
  • Replace Angular 1 bootstrap with Angular 2.x bootstrap
  • +
+

Let’s use ngUpgrade to upgrade our components to Angular 2.x!

+

Bootstrapping with ngUpgrade

+

The first thing we need to do is to upgrade our Angular 1 application with ngUpgrade. Whenever we upgrade an Agular app, we always have an Angular 1.x module being bootstrap at root level. This means, during the process of upgrade, Angular components are always bootstrap inside Angular 1.x components.

+

Let’s start with an app we want to upgrade;

+
var app = angular.module('myApp', []);
+

Plain old Angular 1 module. Usually, this module is bootstrapped using the ng-app attribute, but now we want to bootstrap our module using ngUpgrade. We do that by removing the ng-app attribute from the HTML, create an ngUpgrade adapter from the upgrade module, and call bootstrap() on it with myApp as module dependency:

+
import { UpgradeAdapter } from '@angular/upgrade';
+
+var adapter = new UpgradeAdapter();
+var app = angular.module('myApp', []);
+
+adapter.bootstrap(document.body, ['myApp']);
+

Cool, our app is now bootstrapped using ngUpgrade and we can start mixing Angular 1.x components with Angular 2.x components. However, in a real world application, you want to create an instance of UpgradeAdapter in a separate module and import it where you need it. This leads to cleaner code when upgrading across your application.

+

Downgrading Angular 2.x components

+

Let’s upgrade our first component to Angular 2.x and use it in our Angular 1.x application. Here we have a productDetail component that needs to be upgraded:

+
app.component('productDetail', () => {
+  bindings: {
+    product: '='
+  },
+  controller: 'ProductDetailController',
+  template: `
+    <h2>{{$ctrl.product.name}}</h2>
+    <p>{{$ctrl.product.description}}</p>
+  `
+});
+

Upgrading this to Angular 2.x looks something like this:

+
@Component({
+  selector: 'product-detail',
+  template: `
+    <h2>{{product.name}}</h2>
+    <p>{{product.description}}</p>
+  `
+})
+class ProductDetail {
+  @Input() product: Product;
+}
+

Note: You might want to define this component in a separate file, for simplicity sake we defined it in place.

+

Perfect! But how do we get this Angular component into our Angular 1.x application? The UpgradeAdapter we’ve created comes with a method downgradeNg2Component(), which takes an Angular component and creates an Angular 1.x directive from it. Let’s use our Angular component in Angular 1.x world.

+
app.directive('productDetail',
+  adapter.downgradeNg2Component(ProductDetail));
+

Yay! That’s it! The adapter will bootstrap this component from within the Angular 1 template where it’s used.

+

But wait, our Angular 2.x component is just an Angular 1.x component eventually? Yes and no. The directive is controlled by Angular 1.x, but the component’s view will be controller by Angular >= 2.x. This means the resulting Angular 1.x components takes advantage of Angular 2.x features and performance.

+

Upgrading Angular 1 components

+

There might be cases where one component has already been upgraded to Angular >= 2.x, but it still uses Angular 1.x directives in its template. ngUpgrade allows us to use Angular 1.x directives in Angular 2.x components by upgrading them using upgradeNg1Component().

+

Let’s say we continued upgrading our application and have a ProductList component like this:

+
@Component({
+  selector: 'product-list',
+  template: `
+    <h2>Product List</h2>
+    <ol>
+      <li *ngFor="let product of products">
+        <product-list-item [product]="product">
+        </product-list-item>
+      </li>
+    </ol>
+  `
+})
+class ProductList {
+  @Input() products: Product[];
+  ...
+}
+

<product-list-item> is a component that hasn’t been ported to Angular 2.x yet and maybe can’t even for some reason. It needs to be upgraded but how do we get there? As you can see, there’s a directives property in the @Component() metadata. This property defines which directives are used in the component’s template. What we need is a way to add <product-list-item> there too.

+

upgradeNg1Component() enables us to do exactly that. It takes the name of a directive that has been registered somewhere on our Angular 1 module and upgrades it to an Angular 2.x component. Here’s what it looks like:

+
@NgModule({
+  imports: [BrowserModule],
+  declarations: [
+    AppComponent,
+    ProductList,
+    adapter.upgradeNg1Component('productListItem')
+  ],
+  ...
+})
+export class AppModule {}
+

All we need to do is to downgrade ProductList item and we can use it right away!

+

Adding Angular Providers

+

Upgrading components is probably the most crucial part in the entire upgrade process. Sometimes however, components have service dependencies which need to work in both worlds too. Luckily, ngUpgrade provides APIs for that.

+

Let’s say our ProductDetail component, already upgraded to Angular >= 2.x, has a ProductService dependency.

+
class ProductService {
+
+}
+
+@Component()
+class ProductDetail {
+  constructor(productService: ProductService) {
+
+  }
+}
+

In Angular >= 2.x, we have to add a provider configuration for the component’s injector, but since we don’t bootstrap using Angular 2.x, there’s no way to do so. ngUpgrade allows us to add a provider using the addProvider() method to solve this scenario.

+
adapter.addProvider(ProductService);
+

That’s all we need to do!

+

Upgrading Angular 1 Providers

+

Let’s say our ProductService depends on another lower level DataService to communicate with a remote server. DataService is already implemented in our Angular 1 application but not yet upgraded to Angular 2.x.

+
class ProductService {
+
+  constructor(@Inject('DataService') dataService) {
+    ...
+  }
+}
+
+app.service('DataService', () => {
+  ...
+});
+

As you can see, we’re using @Inject to specify the provider token for DataService, since we don’t have a DataService type. If this is unclear, you might want to read our articles on DI in Angular.

+

However, there’s no provider for 'DataService' in Angular 2.x world yet. Let’s make it available using upgradeNg1Provider().

+
adapter.upgradeNg1Provider('DataService');
+

Boom! We can make it even better. Let’s assume our Angular 1 service has already been written as class.

+
class DataService {
+
+}
+
+app.service('DataService', DataService);
+

We can use that class as type and token for dependency injection in Angular 2.x. All we have to do, is to upgrade our service with that token.

+
adapter.upgradeNg1Provider('DataService', {asToken: DataService});
+

Now we can inject it using plain old type annotations.

+
@Injectable()
+class ProductService {
+
+  constructor(dataService: DataService) {
+    ...
+  }
+}
+

Note: We added @Injectable() to our service because TypeScript needs at least one decorator to emit metadata for it. Learn more in our article on Injecting Services in Services in Angular.

+

Downgrading Angular 2.x Providers

+

Last but not least, we might need to be able to use Angular 2.x services in Angular 1.x components. Guess what, ngUpgrade comes with a downgradeNg2Provider() method.

+
app.factory('ProductService',
+  adapter.downgradeNg2Provider(ProductService));
+

Conclusion

+

ngUpgrade provides many useful APIs and is a big step forward when it comes to truly upgrading an application from Angular 1 to Angular >= 2.x. At AngularConnect we gave a workshop on upgrading and we’ve open sourced a repository that shows all the steps we’ve been through, from preparation to upgrade. Make sure to check out the steps branch.

+

Hopefully this article made a bit more clear what this whole upgrade story is all about!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/11/16/multiple-transclusion-and-named-slots.html b/angular/2015/11/16/multiple-transclusion-and-named-slots.html new file mode 100644 index 000000000..9d529f261 --- /dev/null +++ b/angular/2015/11/16/multiple-transclusion-and-named-slots.html @@ -0,0 +1,112 @@ +Multiple Transclusion and named Slots | Articles by thoughtram

Angular

Multiple Transclusion and named Slots

With the upcoming final 1.5 release of the AngularJS framework, tons of new features, improvements and bug fixes are right around the corner. One of those features is multiple transclusion via named slots. While transclusion is already a very nice and powerful feature, with the 1.5 release it’s going to be taken to the next level. In this article we’re going to discuss what multiple transclusion is all about and how it helps the framework to align more with the web components technologies.

+

Understanding Transclusion

+

We surely don’t have to make a huge recap on what transclusion is, since there are tons of resources out there in the internet already and most of us are probably very familiar with that feature. However, just to pick up everyone reading this article, here’s what transclusion is (stolen from Wikipedia):

+
+

In computer science, transclusion is the inclusion of part or all of an electronic document into one or more other documents by reference.

+
+

Clear, right? Well, not really.

+

It’s actually way simpler than it sounds. In Angular world, when we build directives, transclusion allows us to take the HTML from the outer world, that is in between our directive tags, and insert it somewhere inside our directive template, which the outside world doesn’t really know about.

+

The easiest way to illustrate that is the <details> element. <details> renders a UI component (in some browers), which we can click on to open and close it.

+
<details>
+  <p>Hey y'all I've put some content here.</p>
+</details>
+

As you can see, we can put some HTML in between the <details> tags and it gets somehow magically projected somewhere else. The thing that makes this possible are Content Insertion Points which are part of the Shadow DOM specification. They allow us to mark places in an element’s template where Light DOM is going to be projected.

+

Angular’s transclusion feature is basically some sort of polyfill for this kind of functionality, however, pretty much implemented in an Angular specific way. It really just works with the framework.

+

We can easily reimplement a <details> element with Angular like this:

+
angular.module('myApp', [])
+
+.directive('ngDetails', function () {
+  return {
+    restrict: 'E',
+    scope: {},
+    transclude: true,
+    template: `
+      <div class="summary" ng-click="open = !open">
+        {{ open ? '&blacktriangledown;' : '&blacktriangleright;' }} Details
+      </div>
+      <div class="content" ng-show="open" ng-transclude></div>
+    `
+  };
+});
+

Setting transclude to true enables transclusion for the directive, whereas ng-transclude in the template tells Angular where to put the HTML from the outside world. Of course, this is a very very simple reimplementation, but it’s really just to demonstrate the point of transclusion.

+

Tero has written an amazing guide on transclusion, if you want to dig deeper on that topic I highly recommend his guide.

+

Even though transclusion is a very neat feature to provide APIs where consumers can hook into, it turns out that there’s at least one drawback. We either take everything or nothing. Whenever we use transclusion, there’s no way to specify what we want to transclude, we always have to take the whole DOM. This is where Shadow DOM and Content Insertion Points really shine.

+

Content Selection and Shadow DOM

+

Shadow DOM uses a <content> tag to specify insertion points. If we’d reimplement the <details> tag with web components technologies, our component’s template could look something like this (simplified):

+
<div class="summary">
+  Details
+</div>
+<div class="content">
+  <content></content>
+</div>
+

This is more or less the equivalent of transclusion in Angular. However, Shadow DOM takes it even further. It allows us to specify what we want to project into our shadow DOM. This is where the select attribute comes into play. Let’s say we’re only interested in projecting <h2> elements, we can update our template with content selection like this:

+
<div class="summary">
+  Details
+</div>
+<div class="content">
+  <content select="h2"></content>
+</div>
+

Super powerful! The specification has even evolved more with another <slot> tag which is a bit more powerful. However, after all it everything boils down to what we’ve seen so far.

+

This is where multiple transclusion comes into play, with Angular 1.5 we can finally do exactly that!

+

Multiple Transclusion

+

Multiple transclusion has been proposed a loooong time ago. In fact, Vojta came up with this over two years ago. Now, thanks to Pete, it’s right here. So let’s get back to our <ng-details> implementation and take a look at it.

+

The <details> tag allows us to configure a “summary” which defaults to "Details". In order to change it, all we have to do is to put a <summary> tag inside the <details> element like this:

+
<details>
+  <summary>Click me!</summary>
+  <p>Hey y'all I've put some content here.</p>
+</details>
+

This we couldn’t do with Angular’s transclusion before, because we can’t just take all the DOM as it is. We would need to take the <summary>, transclude at a specific place in our template, and then we’d need to transclude the rest somewhere else.

+

With multpile transclusion we can totally do that. We just have to extend our directive a tiny little bit (note that we’re using <span> as summary element, but you can use whatever you want):

+
angular.module('myApp', [])
+
+.directive('ngDetails', function () {
+  return {
+    restrict: 'E',
+    scope: {},
+    transclude: {
+      'summarySlot': 'span',
+    },
+    template: `
+      <div class="summary" ng-click="open = !open">
+        {{ open ? '&blacktriangledown;' : '&blacktriangleright;' }} <span ng-transclude="summarySlot"></span>
+      </div>
+      <div class="content" ng-show="open" ng-transclude></div>
+    `
+  };
+});
+

We basically made two changes:

+
    +
  • We changed the transclude property to an object which specifies the transclusion slots. The key is the name of a element or directive in camel-case slot we can later use in our template, the value the name of an element or directive in camel-case we want to transclude.
  • +
  • We replaced the default "Details" summary with an element that has ng-transclude="summarySlot". As you can see, ng-transclude now excepts a string which is the name of a transclusion slot that we’ve defined earlier.
  • +
+

The original ng-transclude stays as is, since it simply takes the rest to be transcluded. We can now use our <ng-details> component like this:

+
<ng-details>
+  <span>Details</span>
+  <p>More content here</p>
+</ng-details>
+

We can even make transclusion slots optional by prefixing the element tag name with a ? like this:

+
transclude: {
+  'summarySlot': '?span'
+}
+

This is already very cool, but our ng-details directive still lacks one specific behaviour. If we don’t specify a <summary>, <details> defaults to "Details". Our component however, doesn’t do this. We can provide a fallback summary by simply putting something into the DOM where other elements will be transcluded to:

+
<div class="summary" ng-click="open = !open">
+  {{ open ? '&blacktriangledown;' : '&blacktriangleright;' }} <span ng-transclude="summarySlot">Details</span>
+</div>
+

Seen what happened? We just put "Details" as text into our span element. This text will be replace with the transcluded DOM, if it is applied.

+

What are you waiting for? Start using multiple transclusion in your directives and design beautful APIs for your consumers!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2015/12/10/ng-message-format-the-unheard-feature-in-angular.html b/angular/2015/12/10/ng-message-format-the-unheard-feature-in-angular.html new file mode 100644 index 000000000..344470ad5 --- /dev/null +++ b/angular/2015/12/10/ng-message-format-the-unheard-feature-in-angular.html @@ -0,0 +1,94 @@ +ngMessageFormat - Angular's unheard feature | Articles by thoughtram

Angular

ngMessageFormat - Angular's unheard feature

Angular 1.5 is pretty much around the corner and with a new release, new fixes, improvements and features are added to the framework. While this is awesome and we’re all excited about it, it seems like we’re forgetting about all the nice things we already have.

+

+ + + tweet + +

+

A few days ago, we asked on twitter who’s interested to learn about an unheard feature in Angular. Based on the reactions to that tweet, it’s quite obvious that you all are and that’s awesome! So what is this unheard feature we’re talking about? If you read this article you can surely tell from its title that it’s probably about this thing called ngMessageFormat. We’ll get right into it but first we’d like to make one thing clear:

+

This feature is available since Angular 1.4

+

If you’re on version 1.4 or higher, this feature is already available to use. So, what you learn in the next couple of minutes you can use straight away!

+

Understanding Pluralization and Gender Selection

+

Earlier this year I gave a talk together with Chirayu, a former member of the Angular core team, about how the Angular project is going to solve internationalization and localization in the future. The talk can be watched right here and if you’re more a reader kind of person, we wrote about everything in our article on Angular and i18n - A new world.

+

One thing that is an essential part of i18n, but also a sort of isolated topic at the same time, is pluralization and gender selection. We probably all ran into this at a some point. For example, displaying a notification that says:

+
You have {{numberOfMessages}} new messages.
+

While this works as long as numberOfMessages evaluates to something > 1, it doesn’t fit anymore as soon as we have just a single message. Our template would look something like this:

+
You have 1 new messages.
+

This can easily be solved with the ngSwitch directive, or, in fact Angular comes with an ngPluralize directive that introduces a couple more features (like offset) to make pluralization easy. Here’s an ngPluralize solution for the scenario above:

+
<ng-pluralize count="numberOfMessages"
+              when="{'1': 'You have one new message.',
+                     'other': 'You have {} new messages.'}">
+</ng-pluralize>
+

Pluralization can be hard, especially if we consider that it can vary heavily depending on the language we’re using. While we have “one” and “more” in most of the european language rules, other languages have “one”, “few” and “more”.

+

Another thing that comes into play is gender selection. Depending on a persons gender, we might need to output different text.

+
Send him an invite.
+Send her an invite.
+Send them an invite.
+

This can not be solved with ngPluralize today. Also, what if we have text in HTML attributes that needs to be pluralized as well?

+

Introducing ngMessageFormat

+

Luckily, there’s a standard called ICU Messageformat which tackles pluralization and gender selection properly. In fact, with Messageformat, we can even nest gender selection rules and pluralization rules and vice-versa. But what has this to do with Angular?

+

Well, as part of the effort for the new i18n solution, Angular’s interplation syntax got extended in Angular 1.4. In other words, we can basically overload the expression syntax with ICU Messageformat expressions. All we have to do is to include the ngMessageFormat module.

+

ngMessageFormat can be installed via npm using the following command:

+
$ npm install angular-message-format
+

Once installed and included in our HTML document, we can add it as a module dependency and start using it right away!

+
angular.module('myApp', ['ngMessageFormat']);
+

Pluralization with ngMessageFormat

+

With ngMessageFormat included, we can overload Angular expressions using a comma like this:

+
{{EXPRESSION, TYPE,
+     =VALUE { MESSAGE }
+     ...
+}}
+

Whereas EXPRESSION is the expression that needs to be evaluated, TYPE specifies what we want to do plural or select for pluralization and gender selection respectively. Let’s use this syntax to output our notification, based on numberOfMessages.

+
{{numberOfMessages, plural,
+    =0 { You have no new messages }
+    =1 { You have one new message }
+    other { You have # new messages }
+}}
+

As we can see, # can be used as a placeholder that gets replaced with the actual evaluated value. Another nice thing to notice: We can use still use Angular expressions and filters inside those messages!

+

Gender selection with ngMessageFormat

+

Gender selection uses the exact same syntax. All we have to do is to change the selection type and define messages for each gender:

+
{{genderExpression, select,
+    male { Send him a message. }
+    female { Send her a message. }
+    other { Send them a message. }
+}}
+

Conclusion

+

Unfortunately ngMessageFormat is not very well documented and it didn’t get a lot of love after it has been released. However, it is right there and it wants to be used. Just remember that it allows you to pluralize not only HTML attributes, it even makes nesting of plural and gender selection possible!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html b/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html new file mode 100644 index 000000000..af5129a95 --- /dev/null +++ b/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html @@ -0,0 +1,177 @@ +Taking advantage of Observables in Angular | Articles by thoughtram

Angular

Taking advantage of Observables in Angular

Some people seem to be confused why Angular seems to favor the Observable abstraction over the Promise abstraction when it comes to dealing with async behavior.

+

There are pretty good resources about the difference between Observables and Promises already out there. I especially like to highlight this free 7 minutes video by Ben Lesh on egghead.io. Technically there are a couple of obvious differences like the disposability and lazyness of Observables. In this article we like to focus on some practical advantages that Observables introduce for server communication.

+

The scenario

+

Consider you are building a search input mask that should instantly show you results as you type.

+

If you’ve ever build such a thing before you are probably aware of the challenges that come with that task.

+

1. Don’t hit the search endpoint on every key stroke

+

Treat the search endpoint as if you pay for it on a per-request basis. No matter if it’s your own hardware or not. We shouldn’t be hammering +the search endpoint more often than needed. Basically we only want to hit it once the user has stopped typing instead of with every keystroke.

+

2. Don’t hit the search endpoint with the same query params for subsequent requests

+

Consider you type foo, stop, type another o, followed by an immediate backspace and rest back at foo. That should be just one request with the term foo and not two even if we technically stopped twice after we had foo in the search box.

+

3. Deal with out-of-order responses

+

When we have multiple requests in-flight at the same time we must account for cases where they come back in unexpected order. Consider we first typed +computer, stop, a request goes out, we type car, stop, a request goes out. Now we have two requests in-flight. Unfortunately the request that carries the results for computer comes back +after the request that carries the results for car. This may happen because they are served by different servers. If we don’t deal with such cases properly we may end up showing results for computer whereas the search box reads car.

+

Challenge accepted

+

We will use the free and open wikipedia API to write a little demo.

+

For simplicity our demo will simply consist of two files: app.ts and wikipedia-service.ts. In a real world scenario we would most likely split things further up though.

+

Let’s start with a Promise-based implementation that doesn’t handle any of the described edge cases.

+

This is what our WikipediaService looks like. Despite the fact that the Http/JsonP API still has some little unergonomic parts, there shouldn’t be much of surprise here.

+
import { Injectable } from '@angular/core';
+import { URLSearchParams, Jsonp } from '@angular/http';
+
+@Injectable()
+export class WikipediaService {
+  constructor(private jsonp: Jsonp) {}
+
+  search (term: string) {
+    var search = new URLSearchParams()
+    search.set('action', 'opensearch');
+    search.set('search', term);
+    search.set('format', 'json');
+    return this.jsonp
+                .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
+                .toPromise()
+                .then((response) => response.json()[1]);
+  }
+}
+

Basically we are injecting the Jsonp service to make a GET request against the wikipedia API with a given search term. Notice that we call toPromise in order to get from an Observable<Response> to a Promise<Response>. With a little bit of then-chaining we eventually end up with a Promise<Array<string>> as the return type of our search method.

+

So far so good, let’s take a look at the app.ts file that holds our App Component.

+
// check the plnkr for the full list of imports
+import {...} from '...';
+
+@Component({
+  selector: 'my-app',
+  template: `
+    <div>
+      <h2>Wikipedia Search</h2>
+      <input #term type="text" (keyup)="search(term.value)">
+      <ul>
+        <li *ngFor="let item of items">{{item}}</li>
+      </ul>
+    </div>
+  `
+})
+export class AppComponent {
+  items: Array<string>;
+
+  constructor(private wikipediaService: WikipediaService) {}
+
+  search(term) {
+    this.wikipediaService.search(term)
+                         .then(items => this.items = items);
+  }
+}
+

Not much of a surprise here either. We inject our WikipediaService and expose it’s functionality via a search method to the template. The template simply binds to keyup and calls search(term.value) leveraging Angular’s awesome template ref feature.

+

We unwrap the result of the Promise that the search method of the WikipediaService returns and expose it as a simple Array of strings to the template so that we can have *ngFor loop through it and build up a list for us.

+

Unfortunately this implementation doesn’t address any of the described edge cases that we would like to deal with. Let’s refactor our code to make it match the expected behavior.

+

Taming the user input

+

Let’s change our code to not hammer the endpoint with every keystroke but instead only send a request when the user stopped typing for 400 ms. This is where Observables really shine. The Reactive Extensions (Rx) offer a broad range of operators that let us alter the behavior of Observables and create new Observables with the desired semantics.

+

To unveil such super powers we first need to get an Observable<string> that carries the search term that the user types in. Instead of manually binding to the keyup event, we can take advantage of Angular’s formControl directive. To use this directive, we first need to import the ReactiveFormsModule into our application module.

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { JsonpModule } from '@angular/http';
+import { ReactiveFormsModule } from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, JsonpModule, ReactiveFormsModule]
+  declarations: [AppComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

Once imported, we can use formControl from within our template and set it to the name "term".

+
<input type="text" [formControl]="term"/>
+

In our component we create an instance of FormControl from @angular/form and expose it as a field under the name term on our component.

+

Behind the scenes term automatically exposes an Observable<string> as property valueChanges that we can subscribe to. Now that we have an Observable<string>, taming the user input is as easy as calling debounceTime(400) on our Observable. This will return a new Observable<string> that will only emit a new value when there haven’t been coming new values for 400ms.

+
export class App {
+  items: Array<string>;
+  term = new FormControl();
+  constructor(private wikipediaService: WikipediaService) {
+    this.term.valueChanges
+             .debounceTime(400)
+             .subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));
+  }
+}
+

Don’t hit me twice

+

As we said, it would be a waste of resources to send out another request for a search term that our app already shows the results for. Fortunately Rx simplifies many operations that it nearly feels unnecessary to mention them. All we have to do to achieve the desired behavior is to call the distinctUntilChanged operator right after we called debounceTime(400). Again, we will get back an Observable<string> but one that ignores values that are the same as the previous.

+

Dealing with out-of-order responses

+

Dealing with out of order responses can be a tricky task. Basically we need a way to express that we aren’t interested anymore in results from previous in-flight requests as soon as we are sending out new requests. In other words: cancel out all previous request as soon as we start a new one. As I briefly mentioned in the beginning Observables are disposable which means we can unsubscribe from them.

+

This is where we want to change our WikipediaService to return an Observable<Array<string>> instead of an Promise<Array<string>>. That’s as easy as dropping toPromise and using map instead of then.

+
search (term: string) {
+  var search = new URLSearchParams()
+  search.set('action', 'opensearch');
+  search.set('search', term);
+  search.set('format', 'json');
+  return this.jsonp
+              .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
+              .map((response) => response.json()[1]);
+}
+

Now that our WikipediaSerice returns an Observable instead of a Promise we simply need to replace then with subscribe in our App component.

+
this.term.valueChanges
+           .debounceTime(400)
+           .distinctUntilChanged()
+           .subscribe(term => this.wikipediaService.search(term).subscribe(items => this.items = items));
+

But now we have two subscribe calls. This is needlessly verbose and often a sign for unidiomatic usage. +The good news is, now that search returns an Observable<Array<string>> we can simply use flatMap to project our Observable<string> into the desired Observable<Array<string>> by composing the Observables.

+
this.term.valueChanges
+         .debounceTime(400)
+         .distinctUntilChanged()
+         .flatMap(term => this.wikipediaService.search(term))
+         .subscribe(items => this.items = items);
+

You may be wondering what flatMap does and why we can’t use map here. The answer is quite simple. The map operator expects a function that takes a value T and returns a value U. For instance a function that takes in a string and returns a Number. Hence when you use map you get from an Observable<T> to an Observable<U>. However, our search method produces an Observable<Array> itself. So coming from an Observable<string> that we have right after distinctUntilChanged, map would take us to an Observable<Observable<Array<string>>. That’s not quite what we want.

+

The flatMap operator on the other hand expects a function that takes a T and returns an Observable<U> and produces an Observable<U> for us.

+

NOTE: That’s not entirely true, but it helps as a simplification.

+

That perfectly matches our case. We have an Observable<string>, then call flatMap with a function that takes a string and returns an Observable<Array<string>>.

+

So does this solve our out-of-order response issues? Unfortunately not. So, why am I bothering you with all this in the first place? Well, now that you understood flatMap just replace it with switchMap and you are done.

+

What?! You may be wondering if I’m kidding you but no I am not. That’s the beautify of Rx with all it’s useful operators. The switchMap operator is comparable to flatMap in a way. Both operators automatically subscribe to the Observable that the function produces and flatten the result for us. The difference is that the switchMap operator automatically unsubscribes from previous subscriptions as soon as the outer Observable emits new values.

+

Putting some sugar on top

+

Now that we got the semantics right, there’s one more little trick that we can use to save us some typing. Instead of manually subscribing to the Observable we can let Angular do the unwrapping for us right from within the template. All we have to do to accomplish that is to use the AsyncPipe in our template and expose the Observable<Array<string>> instead of Array<string>.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <div>
+      <h2>Wikipedia Search</h2>
+      <input type="text" [formControl]="term"/>
+      <ul>
+        <li *ngFor="let item of items | async">{{item}}</li>
+      </ul>
+    </div>
+  `
+})
+export class App {
+
+  items: Observable<Array<string>>;
+  term = new FormControl();
+
+  constructor(private wikipediaService: WikipediaService) {
+    this.items = this.term.valueChanges
+                 .debounceTime(400)
+                 .distinctUntilChanged()
+                 .switchMap(term => this.wikipediaService.search(term));
+  }
+}
+

And voilà, we’re done. Check out the demos below!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2016/01/07/taking-advantage-of-observables-in-angular2-pt2.html b/angular/2016/01/07/taking-advantage-of-observables-in-angular2-pt2.html new file mode 100644 index 000000000..1c0bc0276 --- /dev/null +++ b/angular/2016/01/07/taking-advantage-of-observables-in-angular2-pt2.html @@ -0,0 +1,117 @@ +Taking advantage of Observables in Angular 2 - Part 2 | Articles by thoughtram

Angular

Taking advantage of Observables in Angular 2 - Part 2

In a previous post we showed how to leverage Observables, and especially their strength of composability to ease complicated async tasks. Today we want to take it one step further.

+

As a recap, we built a simple Wikipedia search demo consisting of a WikipediaService to query a JSONP API.

+
import { Injectable } from '@angular/core';
+import { URLSearchParams, Jsonp } from '@angular/http';
+
+@Injectable()
+export class WikipediaService {
+
+  constructor(private jsonp: Jsonp) {}
+
+  search (term: string) {
+    var search = new URLSearchParams()
+    search.set('action', 'opensearch');
+    search.set('search', term);
+    search.set('format', 'json');
+    return this.jsonp
+                .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
+                .map((response) => response.json()[1]);
+  }
+}
+

We also built an App component that uses this service and applies some Rx gymnastics to tame the user input, prevent duplicate requests and deal with out-of-order responses.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <div>
+      <h2>Wikipedia Search</h2>
+      <input type="text" [formControl]="term"/>
+      <ul>
+        <li *ngFor="let item of items | async">{{item}}</li>
+      </ul>
+    </div>
+  `
+})
+export class App implements OnInit {
+
+  items: Observable<Array<string>>;
+  term = new FormControl();
+
+  constructor(private wikipediaService: WikipediaService) {}
+
+  ngOnInit() {
+    this.items = this.term.valueChanges
+                 .debounceTime(400)
+                 .distinctUntilChanged()
+                 .switchMap(term => this.wikipediaService.search(term));
+  }
+}
+

Thinking ahead we can refactor our code even further and let our API design leverage from the power of Observables.

+

Observables = Promises + Events (in a way!)

+

In a way Observables may be seen as the clever child of Events and Promises. Promises are first class objects that encapsulate the state of an asynchronous operation. But they are for singular operations only. A request is such an operation. You invoke a method, kicking off some async task and get a first class object that eventually will get you to the result of the operation (ignoring error handling for now).

+

Events on the other hand are for async operations that can continue to emit new values for an infinite duration. But Unfortunately they are traditionally not represented in a format that matches the criteria of a first class object. You can’t just pass an event of clicks around that skips every third click for instance.

+

Well, with Observables you can. You get the power of first class objects but without the limitations of singularity.

+

In fact, in a modern .NET language such as F#, which embraces Observables all the way down, every IEvent<T> inherits from IObservable<T>. Angular also went down this path and made EventEmiter<T> implement Observable<T>.

+

Smart service, dumb component

+

With that in mind: wouldn’t it be actually nice if we could save the component from dealing with all these edge cases? What if we just make the debounce duration configureable but let the rest of the complexity be handled by our WikipediaService?

+

To let code speak we can transform our WikipediaService into this.

+
@Injectable()
+export class WikipediaService {
+  constructor(private jsonp: Jsonp) {}
+
+  search(terms: Observable<string>, debounceDuration = 400) {
+    return terms.debounceTime(debounceDuration)
+                .distinctUntilChanged()
+                .switchMap(term => this.rawSearch(term));
+  }
+
+  rawSearch (term: string) {
+    var search = new URLSearchParams()
+    search.set('action', 'opensearch');
+    search.set('search', term);
+    search.set('format', 'json');
+    return this.jsonp
+                .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
+                .map((response) => response.json()[1]);
+  }
+}
+

Notice that the service still exposes the previous api as rawSearch and builds a more clever search API on top of it.

+

This dramatically simplifies our App component.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <div>
+      <h2>Wikipedia Search</h2>
+      <input type="text" [formControl]="term"/>
+      <ul>
+        <li *ngFor="let item of items | async">{{item}}</li>
+      </ul>
+    </div>
+  `
+})
+export class App {
+
+  items: Observable<Array<string>>;
+  term = new FormControl();
+
+  constructor(private wikipediaService: WikipediaService) {
+    this.items = wikipediaService.search(this.term.valueChanges);
+  }
+}
+

See what happened? We just wire together event streams like lego blocks!

+

You can play around with the demo right here. Enjoy!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2016/01/22/understanding-zones.html b/angular/2016/01/22/understanding-zones.html new file mode 100644 index 000000000..31f0a7c5f --- /dev/null +++ b/angular/2016/01/22/understanding-zones.html @@ -0,0 +1,151 @@ +Understanding Zones | Articles by thoughtram

Angular

Understanding Zones

At NG-Conf 2014, Brian gave an excellent talk on zones and how they can change the way we deal with asynchronous code. If you haven’t watched this talk yet, give it a shot, it’s just ~15 minutes long. APIs might be different nowadays, but the semantics and underlying concepts are the same. In this article we’d like to dive a bit deeper into how zones work.

+

The problem to be solved

+

Let’s recap really quick what zones are. As Brian stated in his talk, they are basically an execution context for asynchronous operations. They turn out to be really useful for things like error handling and profiling. But what exactly does that mean?

+

In order to understand the execution context part of it, we need to get a better picture of what the problem is that zones are trying to solve. Let’s first take a look at the following JavaScript code.

+
foo();
+bar();
+baz();
+
+function foo() {...}
+function bar() {...}
+function baz() {...}
+

Nothing special going on here. We have three functions foo, bar and baz that are executed in sequence. Let’s say we want to measure the execution time of this code. We could easily extend the snippet with some profiling bits like this:

+
var start,
+    time = 0;
+    timer = performance ? performance.now : Date.now;
+
+// start timer
+start = timer();
+foo();
+bar();
+baz();
+// stop timer
+time = timer() - start;
+// log time in ms
+console.log(Math.floor(time*100) / 100 + 'ms');
+

However, often we have asynchronous work todo. This can be an AJAX request to fetch some data from a remote server, or a maybe we just want to schedule some work for the next frame. Whatever this asynchronous work is, it happens, as the name claims, asynchronously. Which basically means, those operations won’t be considered by our profiler. Take a look at this snippet:

+
function doSomething() {
+  console.log('Async task');
+}
+
+// start timer
+start = timer();
+foo();
+setTimeout(doSomething, 2000);
+bar();
+baz();
+// stop timer
+time = timer() - start;
+

We extended the code sequence with another operation, but this time it’s asynchronous. What effect does that have on our profiling? Well, we’ll see that there’s not such a big difference.

+

There is in fact one more operation, so it takes slightly longer to execute this code, however, the actual execution time of when the setTimeout() call returns is not part of the overall profiling. This is because asynchronous operations are added to the browser’s event queue, which eventually gets cleaned up by the event loop once there’s time for that.

+

If this is entirely new to you, you might want to watch this great talk on how the browser event loops works.

+

So how do we solve this issue? What we need are basically hooks that allow us to execute some profiling code whenever such an asynchronous task happens. Sure, we could probably create and start an individual timer for each asynchronous operation manually, but that would get quite messy as asynchronous operations are added to the code sequence.

+

This is exactly where zones come into play. Zones can perform an operation - such as starting or stopping a timer, or saving a stack trace - each time that code enters or exits a zone. They can override methods within our code, or even associate data with individual zones.

+

Creating, forking and extending Zones

+

Zones are actually a language feature in Dart. However, since Dart also just compiles to JavaScript, we can implement the same functionality in JavaScript too. Brian (the guy I’ve mentioned earlier) has done exactly that. He created zone.js as a port of Zones to JavaScript, which is also a dependency of Angular. Before we take a look at how we can profile our code samples with Zones, let’s first discuss how zones are created.

+

Once we’ve embedded zone.js into our website, we have access to the global zone object. zone comes with a method run() that takes a function which should be executed in that zone. In other words, if we’d like to run our code in a zone, we can already do it likes this:

+
function main() {
+  foo();
+  setTimeout(doSomething, 2000);
+  bar();
+  baz();
+}
+
+zone.run(main);
+

Okay cool. But what’s the point of this? Well… currently there’s in fact no difference in the outcome, except that we had to write slightly more code. However, at this point, our code runs in a zone (another execution context) and as we learned earlier, Zones can perform an operation each time our code enters or exits a zone.

+

In order to set up these hooks, we need to fork the current zone. Forking a zone returns a new zone, which basically inherits from the “parent” zone. However, forking a zone also allows us to extend the returning zone’s behaviour. We can fork a zone by calling .fork() on the zone object. Here’s what that could look like:

+
var myZone = zone.fork();
+
+myZone.run(main);
+

This really just gives us a new zone with the same power of the original zone (which we haven’t discussed just yet). Let’s try out these hooks we’ve mentioned and extend our new zone. Hooks are defined using a ZoneSpecification that we can pass to fork(). We can take advantage of the following hooks:

+
    +
  • onZoneCreated - Runs when zone is forked
  • +
  • beforeTask - Runs before a function called with zone.run is executed
  • +
  • afterTask - Runs after a function in the zone runs
  • +
  • onError - Runs when a function passed to zone.run will throw
  • +
+

Here’s our code sample with an extended zone that logs before and after each task is executed:

+
var myZoneSpec = {
+  beforeTask: function () {
+    console.log('Before task');
+  },
+  afterTask: function () {
+    console.log('After task');
+  }
+};
+
+var myZone = zone.fork(myZoneSpec);
+myZone.run(main);
+
+// Logs:
+// Before task
+// After task
+// Before task
+// Async task
+// After task
+

Oh wait! What’s that? Both hooks are executed twice? Why is that? Sure, we’ve learned that zone.run is obviously considered a “task” which is why the first two messages are logged. But it seems like the setTimeout() call is treated as a task too. How is that possible?

+

Monkey-patched Hooks

+

It turns out that there are a few other hooks. In fact, those aren’t just hooks, but monkey-patched methods on the global scope. As soon as we embed zone.js in our site, pretty much all methods that cause asynchronous operations are monkey-patched to run in a new zone.

+

For example, when we call setTimeout() we actually call Zone.setTimeout(), which in turn creates a new zone using zone.fork() in which the given handler is executed. And that’s why our hooks are executed as well, because the forked zone in which the handler will be executed, simply inherits from the parent zone.

+

There are some other methods that zone.js overrides by default and provides us as hooks:

+
    +
  • Zone.setInterval()
  • +
  • Zone.alert()
  • +
  • Zone.prompt()
  • +
  • Zone.requestAnimationFrame()
  • +
  • Zone.addEventListener()
  • +
  • Zone.removeEventListener()
  • +
+

We might wonder why methods like alert() and prompt() are patched as well. As mentioned earlier, those patched methods are hooks at the same time. We can change and extend them by forking a zone exactly the same way we did with beforeTask and afterTask. This turns out to be super powerful, because we can intercept calls to alert() and prompt() and change their behaviour when we write tests.

+

zone.js comes with a tiny DSL that allows you to augment zone hooks. The project’s readme is probably the best place to take a look at, if you’re interested in this particular thing.

+

Creating a Profiling Zone

+

Our original problem was that we couldn’t capture the execution time of asynchronous tasks inside our code. Now with Zones and the provided APIs we’ve learned about, we have actually everything we need to create a zone that profiles the CPU time of our asynchronous tasks. Luckily, such an implementation of a profiling zone is already available as an example in the zone.js repository and you can find it here.

+

Here’s what it looks like:

+
var profilingZone = (function () {
+  var time = 0,
+      timer = performance ?
+                  performance.now.bind(performance) :
+                  Date.now.bind(Date);
+  return {
+    beforeTask: function () {
+      this.start = timer();
+    },
+    afterTask: function () {
+      time += timer() - this.start;
+    },
+    time: function () {
+      return Math.floor(time*100) / 100 + 'ms';
+    },
+    reset: function () {
+      time = 0;
+    }
+  };
+}());
+

Pretty much the same code as the one we started off with at the beginning of this article, just wrapped in a zone specification. The example also adds a .time() and .reset() method to the zone, which can be invoked on the zone object like this:

+
zone
+  .fork(profilingZone)
+  .fork({
+    '+afterTask': function () {
+      console.log('Took: ' + zone.time());
+    }
+  })
+  .run(main);
+

The + syntax is a shorthand DSL that allows us to extend the parent zone’s hook. Neat ha?

+

There’s also a LongStackTraceZone we can take advantage of and even more examples. Make sure to check those out too!

+

Watch out for more articles as we’re going to discuss very soon what role Zones play in the Angular framework. Find more useful and related links below.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/02/01/zones-in-angular-2.html b/angular/2016/02/01/zones-in-angular-2.html new file mode 100644 index 000000000..ddc3ba51e --- /dev/null +++ b/angular/2016/02/01/zones-in-angular-2.html @@ -0,0 +1,141 @@ +Zones in Angular | Articles by thoughtram

Angular

Zones in Angular

In Understanding Zones, we explored the power of Zones by building a profiling zone that profiles asynchronous operations in our code. We learned that Zones are a sort of execution context that allows us to hook into our asynchronous tasks. If you haven’t read that article, we highly recommend checking it out as this one is based on it. In this article we’re going to take a closer look at what role Zones play in Angular.

+

UPDATE: We’ve published another article that discusses how to use Zones to make your Angular apps faster

+

Zones are a perfect fit for Angular

+

It turns out that, the problem that Zones solve, plays very nicely with what Angular needs in order to perform change detection in our applications. Did you ever ask yourself when and why Angular performs change detection? What is it that tells Angular “Dude, a change probably occurred in my application. Can you please check?“.

+

Before we dive into these questions, let’s first think about what actually causes this change in our applications. Or rather, what can change state in our applications. Application state change is caused by three things:

+
    +
  • Events - User events like click, change, input, submit, …
  • +
  • XMLHttpRequests - E.g. when fetching data from a remote service
  • +
  • Timers - setTimeout(), setInterval(), because JavaScript
  • +
+

It turns out that these three things have something in common. Can you name it? … Correct! They are all asynchronous.

+

Why do you think is this important? Well … because it turns out that these are the only cases when Angular is actually interested in updating the view. Let’s say we have an Angular component that executes a handler when a button is clicked:

+
@Component({
+  selector: 'my-component',
+  template: `
+    <h3>We love {{name}}</h3>
+    <button (click)="changeName()">Change name</button>
+  `
+})
+class MyComponent {
+
+  name:string = 'thoughtram';
+
+  changeName() {
+    this.name = 'Angular';
+  }
+}
+

If you’re not familiar with the (click) syntax, you might want to read our article on Angular’s Template Syntax Demystified. The short version is, that this sets up an event handler for the click event on the <button> element.

+

When the component’s button is clicked, changeName() is executed, which in turn will change the name property of the component. Since we want this change to be reflected in the DOM as well, Angular is going to update the view binding {% raw %}{{name}}{% endraw %} accordingly. Nice, that seems to magically work.

+

Another example would be to update the name property using setTimeout(). Note that we removed the button.

+
@Component({
+  selector: 'my-component',
+  template: `
+    <h3>We love {{name}}</h3>
+  `
+})
+class MyComponent implements OnInit {
+
+  name:string = 'thoughtram';
+
+  ngOnInit() {
+    setTimeout(() => {
+      this.name = 'Angular';
+    }, 1000);
+  }
+}
+

We don’t have to do anything special to tell the framework that a change has happened. No ng-click, no $timeout, $scope.$apply().

+

If you’ve read our article on understanding Zones, you know that this works obviously because Angular takes advantage of Zones. Zones monkey-patches global asynchronous operations such as setTimeout() and addEventListener(), which is why Angular can easily find out, when to update the DOM.

+

In fact, the code that tells Angular to perform change detection whenever the VM turn is done, is as simple as this:

+
ObservableWrapper.subscribe(this.zone.onTurnDone, () => {
+  this.zone.run(() => {
+    this.tick();
+  });
+});
+
+tick() {
+  // perform change detection
+  this.changeDetectorRefs.forEach((detector) => {
+    detector.detectChanges();
+  });
+}
+

Whenever Angular’s zone emits an onTurnDone event, it runs a task that performs change detection for the entire application. If you’re interested in how change detection in Angular works, watch out, we’re going to publish another article on that soon go ahead and read this article.

+

But wait, where does the onTurnDone event emitter come from? This is not part of the default Zone API, right? It turns out that Angular introduces its own zone called NgZone.

+

NgZone in Angular

+

NgZone is basically a forked zone that extends its API and adds some additional functionality to its execution context. One of the things it adds to the API is the following set of custom events we can subscribe to, as they are observable streams:

+
    +
  • onTurnStart() - Notifies subscribers just before Angular’s event turn starts. Emits an event once per browser task that is handled by Angular.
  • +
  • onTurnDone() - Notifies subscribers immediately after Angular’s zone is done processing the current turn and any micro tasks scheduled from that turn.
  • +
  • onEventDone() - Notifies subscribers immediately after the final onTurnDone() callback before ending VM event. Useful for testing to validate application state.
  • +
+

If “Observables” and “Streams” are super new to you, you might want to read our article on Taking advantage of Observables in Angular.

+

The main reason Angular adds its own event emitters instead of relying on beforeTask and afterTask callbacks, is that it has to keep track of timers and other micro tasks. It’s also nice that Observables are used as an API to handle these events.

+

Running code outside Angular’s zone

+

Since NgZone is really just a fork of the global zone, Angular has full control over when to run something inside its zone to perform change detection and when not. Why is that useful? Well, it turns out that we don’t always want Angular to magically perform change detection.

+

As mentioned a couple of times, Zones monkey-patches pretty much any global asynchronous operations by the browser. And since NgZone is just a fork of that zone which notifies the framework to perform change detection when an asynchronous operation has happened, it would also trigger change detection when things like mousemove events fire.

+

We probably don’t want to perform change detection every time mousemove is fired as it would slow down our application and results in very bad user experience.

+

That’s why NgZone comes with an API runOutsideAngular() which performs a given task in NgZone’s parent zone, which does not emit an onTurnDone event, hence no change detection is performed. To demonstrate this useful feature, let’s take look at the following code:

+
@Component({
+  selector: 'progress-bar',
+  template: `
+    <h3>Progress: {{progress}}</h3>
+    <button (click)="processWithinAngularZone()">
+      Process within Angular zone
+    </button>
+  `
+})
+class ProgressBar {
+
+  progress: number = 0;
+
+  constructor(private zone: NgZone) {}
+
+  processWithinAngularZone() {
+    this.progress = 0;
+    this.increaseProgress(() => console.log('Done!'));
+  }
+}
+

Nothing special going on here. We have component that calls processWithinAngularZone() when the button in the template is clicked. However, that method calls increaseProgress(). Let’s take a closer look at this one:

+
increaseProgress(doneCallback: () => void) {
+  this.progress += 1;
+  console.log(`Current progress: ${this.progress}%`);
+
+  if (this.progress < 100) {
+    window.setTimeout(() => {
+      this.increaseProgress(doneCallback);
+    }, 10);
+  } else {
+    doneCallback();
+  }
+}
+

increaseProgress() calls itself every 10 milliseconds until progress equals 100. Once it’s done, the given doneCallback will execute. Notice how we use setTimeout() to increase the progress.

+

Running this code in the browser, basically demonstrates what we already know. After each setTimeout() call, Angular performs change detection and updates the view, which allows us to see how progress is increased every 10 milliseconds. It gets more interesting when we run this code outside Angular’s zone. Let’s add a method that does exactly that.

+
processOutsideAngularZone() {
+  this.progress = 0;
+  this.zone.runOutsideAngular(() => {
+    this.increaseProgress(() => {
+      this.zone.run(() => {
+        console.log('Outside Done!');
+      });
+    });
+  });
+}
+

processOutsideAngularZone() also calls increaseProgress() but this time using runOutsideAngularZone() which causes Angular not to be notified after each timeout. We access Angular’s zone by injecting it into our component using the NgZone token.

+

The UI is not updated as progress increases. However, once increaseProgress() is done, we run another task inside Angular’s zone again using zone.run() which in turn causes Angular to perform change detection which will update the view. In other words, instead of seeing progress increasing, all we see is the final value once it’s done. Check out the running code in action below.

+

Zones have now also been proposed as a standard at TC39, maybe another reason to take a closer look at them.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/02/22/angular-2-change-detection-explained.html b/angular/2016/02/22/angular-2-change-detection-explained.html new file mode 100644 index 000000000..8aa8ed174 --- /dev/null +++ b/angular/2016/02/22/angular-2-change-detection-explained.html @@ -0,0 +1,225 @@ +Angular Change Detection Explained | Articles by thoughtram

Angular

Angular Change Detection Explained

NG-NL has happened and it was awesome! I had the honour of giving a talk about change detection in Angular and it seemed to be a huge success as attendees liked it a lot. With this article, we’d like to transform the talk into a written version, so everyone can read about how Angular’s change detection works and how to make it faster for our use cases. If you’re interested in the talk, you can view the slides here and as soon as the talk recording is up, you’ll find it here as well watch the recording on Youtube.

+

Now let’s take a look at this beast.

+

What’s Change Detection anyways?

+

The basic task of change detection is to take the internal state of a program and make it somehow visible to the user interface. This state can be any kind of objects, arrays, primitives, … just any kind of JavaScript data structures.

+

This state might end up as paragraphs, forms, links or buttons in the user interface and specifically on the web, it’s the Document Object Model (DOM). So basically we take data structures as input and generate DOM output to display it to the user. We call this process rendering.

+

+

However, it gets trickier when a change happens at runtime. Some time later when the DOM has already been rendered. How do we figure out what has changed in our model, and where do we need to update the DOM? Accessing the DOM tree is always expensive, so not only do we need to find out where updates are needed, but we also want to keep that access as tiny as possible.

+

This can be tackled in many different ways. One way, for instance, is simply making a http request and re-rendering the whole page. Another approach is the concept of diffing the DOM of the new state with the previous state and only render the difference, which is what ReactJS is doing with Virtual DOM.

+

Tero has written an awesome article on Change and its detection in JavaScript frameworks, we recommend checking it out if you’re more interested in how different frameworks solve this issue. In this article we’re going to focus on Angular version >= 2.x.

+

So basically the goal of change detection is always projecting data and its change.

+

What causes change?

+

Now that we know what change detection is all about, we might wonder, when exactly can such a change happen? When does Angular know that it has to update the view? Well, let’s take a look at the following code:

+
@Component({
+  template: `
+    <h1>{{firstname}} {{lastname}}</h1>
+    <button (click)="changeName()">Change name</button>
+  `
+})
+class MyApp {
+
+  firstname:string = 'Pascal';
+  lastname:string = 'Precht';
+
+  changeName() {
+    this.firstname = 'Brad';
+    this.lastname = 'Green';
+  }
+}
+

If this is the first time you’re seeing an Angular component, you might want to read our article on building a tabs component.

+

The component above simply displays two properties and provides a method to change them when the button in the template is clicked. The moment this particular button is clicked is the moment when application state has changed, because it changes the properties of the component. That’s the moment we want to update the view.

+

Here’s another one:

+
@Component()
+class ContactsApp implements OnInit{
+
+  contacts:Contact[] = [];
+
+  constructor(private http: Http) {}
+
+  ngOnInit() {
+    this.http.get('/contacts')
+      .map(res => res.json())
+      .subscribe(contacts => this.contacts = contacts);
+  }
+}
+

This component holds a list of contacts and when it initializes, it performs a http request. Once this request comes back, the list gets updated. Again, at this point, our application state has changed so we will want to update the view.

+

Basically application state change can be caused by three things:

+
    +
  • Events - click, submit, …
  • +
  • XHR - Fetching data from a remote server
  • +
  • Timers - setTimeout(), setInterval()
  • +
+

They are all asynchronous. Which brings us to the conclusion that, basically whenever some asynchronous operation has been performed, our application state might have changed. This is when someone needs to tell Angular to update the view.

+

Who notifies Angular?

+

Alright, we now know what causes application state change. But what is it that tells Angular, that at this particular moment, the view has to be updated?

+

Angular allows us to use native APIs directly. There are no interceptor methods we have to call so Angular gets notified to update the DOM. Is that pure magic?

+

If you’ve followed our latest articles, you know that Zones take care of this. In fact, Angular comes with its own zone called NgZone, which we’ve written about in our article Zones in Angular. You might want to read that, too.

+

The short version is, that somewhere in Angular’s source code, there’s this thing called ApplicationRef, which listens to NgZones onTurnDone event. Whenever this event is fired, it executes a tick() function which essentially performs change detection.

+
// very simplified version of actual source
+class ApplicationRef {
+
+  changeDetectorRefs:ChangeDetectorRef[] = [];
+
+  constructor(private zone: NgZone) {
+    this.zone.onTurnDone
+      .subscribe(() => this.zone.run(() => this.tick());
+  }
+
+  tick() {
+    this.changeDetectorRefs
+      .forEach((ref) => ref.detectChanges());
+  }
+}
+

Change Detection

+

Okay cool, we now know when change detection is triggered, but how is it performed? Well, the first thing we need to notice is that, in Angular, each component has its own change detector.

+

+

This is a significant fact, since this allows us to control, for each component individually, how and when change detection is performed! More on that later.

+

Let’s assume that somewhere in our component tree an event is fired, maybe a button has been clicked. What happens next? We just learned that zones execute the given handler and notify Angular when the turn is done, which eventually causes Angular to perform change detection.

+

+

Since each component has its own change detector, and an Angular application consists of a component tree, the logical result is that we’re having a change detector tree too. This tree can also be viewed as a directed graph where data always flows from top to bottom.

+

The reason why data flows from top to bottom, is because change detection is also always performed from top to bottom for every single component, every single time, starting from the root component. This is awesome, as unidirectional data flow is more predictable than cycles. We always know where the data we use in our views comes from, because it can only result from its component.

+

Another interesting observation is that change detection gets stable after a single pass. Meaning that, if one of our components causes any additional side effects after the first run during change detection, Angular will throw an error.

+

Performance

+

By default, even if we have to check every single component every single time an event happens, Angular is very fast. It can perform hundreds of thousands of checks within a couple of milliseconds. This is mainly due to the fact that Angular generates VM friendly code.

+

What does that mean? Well, when we said that each component has its own change detector, it’s not like there’s this single generic thing in Angular that takes care of change detection for each individual component.

+

The reason for that is, that it has to be written in a dynamic way, so it can check every component no matter what its model structure looks like. VMs don’t like this sort of dynamic code, because they can’t optimize it. It’s considered polymorphic as the shape of the objects isn’t always the same.

+

Angular creates change detector classes at runtime for each component, which are monomorphic, because they know exactly what the shape of the component’s model is. VMs can perfectly optimize this code, which makes it very fast to execute. The good thing is that we don’t have to care about that too much, because Angular does it automatically.

+

Check out Victor Savkin’s talk on Change Detection Reinvented for a deeper explanation on this.

+

Smarter Change Detection

+

Again, Angular has to check every component every single time an event happens because… well, maybe the application state has changed. But wouldn’t it be great if we could tell Angular to only run change detection for the parts of the application that changed their state?

+

Yes it would, and in fact we can! It turns out there are data structures that give us some guarantees of when something has changed or not - Immutables and Observables. If we happen to use these structures or types, and we tell Angular about it, change detection can be much much faster. Okay cool, but how so?

+

Understanding Mutability

+

In order to understand why and how e.g. immutable data structures can help, we need to understand what mutability means. Assume we have the following component:

+
@Component({
+  template: '<v-card [vData]="vData"></v-card>'
+})
+class VCardApp {
+
+  constructor() {
+    this.vData = {
+      name: 'Christoph Burgdorf',
+      email: 'christoph@thoughtram.io'
+    }
+  }
+
+  changeData() {
+    this.vData.name = 'Pascal Precht';
+  }
+}
+

VCardApp uses <v-card> as a child component, which has an input property vData. We’re passing data to that component with VCardApp’s own vData property. vData is an object with two properties. In addition, there’s a method changeData(), which changes the name of vData. No magic going on here.

+

The important part is that changeData() mutates vData by changing its name property. Even though that property is going to be changed, the vData reference itself stays the same.

+

What happens when change detection is performed, assuming that some event causes changeData() to be executed? First, vData.name gets changed, and then it’s passed to <v-card>. <v-card>’s change detector now checks if the given vData is still the same as before, and yes, it is. The reference hasn’t changed. However, the name property has changed, so Angular will perform change detection for that object nonetheless.

+

Because objects are mutable by default in JavaScript (except for primitives), Angular has to be conservative and run change detection every single time for every component when an event happens.

+

This is where immutable data structures come into play.

+

Immutable Objects

+

Immutable objects give us the guarantee that objects can’t change. Meaning that, if we use immutable objects and we want to make a change on such an object, we’ll always get a new reference with that change, as the original object is immutable.

+

This pseudo code demonstrates it:

+
var vData = someAPIForImmutables.create({
+              name: 'Pascal Precht'
+            });
+
+var vData2 = vData.set('name', 'Christoph Burgdorf');
+
+vData === vData2 // false
+

someAPIForImmutables can be any API we want to use for immutable data structures. However, as we can see, we can’t simply change the name property. We’ll get a new object with that particular change and this object has a new reference. +Or in short: If there’s a change, we get a new reference.

+

Reducing the number of checks

+

Angular can skip entire change detection subtrees when input properties don’t change. We just learned that a “change” means “new reference”. If we use immutable objects in our Angular app, all we need to do is tell Angular that a component can skip change detection, if its input hasn’t changed.

+

Let’s see how that works by taking a look at <v-card>:

+
@Component({
+  template: `
+    <h2>{{vData.name}}</h2>
+    <span>{{vData.email}}</span>
+  `
+})
+class VCardCmp {
+  @Input() vData;
+}
+

As we can see, VCardCmp only depends on its input properties. Great. We can tell Angular to skip change detection for this component’s subtree if none of its inputs changed by setting the change detection strategy to OnPush like this:

+
@Component({
+  template: `
+    <h2>{{vData.name}}</h2>
+    <span>{{vData.email}}</span>
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+class VCardCmp {
+  @Input() vData;
+}
+

That’s it! Now imagine a bigger component tree. We can skip entire subtrees when immutable objects are used and Angular is informed accordingly.

+

+

Jurgen Van De Moere has written an in-depth article on how he made a minesweeper game built with Angular and Immutable.js blazingly fast. Make sure to check that one out.

+

Observables

+

As mentioned earlier, Observables also give us some certain guarantees of when a change has happened. Unlike immutable objects, they don’t give us new references when a change is made. Instead, they fire events we can subscribe to in order to react to them.

+

So, if we use Observables and we want to use OnPush to skip change detector subtrees, but the reference of these objects will never change, how do we deal with that? It turns out Angular has a very smart way to enable paths in the component tree to be checked for certain events, which is exactly what we need in that case.

+

To understand what that means, let’s take a look at this component:

+
@Component({
+  template: '{{counter}}',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+class CartBadgeCmp {
+
+  @Input() addItemStream:Observable<any>;
+  counter = 0;
+
+  ngOnInit() {
+    this.addItemStream.subscribe(() => {
+      this.counter++; // application state changed
+    })
+  }
+}
+

Let’s say we build an e-commerce application with a shopping cart. Whenever a user puts a product into the shopping cart, we want a little counter to show up in our UI, so the user can see the amount of products in the cart.

+

CartBadgeCmp does exactly that. It has a counter and an input property addItemStream, which is a stream of events that gets fired, whenever a product is added to the shopping cart.

+

We won’t go into much detail on how observables work in this article. If you want to learn more about observables, make sure to read our article on taking advantage of Observables in Angular.

+

In addition, we set the change detection strategy to OnPush, so change detection isn’t performed all the time, only when the component’s input properties change.

+

However, as mentioned earlier, the reference of addItemStream will never change, so change detection is never performed for this component’s subtree. This is a problem because the component subscribes to that stream in its ngOnInit life cycle hook and increments the counter. This is application state change and we want to have this reflected in our view right?

+

Here’s what our change detector tree might look like (we’ve set all to OnPush). No change detection will be performed when an event happens:

+

+

How can we inform Angular about this change? How can we tell Angular that change detection needs to be performed for this component, even though the entire tree is set to OnPush?

+

No worries, Angular has us covered. As we’ve learned earlier, change detection is always performed from top to bottom. So what we need is a way to detect changes for the entire path of the tree to the component where the change happened. Angular can’t know which path it is, but we do.

+

We can access a component’s ChangeDetectorRef via dependency injection, which comes with an API called markForCheck(). This method does exactly what we need! It marks the path from our component until root to be checked for the next change detection run.

+

Let’s inject it into our component:

+
constructor(private cd: ChangeDetectorRef) {}
+

Then, tell Angular to mark the path from this component until root to be checked:

+
ngOnInit() {
+  this.addItemStream.subscribe(() => {
+    this.counter++; // application state changed
+    this.cd.markForCheck(); // marks path
+  })
+}
+

Boom, that’s it! Here’s what it looks like after the observable event is fired but before change detection kicked in:

+

+

Now, when change detection is performed, it’ll simply go from top to bottom.

+

+

How cool is that? Once the change detection run is over, it’ll restore the OnPush state for the entire tree.

+

There’s more…

+

In fact there are some more APIs which we haven’t covered in this article, but feel free to check out the slides and/or the recording of the talk.

+

There are also some demos to play with in this repository, which you can run on your local machine.

+

Hopefully this made it a little bit more clear how using immutable data structures or Observables can make our Angular application even faster.

+

Credits

+

I’d like to thank Jurgen Van De Moere for being a huge help and support when I was preparing this talk. He spent a lot of time with me discussing my understandings and raised a lot of good questions that helped me put this content together. He also made sure that the demos look as nice as they do. His CSS skills are amazing - Jurgen, thank you so so much for being such a supportive and nice person.

+

I’d also like to thank Victor Savkin for answering a lot of my questions regarding change detection in Angular, plus all the very informative articles that he’s written - Thanks Victor!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/03/14/custom-validators-in-angular-2.html b/angular/2016/03/14/custom-validators-in-angular-2.html new file mode 100644 index 000000000..707e3fa8d --- /dev/null +++ b/angular/2016/03/14/custom-validators-in-angular-2.html @@ -0,0 +1,288 @@ +Custom Validators in Angular | Articles by thoughtram

Angular

Custom Validators in Angular

Forms are part of almost every web application out there. Angular strives for making working with forms a breeze. While there are a couple of built-in validators provided by the framework, we often need to add some custom validation capabilities to our application’s form, in order to fulfill our needs.

+

We can easily extend the browser vocabulary with additional custom validators and in this article we are going to explore how to do that.

+

Built-in Validators

+

Angular comes with a subset of built-in validators out of the box. We can apply them either declaratively as directives on elements in our DOM, in case we’re building a template-driven form, or imperatively using the FormControl and FormGroup or FormBuilder APIs, in case we’re building a reactive forms. If you don’t know what it’s all about with template-driven and reactive forms, don’t worry, we have an articles about both topics here and here.

+

The supported built-in validators, at the time of writing this article, are:

+
    +
  • required - Requires a form control to have a non-empty value
  • +
  • minlength - Requires a form control to have a value of a minimum length
  • +
  • maxlength - Requires a form control to have a value of a maximum length
  • +
  • pattern - Requires a form control’s value to match a given regex
  • +
+

As mentioned earlier, validators can be applied by simply using their corresponding directives. To make these directives available, we need to import Angular’s FormsModule to our application module first:

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, FormsModule], // we add FormsModule here
+  declarations: [AppComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

Once this is done, we can use all directives provided by this module in our application. The following form shows how the built-in validators are applied to dedicated form controls:

+
<form novalidate>
+  <input type="text" name="name" ngModel required>
+  <input type="text" name="street" ngModel minlength="3">
+  <input type="text" name="city" ngModel maxlength="10">
+  <input type="text" name="zip" ngModel pattern="[A-Za-z]{5}">
+</form>
+

Or, if we had a reactive form, we’d need to import the ReactiveFormsModule first:

+
import { ReactiveFormsModule } from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, ReactiveFormsModule],
+  ...
+})
+export class AppModule {}
+

And can then build our form either using FormControl and FormGroup APIs:

+
@Component()
+class Cmp {
+
+  form: FormGroup;
+
+  ngOnInit() {
+    this.form = new FormGroup({
+      name: new FormControl('', Validators.required)),
+      street: new FormControl('', Validators.minLength(3)),
+      city: new FormControl('', Validators.maxLength(10)),
+      zip: new FormControl('', Validators.pattern('[A-Za-z]{5}'))
+    });
+  }
+}
+

Or use the less verbose FormBuilder API that does the same work for us:

+
@Component()
+class Cmp {
+
+  constructor(private fb: FormBuilder) {}
+
+  ngOnInit() {
+    this.form = this.fb.group({
+      name: ['', Validators.required],
+      street: ['', Validators.minLength(3)],
+      city: ['', Validators.maxLength(10)],
+      zip: ['', Validators.pattern('[A-Za-z]{5}')]
+    });
+  }
+}
+

We would still need to associate a form model with a form in the DOM using the [formGroup] directive likes this:

+
<form novalidate [formGroup]="form">
+  ...
+</form>
+

Observing these two to three different methods of creating a form, we might wonder how it is done that we can use the validator methods imperatively in our component code, and apply them as directives to input controls declaratively in our HTML code.

+

It turns out there’s really not such a big magic involved, so let’s build our own custom email validator.

+

Building a custom validator

+

In it’s simplest form, a validator is really just a function that takes a Control and returns either null when it’s valid, or and error object if it’s not. A TypeScript interface for such a validator looks something like this:

+
interface Validator<T extends FormControl> {
+   (c:T): {[error: string]:any};
+}
+

Let’s implement a validator function validateEmail which implements that interface. All we need to do is to define a function that takes a FormControl, checks if it’s value matches the regex of an email address, and if not, returns an error object, or null in case the value is valid.

+

Here’s what such an implementation could look like:

+
import { FormControl } from '@angular/forms';
+
+function validateEmail(c: FormControl) {
+  let EMAIL_REGEXP = ...
+
+  return EMAIL_REGEXP.test(c.value) ? null : {
+    validateEmail: {
+      valid: false
+    }
+  };
+}
+

Pretty straight forward right? We import FormControl from @angular/forms to have the type information the function’s signature and simply test a regular expression with the FormControl’s value. That’s it. That’s a validator.

+

But how do we apply them to other form controls? Well, we’ve seen how Validators.required and the other validators are added to the new FormControl() calls. FormControl() takes an initial value, a synchronous validator and an asynchronous validator. Which means, we do exactly the same with our custom validators.

+
ngOnInit() {
+  this.form = new FormGroup({
+    ...
+    email: new FormControl('', validateEmail)
+  });
+}
+

Don’t forget to import validateEmail accordinlgy, if necessary. Okay cool, now we know how to add our custom validator to a form control.

+

However, what if we want to combine multiple validators on a single control? Let’s say our email field is required and needs to match the shape of an email address. FormControls takes a single synchronous and a single asynchronous validator, or, a collection of synchronous and asynchronous validators. +Here’s what it looks like if we’d combine the required validator with our custom one:

+
ngOnInit() {
+  this.form = new FormGroup({
+    ...
+    email: new FormControl('', [
+      Validators.required,
+      validateEmail
+    ])
+  });
+}
+

Building custom validator directives

+

Now that we’re able to add our custom validator to our form controls imperatively when building model-driven forms, we might also enable our validator to be used in template driven forms. In other words: We need a directive. The validator should be usable like this:

+
<form novalidate>
+  ...
+  <input type="email" name="email" ngModel validateEmail>
+</form>
+

validateEmail is applied as an attribute to the <input> DOM element, which already gives us an idea what we need to do. We need to build a directive with a matching selector so it will be executed on all input controls where the directive is applied. Let’s start off with that.

+
import { Directive } from '@angular/core';
+
+@Directive({
+  selector: '[validateEmail][ngModel]'
+})
+export class EmailValidator {}
+

We import the @Directive decorator form @angular/core and use it on a new EmailValidator class. If you’re familiar with the @Component decorator that this is probably not new to you. In fact, @Directive is a superset of @Component which is why most of the configuration properties are available.

+

Okay, technically we could already make this directive execute in our app, all we need to do is to add it to our module’s declarations:

+
import { EmailValidator } from './email.validator';
+
+@NgModule({
+  ...
+  declarations: [AppComponent, EmailValidator],
+})
+export class AppModule {}
+

Even though this works, there’s nothing our directive does at the moment. What we want to do is to make sure that our custom validator is executed when Angular compiles this directive. How do we get there?

+

Angular has an internal mechanism to execute validators on a form control. It maintains a multi provider for a dependency token called NG_VALIDATORS. If you’ve read our article on multi providers in Angular, you know that Angular injects multiple values for a single token that is used for a multi provider. If you haven’t, we highly recommend checking it out as the rest of this article is based on it.

+

It turns out that all built-in validators are already added to the NG_VALIDATORS token. So whenever Angular instantiates a form control and performs validation, it basically injects the dependency for the NG_VALIDATORS token, which is a list of all validators, and executes them one by one on that form control.

+

Since multi providers can be extended by adding more multi providers to a token, we can consider NG_VALIDATORS as a hook to add our own validators.

+

Let’s add our validator to the NG_VALIDATORS via our new directive:

+
import { Directive } from '@angular/core';
+import { NG_VALIDATORS } from '@angular/forms';
+
+@Directive({
+  selector: '[validateEmail][ngModel]',
+  providers: [
+    { provide: NG_VALIDATORS, useValue: validateEmail, multi: true }
+  ]
+})
+class EmailValidator {}
+

Again, if you’ve read our article on multi providers, this should look very familiar to you. We basically add a new value to the NG_VALIDATORS token by taking advantage of multi providers. Angular will pick our validator up by injecting what it gets for NG_VALIDATORS, and performs validation on a form control. Awesome, we can now use our validator for reactiveand for template-driven forms!

+

Custom Validators with dependencies

+

Sometimes, a custom validator has dependencies so we need a way to inject them. Let’s say our email validator needs an EmailBlackList service, to check if the given control value is not only a valid email address but also not on our email black list (in an ideal world, we’d build a separate validator for checking against an email black list, but we use that as a motivation for now to have a dependency).

+

The not-so-nice way

+

One way to handle this is to create a factory function that returns our validateEmail function, which then uses an instance of EmailBlackList service. Here’s what such a factory function could look like:

+
import { FormControl } from '@angular/forms';
+
+function validateEmailFactory(emailBlackList: EmailBlackList) {
+  return (c: FormControl) => {
+    let EMAIL_REGEXP = ...
+
+    let isValid = /* check validation with emailBlackList */
+
+    return isValid ? null : {
+      validateEmail: {
+        valid: false
+      }
+    };
+  };
+}
+

This would allow us to register our custom validator via dependency injection like this:

+
@Directive({
+  ...
+  providers: [
+    {
+      provide: NG_VALIDATORS,
+      useFactory: (emailBlackList) => {
+        return validateEmailFactory(emailBlackList);
+      },
+      deps: [EmailBlackList]
+      multi: true
+    }
+  ]
+})
+class EmailValidator {}
+

We can’t use useValue as provider recipe anymore, because we don’t want to return the factory function, but rather what the factory function returns. And since our factory function has a dependency itself, we need to have access to dependency tokens, which is why we use useFactory and deps. If this is entirely new to you, you might want to read our article on Dependency Injection in Angular before we move on.

+

Even though this would work, it’s quite a lot of work and also very verbose. We can do better here.

+

The better way

+

Wouldn’t it be nice if we could use constructor injection as we’re used to it in Angular? Yes, and guess what, Angular has us covered. It turns out that a validator can also be a class as long as it implements a validate(c: FormControl) method. Why is that nice? Well, we can inject our dependency using constructor injection and don’t have to setup a provider factory as we did before.

+

Here’s what our EmailValidator class would look like when we apply this pattern to it:

+
@Directive({
+  ...
+})
+class EmailValidator {
+  
+  validator: Function;
+
+  constructor(emailBlackList: EmailBlackList) {
+    this.validator = validateEmailFactory(emailBlackList);
+  }
+
+  validate(c: FormControl) {
+    return this.validator(c);
+  }
+}
+

However, we now need to adjust the provider for NG_VALIDATORS, because we want to use an instance of EmailValidator to be used for validation, not the factory function. This seems easy to fix, because we know that we create instances of classes for dependency injection using the useClass recipe.

+

However, we already added EmailValidator to the directives property of our component, which is a provider with the useClass recipe. We want to make sure that we get the exact same instance of EmailValidator on our form control, even though, we define a new provider for it. Luckily we have the useExisting recipe for that. useExisting defines an alias token for but returns the same instance as the original token:

+
@Directive({
+  ...
+  providers: [
+    { provide: NG_VALIDATORS, useExisting: EmailValidator, multi: true }
+  ]
+})
+class EmailValidator {
+  ...
+}
+

Yikes! This won’t work . We’re referencing a token (EmailValidator) which is undefined at the point we’re using it because the class definition itself happens later in the code. That’s where forwardRef() comes into play.

+
import { forwardRef } from '@angular/core';
+
+@Directive({
+  ...
+  providers: [
+    { provide: NG_VALIDATORS, useExisting: forwardRef(() => EmailValidator), multi: true }
+  ]
+})
+class EmailValidator {
+  ...
+}
+

If you don’t know what forwardRef() does, you might want to read our article on Forward References in Angular.

+

Here’s the full code for our custom email validator:

+
import { Directive, forwardRef } from '@angular/core';
+import { NG_VALIDATORS, FormControl } from '@angular/forms';
+
+function validateEmailFactory(emailBlackList: EmailBlackList) {
+  return (c: FormControl) => {
+    let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
+
+    return EMAIL_REGEXP.test(c.value) ? null : {
+      validateEmail: {
+        valid: false
+      }
+    };
+  };
+}
+
+@Directive({
+  selector: '[validateEmail][ngModel],[validateEmail][formControl]',
+  providers: [
+    { provide: NG_VALIDATORS, useExisting: forwardRef(() => EmailValidator), multi: true }
+  ]
+})
+export class EmailValidator {
+
+  validator: Function;
+
+  constructor(emailBlackList: EmailBlackList) {
+    this.validator = validateEmailFactory(emailBlackList);
+  }
+
+  validate(c: FormControl) {
+    return this.validator(c);
+  }
+}
+

You might notice that we’ve extended the selector, so that our validator not only works with ngModel but also with formControl directives. If you’re interested in more articles on forms in Angular, we’ve written a couple about template-driven forms and reactive forms.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/03/21/template-driven-forms-in-angular-2.html b/angular/2016/03/21/template-driven-forms-in-angular-2.html new file mode 100644 index 000000000..237eb18fa --- /dev/null +++ b/angular/2016/03/21/template-driven-forms-in-angular-2.html @@ -0,0 +1,224 @@ +Template-driven Forms in Angular | Articles by thoughtram

Angular

Template-driven Forms in Angular

Angular comes with three different ways of building forms in our applications. There’s the template-driven approach which allows us to build forms with very little to none application code required, then there’s the model-driven or reactive approach using low level APIs, which makes our forms testable without a DOM being required, and last but not least, we can build our forms model-driven but with a higher level API called the FormBuilder.

+

Hearing all these different solutions, it’s kind of natural that there are also probably many different tools to reach the goal. This can be sometimes confusing and with this article we want to clarify a subset of form directives by focussing on template-driven forms in Angular.

+

Activating new Form APIs

+

The form APIs have changed in RC2 and in order to not break all existing apps that have been built with RC1 and use forms, these new APIs are added on top of the existing ones. That means, we need to tell Angular explicitly which APIs we want to use (of course, this will go away in the final release).

+

In order to activate the new APIs we need to import Angular’s FormsModule into our application module.

+

Here’s what that could look like:

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, FormsModule],
+  declarations: [AppComponent],
+  bootstrap: [AppComponent]
+})
+export AppModule {}
+

We then bootstrap our application module like this:

+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+

ngForm Directive

+

Let’s start off with a simple login form that asks for some user data:

+
<form>
+  <label>Firstname:</label>
+  <input type="text">
+
+  <label>Lastname:</label>
+  <input type="text">
+
+  <label>Street:</label>
+  <input type="text">
+
+  <label>Zip:</label>
+  <input type="text">
+
+  <label>City:</label>
+  <input type="text">
+
+  <button type="submit">Submit</button>
+</form>
+

We’ve probably all built one of those forms several times. A simple HTML form with input controls for a name and an address of a user. Nothing special going on here.

+

What we can’t see here is that Angular comes with a directive ngForm that matches the <form> selector, so in fact, our form element already has an instance of ngForm applied. ngForm is there for a reason. It provides us information about the current state of the form including:

+
    +
  • A JSON representation of the form value
  • +
  • Validity state of the entire form
  • +
+

Accessing the ngForm instance

+

Angular comes with a very convenient way of exposing directive instances in a component’s template using the exportAs property of the directive metadata. For example, if we’d build a directive draggable, we could expose an instance to the template via the name draggable like so:

+
@Directive({
+  selector: '[draggable]',
+  exportAs: 'draggable'
+})
+class Draggable {
+  ...
+}
+

And then, in the template where it’s used, we can simply ask for it using Angular’s local variable mechanism:

+
<div draggable #myDraggable="draggable">I'm draggable!</div>
+

From this point on myDraggable is a reference to an instance of Draggable and we can use it throughout our entire template as part of other expressions.

+

You might wonder why that’s interesting. Well, it turns out that ngForm directive is exposed as ngForm, which means we can get an instance of our form without writing any application code like this:

+
<form #form="ngForm">
+  ...
+</form>
+

Submitting a form and Accessing its value

+

We can now use form to access the form’s value and it’s validity state. Let’s log the value of the form when it’s submitted. All we have to do is to add a handler to the form’s submit event and pass it the form’s value. In fact, there’s a property on the ngForm instance called value, so this is what it’d look like:

+
<form #form="ngForm" (submit)="logForm(form.value)">
+  ...
+</form>
+

Even though this would work, it turns out that there’s another output event ngForm fires when it’s submitted. It’s called ngSubmit, and it seems to be doing exactly the same as submit at a first glance. However, ngSubmit ensures that the form doesn’t submit when the handler code throws (which is the default behaviour of submit) and causes an actual http post request. Let’s use ngSubmit instead as this is the best practice:

+
<form #form="ngForm" (ngSubmit)="logForm(form.value)">
+  ...
+</form>
+

In addition, we might have a component that looks something like this:

+
@Component({
+  selector: 'app',
+  template: ...
+})
+class App {
+
+  logForm(value: any) {
+    console.log(value);
+  }
+}
+

Running this code makes us realize, that the form’s value is an empty object. This seems natural, because there’s nothing in our component’s template yet, that tells the form that the input controls are part of this form. We need a way to register them. This is where ngModel comes into play.

+

ngModel Directive

+

In order to register form controls on an ngForm instance, we use the ngModel directive. In combination with a name attribute, ngModel creates a form control abstraction for us behind the scenes. Every form control that is registered with ngModel will automatically show up in form.value and can then easily be used for further post processing.

+

Let’s give our form object some structure and register our form controls:

+
<form #form="ngForm" (ngSubmit)="logForm(form.value)">
+  <label>Firstname:</label>
+  <input type="text" name="firstname" ngModel>
+
+  <label>Lastname:</label>
+  <input type="text" name="lastname" ngModel>
+
+  <label>Street:</label>
+  <input type="text" name="street" ngModel>
+
+  <label>Zip:</label>
+  <input type="text" name="zip" ngModel>
+
+  <label>City:</label>
+  <input type="text" name="city" ngModel>
+
+  <button type="submit">Submit</button>
+</form>
+

Great! If we now enter some values and submit the form, we’ll see that our application will log something like this:

+
{
+  firstname: 'Pascal',
+  lastname: 'Precht',
+  street: 'thoughtram Road',
+  zip: '00011',
+  city: 'San Francisco'
+}
+

Isn’t that cool? We can basically take this JSON object as it is and send it straight to a remote server for whatever we want to do with it. Oh wait, what if we actually want to have some more structure and make our form object look like this?

+
{
+  name: {
+    firstname: 'Pascal',
+    lastname: 'Precht',
+  },
+  address: {
+    street: 'thoughtram Road',
+    zip: '00011',
+    city: 'San Francisco'
+  }
+}
+

Do we now have to wire everything together by hand when the form is submitted? Nope! Angular has us covered - introducing ngModelGroup.

+

ngModelGroup Directive

+

ngModelGroup enables us to semantically group our form controls. In other words, there can’t be a control group without controls. In addition to that, it also tracks validity state of the inner form controls. This comes in very handy if we want to check the validity state of just a sub set of the form.

+

And if you now think: “Wait, isn’t a form then also just a control group”, then you’re right my friend. A form is also just a control group.

+

Let’s semantically group our form control values with ngModelGroup:

+
<fieldset ngModelGroup="name">
+  <label>Firstname:</label>
+  <input type="text" name="firstname" ngModel>
+
+  <label>Lastname:</label>
+  <input type="text" name="lastname" ngModel>
+</fieldset>
+
+<fieldset ngModelGroup="address">
+  <label>Street:</label>
+  <input type="text" name="street" ngModel>
+
+  <label>Zip:</label>
+  <input type="text" name="zip" ngModel>
+
+  <label>City:</label>
+  <input type="text" name="city" ngModel>
+</fieldset>
+

As you can see, all we did was wrapping form controls in <fieldset> elements and applied ngModelGroup directives to them. There’s no specific reason we used <fieldset> elements. We could’ve used <div>s too. The point is, that there has to be an element, where we add ngModelGroup so it will be registered at our ngForm instance.

+

We can see that it worked out by submitting the form and looking at the output:

+
{
+  name: {
+    firstname: 'Pascal',
+    lastname: 'Precht',
+  },
+  address: {
+    street: 'thoughtram Road',
+    zip: '00011',
+    city: 'San Francisco'
+  }
+}
+

Awesome! We now get the wanted object structure out of our form without writing any application code.

+

What about ngModel with expressions?

+

So ngModel is the thing in Angular that implements two-way data binding. It’s not the only thing that does that, but it’s in most cases the directive we want to use for simple scenarios. So far we’ve used ngModel as attribute directive without any value, but we might want to use it with an expression to bind an existing domain model to our form. This, of course works out of the box!

+

There are two ways to handle this, depending on what we want to do. One thing we can do is to apply property binding using the brackets syntax, so we can bind an existing value to a form control using one-way binding:

+
<fieldset ngModelGroup="name">
+  <label>Firstname:</label>
+  <input type="text" name="firstname" [ngModel]="firstname">
+
+  <label>Lastname:</label>
+  <input type="text" name="lastname" [ngModel]="lastname">
+</fieldset>
+

Whereas the corresponding model could look something like this:

+
@Component({
+  selector: 'app',
+  template: ...
+})
+class App {
+
+  firstname = 'Pascal';
+  lastname = 'Precht';
+
+  logForm(value: any) {
+    console.log(value);
+  }
+}
+

In addition, we can of course use ngModel and two-way data binding, in case we want to reflect the model values somewhere else in our template:

+
<fieldset ngModelGroup="name">
+  <label>Firstname:</label>
+  <input type="text" name="firstname" [(ngModel)]="firstname">
+  <p>You entered {{firstname}}</p>
+
+  <label>Lastname:</label>
+  <input type="text" name="lastname" [(ngModel)]="lastname">
+  <p>You entered {{lastname}}</p>
+</fieldset>
+

This just simply works as expected.

+

More to cover

+

Of course, this is really just the tip of the ice berg when it comes to building forms. We haven’t talked about validation yet and how to display error messages when the validity state of a form control or a control group changes. We will talk about these and other things in future articles. However, if you’re interested in how to build a custom validator in Angular, checkout this article.

+

Watch out for more articles on forms in Angular!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/05/13/angular-2-providers-using-map-literals.html b/angular/2016/05/13/angular-2-providers-using-map-literals.html new file mode 100644 index 000000000..4381d100b --- /dev/null +++ b/angular/2016/05/13/angular-2-providers-using-map-literals.html @@ -0,0 +1,85 @@ +Angular Providers using Map Literals | Articles by thoughtram

Angular

Angular Providers using Map Literals

ng-conf happened just one week ago and there were many announcements about all things Angular. While this is good, sometimes these big announcements cause smaller features to remain unseen, because nobody really talks about them. That’s why we want to discuss providers using Map literals in this article, which is basically a new way of defining providers that landed just recently in the code base.

+

Provider recap

+

So what is it all about? Well, if you’ve read our articles on Dependency Injection in Angular, you know that an injector needs something called a provider, that knows how to create an object of a certain type, or for a specific token.

+

In other words, if we ask for a service dependency in one of our components like this:

+
import { Component } from '@angular/core';
+import { MyService } from './my-service.service';
+
+@Component({
+  selector: 'my-component',
+  template: '{{myService.sayHello()}}'
+})
+class MyComponent {
+
+  constructor(private myService: MyService) {}
+}
+

We would run into an error, because even though, we import MyService and use that type to annotate our constructor parameter, there’s nothing that tells Angular (or the injector) what to do, when someone asks for something of that type. Of course, Angular could simply automagically call new on the given type and that’s it, but then there’s no way to replace the actual dependency with an object of a different type, or maybe even the way we want to construct a dependency (class vs. factory vs. value).

+

That’s why Angular has the concept of providers, which basically act as a sort of recipe that describe how an object of a certain type is created.

+
import { Component } from '@angular/core';
+import { MyService } from './my-service.service';
+@Component({
+  selector: 'my-component',
+  template: '{{myService.sayHello()}}',
+  providers: [MyService] // creates a provider for MyService
+})
+class MyComponent {
+
+  constructor(private myService: MyService) {}
+}
+

You might know that adding a provider as shown above is actually a shorthand syntax for developer ergonomics. The same logic can be expressed with this more verbose syntax:

+
import { provide } from '@angular/core';
+
+@Component({
+  ...
+  providers: [
+    provide(MyService, { useClass: MyService })
+  ]
+})
+class MyComponent {
+  ...
+}
+

This enables us to create objects of different types, or even use completely different ways of constructing objects like using a factory function, or simply injecting a value.

+
// creates instance of MyOtherService
+provide(MyService, { useClass: MyOtherService })
+
+// uses a factory function to create a dependency
+provide(MyService, { useFactory: () => return { foo: 'bar' } })
+
+// injects a simple value
+provide(MyService, { useValue: true })
+

This is already pretty cool and powerful, because we can control what gets injected where in our application, without changing the application code itself.

+

Providers using Map Literals

+

As mentioned earlier, there’s a little change that makes the more verbose syntax a bit more ergonomic again. We can now define providers using Map literals. A Map literal, in TypeScript (or JavaScript) is really just an object hash. So instead of using the provide() function (which we need to import first), to create a provider, we can configure our providers using simple object structures.

+

Here’s one of our earlier snippets, but this time we use Map literals:

+
@Component({
+  selector: 'my-component',
+  template: '{{myService.sayHello()}}',
+  providers: [
+    { provide: MyService, useClass: MyOtherService }
+  ]
+})
+class MyComponent {
+
+  constructor(private myService: MyService) {}
+}
+

Keep in mind that this is not a breaking change that has been introduced. It’s an additional syntax we can take advantage of. If you’re into saving key strokes, you might want to prefer Map literals over provide().

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/05/16/exploring-rx-operators-map.html b/angular/2016/05/16/exploring-rx-operators-map.html new file mode 100644 index 000000000..ebe09c3aa --- /dev/null +++ b/angular/2016/05/16/exploring-rx-operators-map.html @@ -0,0 +1,65 @@ +Exploring Rx Operators: map | Articles by thoughtram

Angular

Exploring Rx Operators: map

In Taking advantage of Observables Part one and two we already highlighted the importance of Observables in Angular. We believe that mastering Observables can make a key difference in how we write our applications. Well, if you agree, here are some good news! This article is the first of a series of posts where we’ll explore operators of the Reactive Extensions for JavaScript (RxJS) and their practical applications.

+

The first operator we want to explore is the most commonly used one: map.

+

Understanding the map operator

+

We’ve probably all used map before when we were working with arrays. The idea is that each +item in the collection will potentially be projected into a different value.

+

Here is a very simple example where an array of numbers is transformed so that each number is multiplied by 10.

+
let values = [1, 2, 3];
+
+let transformed = values.map(value => value * 10);
+
+//prints [10, 20, 30]
+console.log(transformed);
+

By now we may be wondering what that has to do with Observables and the map operator that is part of RxJS.

+

Observables are very much like arrays in a way. Well, they are actually more like Iterators but let’s not get lost in the details. The key point to understand is that both represent a sequence of values. The key difference is that with Arrays/Iterators you pull values out as you want to work with them whereas with Observables you get values pushed to you as they arrive.

+

It’s this similarity that allows us to take advantage of pretty much all operators that we know from the pull-based world and apply them to the push-based world.

+

map and Observables

+

Let’s start with a little demo. All we need is a simple <input> element to enter some text.

+
<input type="text" id="demo"/>
+

Then we create an Observable that emits every time that the value of our input changes.

+
let demoInput = document.querySelector('#demo')
+let obs = Rx.Observable.fromEvent(demoInput, 'input');
+
+// Activate the observable and log all 'pushed' events
+obs.subscribe(event => console.log(event));
+

The payload of the Observable is the plain old Event object that is provided by the input event of the browser. But that may not match what we are most interested in. What if we are more interested in the current value of the input? The map operator lets us project the payload of the Observable into something else. All it takes to project the payload is this tiny change.

+
let obs = Rx.Observable.fromEvent(demoInput, 'input')
+                       .map(e => e.target.value);
+

We can go on and chain map calls to project the data even further. For instance, it may be more convenient to work with a data structure that carries the value among with the length of the string.

+
let obs = Rx.Observable.fromEvent(demoInput, 'input')
+                       .map(e => e.target.value)
+                       .filter( value => value > 100 )
+                       .map(v => {
+                         return {
+                           value: v,
+                           length: v.length
+                         };
+                       });
+

Of course, we could have done the same in the first map call. But it’s sometimes more readable to break things into multiple steps. Notice that often we also use different operators in between of two map calls (e.g to filter something out).

+

At this point, you may think that Observables are really just a minor enhancement on the Observer or Promise patterns… better suited to handle a sequence of events rather than a single callback. And the .map() function certainly does not - at first glance - seem to offer any added-value. The power of Observables is revealed when you start using Rx operators to transform, combine, manipulate, and work with sequences of items emitted by Observables.

+

These operators allow you to compose asynchronous sequences together in a declarative manner with all the efficiency benefits of callbacks but without the drawbacks of nesting callback handlers that are typically associated with asynchronous systems.

+

We will see that in future articles. Watch out for the next article of this series where we’ll build upon this lesson with map() and take a look at the related flatMap operator.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2016/05/23/opaque-tokens-in-angular-2.html b/angular/2016/05/23/opaque-tokens-in-angular-2.html new file mode 100644 index 000000000..a9c859dfa --- /dev/null +++ b/angular/2016/05/23/opaque-tokens-in-angular-2.html @@ -0,0 +1,175 @@ +How to prevent name collisions in Angular providers | Articles by thoughtram

Angular

How to prevent name collisions in Angular providers

If you’ve read our article series on everything dependency injection in Angular, you’ve probably realized that Angular is doing a pretty good job on that. We can either use string or type tokens to make dependencies available to the injector. However, when using string tokens, there’s a possibility of running into naming collisions because… well, maybe someone else has used the same token for a different provider. In this article, we’re going to learn how so-called “opaque tokens” solve this problem.

+

UPDATE: Since Angular version 4.x OpaqueToken is considered deprecated in favour of InjectionToken. Learn about the differences here.

+

Before we jump into the actual problem we want to solve, let’s first recap the differences between a string token and a type token.

+

String Tokens vs Type Tokens

+

Angular DI injects singleton instances which are created by provider-registered factories. And it is these instances that are injected at runtime. In order to configure your application DI and associate a factory with a token, we have to setup providers. A couple weeks ago we blogged how providers can be created using Map literals, if you haven’t read this one yet we recommend to check it out, as this article pretty much builds up on that one.

+

The bottom line is that a provider token can be either a string or a type. Depending on our use case, we want to use one or the other. For example, if we have a DataService class, and all we want to do is inject an instance of that class when we ask for a dependency of that type, we would use DataService as a provider token like this:

+
@Component({
+  selector: 'my-component',
+  providers: [
+    {provide: DataService, useClass: DataService}
+  ]
+})
+class MyComponent {
+
+  constructor(private dataService: DataService) {}
+}
+

Since the token matches the dependency instance-type and the provider strategy is useClass, we can also use the shorthand version, as:

+
providers: [DataService]
+

Angular has many shorthand versions (DI, annotations, etc); and the above code is just one example of those.

+

Now this is cool, as long as we have classes (or types) to represent the things we want to work with. However, sometimes we need to create other objects that don’t necessarily need to be put in a class representation. We could, for example, have a configuration object that we want to inject into our application. This configuration object can be a simple object literal where there is no type involved.

+
const CONFIG = {
+  apiUrl: 'http://my.api.com',
+  theme: 'suicid-squad',
+  title: 'My awesome app'
+};
+

Or maybe, we have a primitive value we want to make injectable, like a string or a boolean value.

+
const FEATURE_ENABLED = true;
+

In these cases, we can’t use the String or Boolean type, as this would set a default value for the place where we ask for dependencies of these types. And we really don’t want to introduce a new type just to represent these values.

+

That’s where string tokens come into play. They allow us to make objects available via DI without introducing an actual type:

+
let featureEnabledToken = 'featureEnabled';
+let configToken = 'config';
+
+...
+providers: [
+  { provide: featureEnabledToken, useValue: FEATURE_ENABLED },
+  { provide: configToken, useValue: CONFIG }
+]
+

Basically all we do is, instead of using a type, we use a simple string as a token. We can inject this dependency using the @Inject() decorator likes this:

+
import { Inject } from '@angular/core';
+
+class MyComponent {
+
+  constructor(
+    @Inject(featureEnabledToken) private featureEnabled,
+    @Inject(configToken) private config
+  ) {...}
+}
+

Note: that above we used @Inject(featureEnabledToken) private featureEnabled without any typing information; e.g. private featureEnabled:boolean.

+

Okay awesome, we can use strings and types as tokens to inject dependencies in our application. Unfortunately, using string tokens like this opens up potential risks for naming collisions.

+

The problem with string tokens

+

Let’s get back to our config string token for a second. “config” is a pretty general name, so we probably could’ve done better naming this thing in the first place. However, even if we come up with a more distinguishable name, it is easily possible that someone else will use the same string as a token. Providers are flattened, meaning that, if there are multiple providers with the same token, the last one wins.

+

And there is another concept that allows us to define multiple providers for the same token. Those are multi providers, and we’ve written about them here.

+

Let’s say we use some sort of third-party library that comes with a set of providers defined like this:

+
export const THIRD_PARTY_LIB_PROVIDERS = [
+  { provide: 'config', useClass: ThirdParyConfig }
+];
+

Even though it’s not a common pattern to use a string token with a class, it’s totally possible to do that, but we really just want to demonstrate the problem here. We can import and use these third-party providers like this:

+
import THIRD_PARTY_LIB_PROVIDERS from './third-party-lib';
+
+...
+providers = [
+  DataService,
+  THIRD_PARTY_LIB_PROVIDERS
+]
+

Okay, so far so good. Very often, we don’t really know what’s defined behind other library providers unless we check out the documentation or the source code. Let’s assume we also don’t know this time, that there’s a string token for config, and we try to add our own provider like this:

+
...
+providers = [
+  DataService,
+  THIRD_PARTY_LIB_PROVIDERS,
+  { provide: configToken, useValue: CONFIG }
+]
+

This will pretty much break our third-party library, because now, the thing that gets injected for the config string token is a different object than what the library expects. We basically ran into a naming collision.

+

Opaque Tokens to the rescue

+

Luckily, Angular anticipated such scenarios. It comes with a type called OpaqueToken that basically allows us to create string-based tokens without running into any collisions.

+

Creating an OpaqueToken is easy. All we need to do is to import and use it. Here’s what the third-party providers collection looks like using OpaqueToken:

+
import { OpaqueToken } from '@angular/core';
+
+const CONFIG_TOKEN = new OpaqueToken('config');
+
+export const THIRDPARTYLIBPROVIDERS = [
+  { provide: CONFIG_TOKEN, useClass: ThirdPartyConfig }
+];
+

Of course, this is an implementation detail and we usually shouldn’t have to care about what APIs are used inside a third-party library. Let’s assume we’ve created an OpaqueToken for our config token as well.

+
import { OpaqueToken } from '@angular/core';
+import THIRDPARTYLIBPROVIDERS from './third-party-lib';
+
+const MY_CONFIG_TOKEN = new OpaqueToken('config');
+
+...
+providers = [
+  DataService,
+  THIRDPARTYLIBPROVIDERS,
+  { provide: MY_CONFIG_TOKEN, useValue: CONFIG }
+]
+

Running this code will show us that, even though our application seems to use the exact same string token for different providers, Angular’s DI is still smart enough to figure out which dependency we’re interested in. As if we’d have two different tokens. And technically, this is exactly what happens.

+

Why it works

+

If we take a look at the implementation of OpaqueToken we’ll realize that it’s just a simple class with only a .toString() method.

+
export class OpaqueToken {
+
+  constructor(private _desc: string) {}
+
+  toString(): string { return `Token ${this._desc}`; }
+}
+

toString() gets called when an error is thrown in case we’re asking for a dependency that doesn’t have a provider. All it does is add a tiny bit more information to the error message.

+

However, the secret sauce is, that we’re creating actual object instances of OpaqueToken as opposed to simple string primitives. That’s why Angular’s DI is able to distinguish between our opaque tokens, even though they are based on the same string, because these instances are never the same.

+

We can easily double-check the equality of two opaque tokens like this:

+
const TOKEN_A = new OpaqueToken('token');
+const TOKEN_B = new OpaqueToken('token');
+
+TOKEN_A === TOKEN_B // false
+

InjectionToken since Angular 4.x

+

Since Angular version 4.x there’s a new, even a little bit better, way of achieving this. InjectionToken does pretty much the same thing as OpaqueToken (in fact, it derives from it). However, it allows to attach type info on the token via TypeScript generics, plus, it adds a little bit of sugar that makes the developer’s life a bit more pleasant when creating factory providers that come with their own dependencies.

+

Let’s take a look at the following provider configuration for DataService:

+
const API_URL = new OpaqueToken('apiUrl');
+
+
+providers: [
+  {
+    provide: DataService,
+    useFactory: (http, apiUrl) => {
+      // create data service
+    },
+    deps: [
+      Http,
+      new Inject(API_URL)
+    ]
+  }
+]
+

We’re using a factory function that will create a DataService instance using http and apiUrl. To ensure Angular knows what http and apiUrl are, we add the corresponding DI token to the provider configuration’s deps property. Notice how we can just add the Http token. However, apiUrl is providing using an OpaqueToken, and since it since a type, we have to use the Inject() constructor (equivalent of @Inject() inside constructors).

+

While this works perfectly fine, developers often ran into errors when they forgot to call new Inject() on the token. That’s why since Angular 4.x we don’t have to do this anymore. We can replace all OpaqueToken instances with InjectionToken instances and everything would work exactly the same way, except for the fact that we don’t have to call new Inject() in factory provider dependencies anymore. Also, notice the generic. It’s the type of what the injector is going to return.

+

In other words, the code above can then be written like this:

+
const API_URL = new InjectionToken<string>('apiUrl'); // generic defines return value of injector
+
+
+providers: [
+  {
+    provide: DataService,
+    useFactory: (http, apiUrl) => {
+      // create data service
+    },
+    deps: [
+      Http,
+      API_URL // no `new Inject()` needed!
+    ]
+  }
+]
+

Cool right? As of version 4.x OpaqueToken is considered deprecated.

+

Conclusion

+

Opaque tokens are distinguishable and prevent us from running into naming collisions. In addition, they provide a bit better error messages. Whenever we create a token that is not a type, OpaqueToken should be used. If we’re using Angular in version >= 4.x, we use InjectionToken instead.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/06/08/component-relative-paths-in-angular-2.html b/angular/2016/06/08/component-relative-paths-in-angular-2.html new file mode 100644 index 000000000..fe72399c7 --- /dev/null +++ b/angular/2016/06/08/component-relative-paths-in-angular-2.html @@ -0,0 +1,292 @@ +Component-Relative Paths in Angular | Articles by thoughtram

Angular

Component-Relative Paths in Angular

Component-based development is Angular’s most-loved feature. By now you should be familiar with using the @Component decorators to create components. You should be familiar with the required metadata information such as selector and template.

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'contacts-header',
+  template: `
+    <nav class="navbar-fixed">
+      <span class="brand-logo center">Contacts</span>
+    </nav>
+  `,
+  styles: ['.navbar-fixed { position:fixed; }']
+})
+export class HeaderComponent implements OnInit {  }
+
+

If the above component syntax is new and strange, you should first review our article on Building a Zipping Component in Angular.

+
+

If you are familiar with these concepts and you happen to be lucky, your components load without any problems. Mostly likely, though, you have already encountered (or soon will) the dreaded, frustrating 404 component-loading errors:

+

component_url_404

+
+

These errors mean your template HTML or styles (CSS) cannot be loaded!

+
+

Let’s talk about why that happens and see how we can solve such problems in a way that is flexible and portable. Before we jump into the actual problem we want to solve, let’s first review two (2) types of custom component implementations.

+

Components with Inline Metadata

+

For every Angular component that we implement, we define not only an HTML template, but may also define the CSS styles that go with that template, specifying any selectors, rules, and media queries that we need.

+

One way to do this is to set the styles and template property in the component metadata. Consider a simple Header component:

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'contacts-header',
+  template: `
+    <nav class="navbar-fixed">
+      <div class="nav-wrapper">
+        <span class="brand-logo center">Contacts</span>
+      </div>
+    </nav>
+  `,
+  styles: ['.navbar-fixed { position:fixed; }']
+})
+export class HeaderComponent implements OnInit {
+}
+
+

We actually use this component in our Angular Master Class training.

+
+

Using this component is super easy. There are no external file dependencies as all the HTML and CSS is defined inline. Therefore we should never see [in the DevTools console] a 404 loading error for this component.

+

Components with External Assets

+

Another component feature allows us to load HTML and styles from external files: using URLs in the metadata configuration block. Refactoring a component’s code, HTML, and CSS into three separate files [in the same package] is a common Angular Best-Practice.

+
    +
  • header.component.ts
  • +
  • header.component.html
  • +
  • header.component.css
  • +
+

This practice of using external files is especially important when your HTML or CSS is non-trivial. And using external files keeps your *.ts logic files much cleaner and easier to maintain.

+

header.component.ts

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector   : 'contacts-header',
+  templateUrl: 'header.component.html',
+  styleUrls  : ['header.component.css']
+})
+export class HeaderComponent implements OnInit {
+}
+

header.component.css

+
.navbar-fixed {
+  position:fixed;
+}
+

header.component.html

+
<nav class="navbar-fixed">
+  <div class="nav-wrapper">
+    <span class="brand-logo center">Contacts</span>
+  </div>
+</nav>
+

Above we used URLs to specify external HTML and CSS resources. The important factor to note is that the URL required above [in header.component.ts] is not an absolute path. The path is actually relative to the application root: which is usually the location of the index.html web page that hosts the application.

+

So the above component - without any url path information - must be stored in the application root in order to avoid 404 errors. Wow, this does not scale well!

+
    +
  • What if we have many components all at the root level ?
  • +
  • What if my components are organized in distinct packages ?
  • +
  • What if we want to organize our components by feature or context ?
  • +
+
+

‘Organizing by feature’ is actually an Angular Style Guide - Best Practice

+
+

To explore the issue, let’s consider the scenario where our details component (and files) are in the src/app/header package. The urls used above ( header.component.html and header.component.css ) would cause the loader to fail and the developer would see the following 404 error in the developers console:

+

component_url_404

+

If path to the component HTML or CSS file is not valid, the EASY workaround is to add absolute paths to the URLs. Let’s briefly explore that idea:

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector   : 'contacts-header',
+  templateUrl: 'src/app/header/header.component.html',
+  styleUrls  : ['src/app/header/header.component.css']
+})
+export class HeaderComponent implements OnInit {
+}
+

This approach immediately triggers important questions and concerns:

+
    +
  • What if we move our components to other packages?
  • +
  • What if we want to reuse our components in other applications?
  • +
  • Can we not use using relative-paths in our components ?
  • +
+

Using absolute paths in our URLs for component HTML or CSS is a horrible idea and band-aid solution. Don’t do it!

+

no-gif

+

Components with Relative-Path URLs

+

In fact, we can use relative paths. But not the way you may first think… and not without understanding and accepting some constraints.

+

At first we might be tempted to try this:

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector   : 'contacts-header',
+  templateUrl: './header.component.html',
+  styleUrls  : ['./header.component.css']
+})
+export class HeaderComponent implements OnInit {
+}
+

We might expect that ./header.component.html is a path relative to the header.component.ts file and that component-relative paths should work, right?

+

Instead, we get another 404 - Not found - Exception.

+

Remember we noted that the paths are relative to the Application Root (at load time)? Since we are using the package src/app/header/header.component.*, then our files obviously are not at the app root. Even worse is a scenario where the deployed files have different structures than the originating source.

+

We could use a gulp or grunt task to deploy to a dist directory and have all our components in the dist root directory. Don’t deploy all your component files to the app root… OMG, that is another horrible idea! Don’t do it.

+

no-gif

+

Why Component-Relative Paths are not supported

+

At first this limitation seems like a real feature screw-up within Angular. But the bright minds at Google know [from experience] that developers can [and will] load the files and modules using many different methods:

+
    +
  • Loading each file explicitly with <script> tags
  • +
  • Loading from CommonJS packages
  • +
  • Loading from SystemJS
  • +
  • Loading using JSPM
  • +
  • Loading using WebPack
  • +
  • Loading using Browserify
  • +
+

There are so many ways developers can deploy their apps: bundled or unbundled, different module formats, etc. It simply is not possible for Angular to absolutely know where the files reside at runtime.

+

Agreeing on Constraints

+

If we decide on CommonJS formats AND we use a standard module loader, then we can use the module.id variable which contains the absolute URL of the component class [when the module file is actually loaded]: the exact syntax is moduleId : module.id.

+
+

This moduleId value is used by the Angular reflection processes and the metadata_resolver component to evaluate the fully-qualified component path before the component is constructed.

+
+

Let’s see how this works with the component using CommonJS, SystemJS, JSPM, and WebPack:

+
+
+

CommonJS

+

header.component.ts

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  moduleId: module.id,    // fully resolved filename; defined at module load time
+  selector: 'contacts-header',
+  templateUrl: 'header.component.html',
+  styleUrls: ['header.component.css']
+})
+export class HeaderComponent implements OnInit {
+}
+
+

Note: the above requires that your tsconfig.json file specifies commonjs; since module.id is a variable available when using that module format:

+
+

tsconfig.json

+
{
+  "compilerOptions": {
+    "module": "commonjs",
+    "target": "es5"
+  }
+}
+
+

SystemJS

+

If we decide to use SystemJS, we use __moduleName variable instead of the module.id variable:

+

header.component.ts

+
import { Component, OnInit } from '@angular/core';
+
+@Component({
+  moduleId: __moduleName,    // fully resolved filename; defined at module load time
+  selector: 'contacts-header',
+  templateUrl: 'header.component.html',
+  styleUrls: ['header.component.css']
+})
+export class HeaderComponent implements OnInit {
+}
+
+

JSPM

+

If we decide to use JSPM, we use the typescriptOptions configuration format in the config.js file:

+

config.js

+
SystemJS.config({
+  typescriptOptions: {
+    module: "commonjs",
+    emitDecoratorMetadata: true,
+    experimentalDecorators: true
+  },
+  transpiler: false,
+  baseURL: "/dist",
+  map: {
+    app: 'src',
+    typescript: 'node_modules/typescript/lib/typescript.js',
+    angular2: 'node_modules/angular2',
+    rxjs: 'node_modules/rxjs'
+  },
+  packages: {
+    app: {
+      defaultExtension: 'ts',
+      main: 'app.ts'
+    },
+    angular2: {
+      defaultExtension: 'js'
+    },
+    rxjs: {
+      defaultExtension: 'js'
+    }
+  }
+});
+
+

Note this solution requires the SystemJS Typescript Plugin to transcompile the typescript

+
+
+

WebPack

+

If we decide to use WebPack to bundle our files, we can use require or import to force Webpack to load the file contents and assign directly to the metadata property. This means that WebPack is loading the content instead of Angular’s runtime loader. See WebPack : An Introduction for more details.

+

With WebPack there are three (3) options available to load the component’s external HTML and CSS.

+
    +
  1. We can use require( ) to reference component-relative paths.
  2. +
+
import { Component } from '@angular/core';
+
+@Component({
+  selector: 'my-app',
+  template: require('./header.component.html'),
+  styles: [require('./header.component.css')]
+})
+export class HeaderComponent implements OnInit {
+}
+

It is important to note that here we are not using the templateUrl or styleUrls keys. Instead we are using require('...') to load the data and assign the file contents directly to the metadata object key template BEFORE the Angular component initializes.

+
    +
  1. As an alternative approach to require(...), we can instead use import headerTemplate from './header.component.html';:
  2. +
+
import { Component } from '@angular/core';
+
+import { Component }  from '@angular/core';
+import headerTemplate from './header.component.html';
+import headerStyle    from './header.component.css';
+
+@Component({
+  selector : 'my-app',
+  template : headerTemplate,
+  styles   : [headerStyle]
+})
+export class HeaderComponent implements OnInit {
+}
+
    +
  1. Finally, WebPack developers can load templates and styles at runtime by adding ./ at the beginning of the template, +styles, and styleUrls properties that reference *component-relative URLS.
  2. +
+
import { Component } from '@angular/core';
+
+@Component({
+  selector : 'my-app',
+  templateUrl: './header.component.html',
+  styleUrls: ['./header.component.css']
+})
+export class HeaderComponent implements OnInit { }
+
+

Behind the scenese, Webpack will actually still do a require to load the templates and styles. +But our markup remains clean and uncluttered.

+
+

Personally, I like (3) approach used with the moduleId property (as a backup). Thanks to Soós Gábor for his WebPack expertise and insight here.

+

Conclusion

+

The key lesson is to set the moduleId : module.id in the @Component decorator! Without the moduleId setting, Angular will look for our files in paths relative to the application root.

+
+

And don’t forget the "module": "commonjs" in your tsconfig.json.

+
+

The beauty of this component-relative-path solution is that we can (1) easily repackage our components and (2) easily reuse components… all without changing the @Component metadata.

+
+

The Angular Cli WebPack preview now uses use webpack instead of Broccoli. +And with WebPack, the Angular Cli defaults to using the component-relative path approach described here. #ftw

+
Written by  Author

Thomas Burleson

\ No newline at end of file diff --git a/angular/2016/06/14/routing-in-angular-2-revisited.html b/angular/2016/06/14/routing-in-angular-2-revisited.html new file mode 100644 index 000000000..a21ec1683 --- /dev/null +++ b/angular/2016/06/14/routing-in-angular-2-revisited.html @@ -0,0 +1,239 @@ +Routing in Angular revisited | Articles by thoughtram

Angular

Routing in Angular revisited

A long time ago we’ve written about routing in Angular and you’ve probably noticed that this article is deprecated due to many changes and rewrites that happened in the router module of Angular. Just recently, the Angular team announced yet another version of the new router, in which they considered all the gathered feedback from the community to make it finally sophisticated enough, so it’ll fulfill our needs when we build applications with Angular.

+

In this article we want to take a first look at the new and better APIs, touching on the most common scenarios when it comes to routing. We’re going to explore how to define routes, linking to other routes, as well as accessing route parameters. Let’s jump right into it!

+

Defining Routes

+

Let’s say we want to build a contacts application (in fact, this is what we do in our Angular Master Class). Our contacts application shows a list of contacts, which is our ContactsListComponent and when we click on a contact, we navigate to the ContactsDetailComponent, which gives us a detailed view of the selected contact.

+

A simplified version of ContactsListComponent could look something like this:

+
@Component({
+  selector: 'contacts-list',
+  template: `
+    <h2>Contacts</h2>
+    <ul>
+      <li *ngFor="let contact of contacts | async">
+        {{contact.name}}
+      </li>
+    </ul>
+  `
+})
+export class ContactsListComponent {
+  ...
+}
+

Let’s not worry about how ContactsListComponent gets hold of the contact data. We just assume it’s there and we generate a list using ngFor in the template.

+

ContactsDetailComponent displays a single contact. Again, we don’t want to worry too much about how this component is implemented yet, but a simplified version could look something like this:

+
@Component({
+  selector: 'contacts-detail',
+  template: `
+    <h2>{{contact.name}}</h2>
+
+    <address>
+      <span>{{contact.street}}</span>
+      <span>{{contact.zip}}</span>
+      <span>{{contact.city}}</span>
+      <span>{{contact.country}}</span>
+    </address>
+  `
+})
+export class ContactsDetailComponent {
+  ...
+}
+

Especially in ContactsDetailComponent there are a couple more things we need to consider when it comes to routing (e.g. how to link to that component, how to get access to URL parameters), but for now, the first thing we want to do is defining routes for our application.

+

Defining routes is easy. All we have to do is to create a collection of Route which simply follows an object structure that looks like this:

+
interface Route {
+  path?: string;
+  component?: Type|string;
+  ...
+}
+

As we can see, there are actually a couple more properties than just the three we show here. We’ll get to them later but this is all we need for now.

+

Routes are best defined in a separate module to keep our application easy to test and also to make them easier to reuse. Let’s define routes for our components in a new module (maybe contacts.routes.ts?) so we can add them to our application in the next step:

+
import { ContactsListComponent } from './contacts-list';
+import { ContactsDetailComponent } from './contacts-detail';
+
+export const ContactsAppRoutes = [
+  { path: '', component: ContactsListComponent },
+  { path: 'contacts/:id', component: ContactsDetailComponent }
+];
+

Pretty straight forward right? You might notice that the path property on our first Route definition is empty. This simply tells the router that this component should be loaded into the view by default (this is especially useful when dealing with child routes). The second route has a placeholder in its path called id. This allows us to have some dynamic value in our path which can later be accessed in the component we route to. Think of a contact id in our case, so we can fetch the contact object we want to display the details for.

+

The next thing we need to do is to make these routes available to our application. In fact, we first need to make sure there’s a router in our application at all. We do that by importing Angular RouterModule into our application module. But not only that, we also configure it with our routes using RouterModule.forRoot().

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+import { ContactsListComponent } from './contacts-list';
+import { ContactsDetailComponent } from './contacts-detail';
+import { ContactsAppComponent } from './app.component';
+import { ContactsAppRoutes } from './app.routes';
+
+@NgModule({
+  imports: [
+    BrowserModule,
+    RouterModule.forRoot(ContactsAppRoutes)
+  ],
+  declarations: [
+    ContactsAppComponent,
+    ContactsListComponent,
+    ContactsDetailComponent
+  ],
+  bootstrap: [ContactsAppComponent]
+})
+

You might wonder where ContactsAppComponent comes from. Well, this is just the root component we use to bootstrap our application. In fact, it doesn’t really know anything about our ContactsListComponent and ContactsDetailComponent. We’re going to take a look at ContactsAppComponent in the next step though.

+

Displaying loaded components

+

Okay cool, our application now knows about these routes. The next thing we want to do is to make sure that the component we route to, is also displayed in our application. We still need to tell Angular “Hey, here’s where we want to display the thing that is loaded!“.

+

For that, we take a look at ContactsAppComponent:

+
@Component({
+  selector: 'contacts-app',
+  template: `
+    <h1>Contacts App</h1>
+
+    <!-- here's where we want to load
+      the detail and the list view -->
+  `
+})
+export class ContactsAppComponent {
+  ...
+}
+

Nothing special going on there. However, we need to change that. In order to tell Angular where to load the component we route to, we need to use a directive called RouterOutlet. Since we’ve imported RouterModule into our application module, this directive is automatically available to us. Let’s add a <router-outlet> tag to our component’s template so that loaded components are displayed accordingly.

+
@Component({
+  ...
+  template: `
+    <h1>Contacts App</h1>
+    <router-outlet></router-outlet>
+  `
+})
+export class ContactsAppComponent {
+  ...
+}
+

Bootstrapping that app now displays a list of contacts! Awesome! The next thing we want to do is to link to ContactsDetailComponent when someone clicks on a contact.

+

Linking to other routes

+

With the new router, there are different ways to route to other components and routes. The most straight forward way is to simply use strings, that represent the path we want to route to. We can use a directive called RouterLink for that. For instance, if we want to route to ContactsDetailComponent and pass the contact id 3, we can do that by simply writing:

+
<a routerLink="/contacts/3">Details</a>
+

This works perfectly fine. RouterLink takes care of generating an href attribute for us that the browser needs to make linking to other sites work. And since we’ve already imported RouterModule, we can simply go ahead and use that directive without further things to do.

+

While this is great we realise very quickly that this isn’t the optimal way to handle links, especially if we have dynamic values that we can only represent as expressions in our template. Taking a look at our ContactsListComponent template, we see that we’re iterating over a list of contacts:

+
<ul>
+  <li *ngFor="let contact of contacts | async">
+    {{contact.name}}
+  </li>
+</ul>
+

We need a way to evaluate something like {% raw %}{{contact.id}}{% endraw %} to generate a link in our template. Luckily, RouterLink supports not only strings, but also expressions! As soon as we want to use expressions to generate our links, we have to use an array literal syntax in RouterLink.

+

Here’s how we could extend ContactsListComponent to link to ContactsDetailComponent:

+
@Component({
+  selector: 'contacts-list',
+  template: `
+    <h2>Contacts</h2>
+    <ul>
+      <li *ngFor="let contact of contacts | async">
+        <a [routerLink]="['/contacts', contact.id]">
+          {{contact.name}}
+        </a>
+      </li>
+    </ul>
+  `
+})
+export class ContactsListComponent {
+  ...
+}
+

There are a couple of things to note here:

+
    +
  • We use the bracket-syntax for RouterLink to make expressions work (if this doesn’t make sense to you, you might want to read out article on Angular’s Template Syntax Demystified
  • +
  • The expression takes an array where the first field is the segment that describes the path we want to route to and the second a dynamic value which ends up as route parameter
  • +
+

Cool! We can now link to ContactsDetailComponent. However, this is only half of the story. We still need to teach ContactsDetailComponent how to access the route parameters so it can use them to load a contact object.

+

Access Route Parameters

+

A component that we route to has access to something that Angular calls the ActivatedRoute. An ActivatedRoute is an object that contains information about route parameters, query parameters and URL fragments. ContactsDetailComponent needs exactly that to get the id of a contact. We can inject the ActivatedRoute into ContactsDetailComponent, by using Angular’s DI like this:

+
import { ActivatedRoute } from '@angular/router';
+
+@Component({
+  selector: 'contacts-detail',
+  ...
+})
+export class ContactsDetailComponent {
+
+  constructor(private route: ActivatedRoute) {
+
+  }
+}
+

ActivatedRoute comes with a params property which is an Observable. To access the contact id, all we have to do is to subscribe to the parameters Observable changes. Let’s say we have a ContactsService that takes a number and returns an observable that emits a contact object. Here’s what that could look like:

+
import { ActivatedRoute } from '@angular/router';
+import { ContactsService } from '../contacts.service';
+
+@Component({
+  selector: 'contacts-detail',
+  ...
+})
+export class ContactsDetailComponent {
+
+  contact: Contact;
+
+  constructor(
+    private route: ActivatedRoute,
+    private contactsService: ContactsService
+  ) {
+
+  }
+
+  ngOnInit() {
+    this.route.params
+      .map(params => params['id'])
+      .subscribe((id) => {
+        this.contactsService
+          .getContact(id)
+          .subscribe(contact => this.contact = contact);
+      });
+  }
+}
+

Oh, look what we have here! Notice that we’re nesting two subscribe() calls? This is usually an indicator that we can refactor our code using flatMap(), or even better switchMap(), as we’re calling an asynchronous API and want to deal with out-of-order responses.

+

Let’s refactor our code:

+
ngOnInit() {
+  this.route.params
+    .map(params => params['id'])
+    .switchMap(id => this.contactsService.getContact(id))
+    .subscribe(contact => this.contact = contact);
+}
+

Much better! But are observables really required to get hold of our id parameter?

+

Using Router Snapshots

+

Sometimes we’re not interested in future changes of a route parameter. All we need is this contact id and once we have it, we can provide the data we want to provide. In this case, an Observable can bit a bit of an overkill, which is why the router supports snapshots. A snapshot is simply a snapshot representation of the activated route. We can access the id parameter of the route using snapshots like this:

+
...
+  ngOnInit() {
+
+    this.contactsService
+        .getContacts(this.route.snapshot.params['id'])
+        .subscribe(contact => this.contact = contact);
+  }
+...
+

Check out the running example right here!

+

ParamMap since Angular version 4.x

+

Because router parameters can either have single or multiple values, the params type should actually be { [key: string]: string | string[] }. We as developers usually know if we expect a single or multiple values. That’s why since Angular version 4.x, there’s a new ParamMap interface that we can use to decide whether we we’re interested in a single value (ParamMap.get()) or multiple values (ParamMap.getAll()).

+

The above code can therefore be rewritten as:

+
...
+  ngOnInit() {
+
+    this.contactsService
+        .getContacts(this.route.snapshot.paramMap.get('id'))
+        .subscribe(contact => this.contact = contact);
+  }
+...
+

This feature has been introduce in this commit, go ahead and take a look for more information.

+

Of course, there’s way more to cover when it comes to routing. We haven’t talked about secondary routes or guards yet, but we’ll do that in our upcoming articles. Hopefully this one gives you an idea of what to expect from the new router. For a more in-depth article on the underlying architecture, you might want to read Victor’s awesome blog!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/06/16/cold-vs-hot-observables.html b/angular/2016/06/16/cold-vs-hot-observables.html new file mode 100644 index 000000000..5432b5d50 --- /dev/null +++ b/angular/2016/06/16/cold-vs-hot-observables.html @@ -0,0 +1,228 @@ +Cold vs Hot Observables | Articles by thoughtram

Angular

Cold vs Hot Observables

One of the most exciting topics around Angular is its relationship to Observables. It’s such an essential part of the framework that we cover a fair amount of Observable knowledge in our Angular Master Class. That said, the topic is so big that one could easily run a 3-day workshop on Observables itself.

+

There’s one particular area of Observables that we don’t explicitly touch on in our workshop but that keeps coming up in questions every single time that we teach them.

+

Hot vs Cold Observables

+

Understanding the nature of hot and cold Observables is a crucial part to master Observables. Before we try to explore the topic through some practical examples, let’s read through the definition from the RxJS project itself:

+
+

Cold observables start running upon subscription, i.e., the observable sequence only starts pushing values to the observers when Subscribe is called. (…) This is different from hot observables such as mouse move events or stock tickers which are already producing values even before a subscription is active.

+
+

Ok, so far so good. Let’s see what that means in practice.

+

We start with a basic Observable that simply emits the number 1. We make two independent subscriptions to the same Observable and print out the output with a prefix so that we can tell them apart.

+
let obs = Rx.Observable.create(observer => observer.next(1));
+
+obs.subscribe(v => console.log("1st subscriber: " + v));
+obs.subscribe(v => console.log("2nd subscriber: " + v));
+

When we run this code we’ll see the following output in the console.

+
1st subscriber: 1
+2nd subscriber: 1
+

Ok, cool. But the interesting question remains: is obs cold or hot? Let’s forget for a moment that we know how obs was created and imagine we would have obtained a reference to obs by calling getObservableFromSomewhere() instead. If that was the case, we wouldn’t be able to tell whether it’s cold or hot. And that’s one important thing to understand. It’s not always possible from the subscriber side to know whether you are dealing with a cold or hot Observable.

+

If we turn back to the definition that we cited in the beginning and think about what makes an Observable cold or hot, we can read between the lines and notice that if obs was cold it should produce fresh values upon subscription. But the pity is, with a value such as 1 we can’t easily tell whether it was created freshly upon subscription or not. So let’s replace 1 with Date.now() and see what happens.

+

If we run that code again we’ll notice that we actually get different output per subscription. Notice the change in the last digit.

+
1st subscriber: 1465990942935
+2nd subscriber: 1465990942936
+

With this output it is clear that there must have been two calls to observer.next(Date.now()). In other words, the Observable started producing the values upon each subscription which makes it cold by definition.

+

Making Cold Observables Hot

+

Now that we know that our Observable is clearly cold, let’s try to warm it up a little.

+
let obs = Rx.Observable
+            .create(observer => observer.next(Date.now()))
+            .publish();
+
+obs.subscribe(v => console.log("1st subscriber: " + v));
+obs.subscribe(v => console.log("2nd subscriber: " + v));
+
+obs.connect();
+

Let’s ignore the publish and connect operators for a moment and solely focus on the output.

+
1st subscriber: 1465994477014
+2nd subscriber: 1465994477014
+

Clearly we can see that there must have been only one call to observer.next(Date.now()) with this setup as the numbers are identical. So, is it hot now? Well, kind of. Let’s put it that way: it’s warmer than a cold one but colder than a really hot one. It’s hot in the sense that there’s no new value producer/source (the thing calling observer.next(val)) created upon subscription. But it’s cold in the sense that it doesn’t start producing values before the subscriptions exist.

+

We can fix that by moving the connect call before the subscriptions.

+
let obs = Rx.Observable
+            .create(observer => observer.next(Date.now()))
+            .publish();
+obs.connect();
+
+obs.subscribe(v => console.log("1st subscriber: " + v));
+obs.subscribe(v => console.log("2nd subscriber: " + v));
+

However, when we run this code we don’t see any output at all. And that’s expected behaviour because now obs is really really hot as in it produces values no matter if anyone listens or not. So by the time that our two subscribers start listening the value has long been pumped through.

+

In order to be able to understand the purpose of publish better, let’s use an Observable that will produce infinite values instead of just a single one.

+
let obs = Rx.Observable
+            .interval(1000)
+            .publish()
+            .refCount();
+
+obs.subscribe(v => console.log("1st subscriber:" + v));
+setTimeout(()
+  // delay for a little more than a second and then add second subscriber
+  => obs.subscribe(v => console.log("2nd subscriber:" + v)), 1100);
+

The setup we use is slightly more complex from what we had before, so let’s break it down.

+
    +
  1. We use interval(1000) to create an Observable that emits every second with an increasing index value starting at 0.
  2. +
  3. We use publish to share the value producer across several subscriptions (one indicator of being hot!)
  4. +
  5. We defer the second subscription by one second.
  6. +
+

Let’s just ignore refCount for now as we’re going to explain it later.

+

We see the following output as we run the script.

+
1st subscriber:0
+1st subscriber:1
+2nd subscriber:1
+1st subscriber:2
+2nd subscriber:2
+...
+

Clearly, we can see that the second subscriber is not starting over at 0. But that would have been the case if we didn’t use publish to share the value producing source across subscribers.

+

Understanding publish, refCount and connect

+

But how hot is obs in this scenario? Let’s make it visible by defering both subscriptions by another 2 seconds. If it’s really hot we shouldn’t see the numbers 0 and 1 at all because they would have been emitted before we start to listen, right?

+

Let’s try out and run this code instead.

+
let obs = Rx.Observable
+            .interval(1000)
+            .publish()
+            .refCount();
+
+setTimeout(() => {
+  // delay both subscriptions by 2 seconds
+  obs.subscribe(v => console.log("1st subscriber:" + v));
+  setTimeout(
+    // delay for a little more than a second and then add second subscriber
+    () => obs.subscribe(
+          v => console.log("2nd subscriber:" + v)), 1100);
+
+},2000);
+

Interestingly we see exactly the same output as in the previous experiment which means we are dealing with an Observable that is rather warm than really hot. And that’s because of the way refCount works. The publish operator creates an ConnectableObservable which means it creates an Observable that shares one single subscription to the underlying source. However, the publish operator doesn’t subscribe to the underlying source just yet. It’s more like a gatekeeper that makes sure that subscriptions aren’t made to the underlying source but to the ConnectableObservable instead.

+

It’s the job of the connect operator to actually cause the ConnectableObservable to subscribe to the underlying source (the thing that produces values). In our case we’re using refCount which is an operator that builds up on connect and causes the ConnectableObservable to subscribe to the underlying source as soon as there is a first subscriber and to unsubscribe from it as soon as there’s no subscriber anymore. It simply keeps track of how many subscriptions are made to the ConnectableObservable.

+

And this explains why we see values starting at 0 with our last experiment. It’s because the subscription to the underlying source is only made once that we have a first subscriber to the ConnectableObservable.

+

If we want obs to be truly hot, we have to call connect ourselves early on.

+
let obs = Rx.Observable
+            .interval(1000)
+            .publish();
+obs.connect();
+
+setTimeout(() => {
+  obs.subscribe(v => console.log("1st subscriber:" + v));
+  setTimeout(
+    () => obs.subscribe(v => console.log("2nd subscriber:" + v)), 1000);
+
+},2000);
+

Notice how we get values starting from 2 when we run this code.

+
1st subscriber:2
+1st subscriber:3
+2nd subscriber:3
+

This means we now have a truly hot Observable that produces values no matter if someone listens or not.

+

When to use what

+

As we’ve seen the question whether an Observable is hot or cold is everything but black and white. In fact there are several strategies how values may be pushed to subscribers that we didn’t even touch on yet. In general we can say that we should be dealing with a hot Observable whenever we subscribe to something that is generating values no matter if someone is listening or not. When we subscribe to such a hot Observable, we don’t see past values but only new ones that were generated after our subscription.

+

A typical example of a hot observable are mousemove events. The mouse moves happen regardless if someone is listening or not. When we start listening for them, we only get future events.

+

Cold Observables on the other hand are the lazy ones. They only start producing values when someone subscribes. But then again, it’s really not a black and white thing. An iced cold Observable starts reproducing the values it emits independently with every new subscriber as we’ve seen in the examples above. But how should we call an Observable that only starts generating values as the first subscriber subscribes and then shares and reemits the exact same values to every new subscriber? Things get blurry and categorizing only by cold and hot doesn’t really cut it for every possible use case.

+

As a rule of thumb, when you have a cold Observable and you want multiple subscribers to it, and you don’t want them to cause regenerating the values but rather reusing existing values, you need to start thinking about publish and friends.

+

Caveat: Http with Observables

+

The Http service in Angular >= 2.x returns cold Observables and the implications may surprise us.

+

Consider this simple component that requests a contacts.json file from a server and renders the contacts as a list in the template.

+
...
+@Component({
+  ...
+  template: `
+    <ul>
+      <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
+    </ul>
+    `
+  ...
+})
+export class AppComponent {
+  contacts: Observable<Array<any>>;
+  constructor (http: Http) {
+    this.contacts = http.get('contacts.json')
+                        .map(response => response.json().items);
+  }
+}
+

As we can see above, we’re exposing an Observable<Array<any>> to the template and subscribe to it from the template using the AsyncPipe.

+

The contacts.json contains a simple object with an items property holding the collection of contacts.

+
{
+  "items": [
+    { "name": "John Conner" },
+    { "name": "Arnold Schwarzenegger" }
+  ]
+}
+

Now the interesting question is: What happens if we’d subscribe to it twice by adding another list to our template?

+
1st List
+<ul>
+  <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
+</ul>
+2st List
+<ul>
+  <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
+</ul>
+

If we run that code and take a look at the network tab of our browser, we’ll notice that the browser fetches contacts.json twice! And that’s in stark contrast to what we’re used to when working with Promises for example. In fact, simply importing the toPromise operator and adding it as the last operator to our Observable causes the second request to vanish.

+

But let’s not fall back to Promises just yet. With all our knowledge regarding cold and hot Observables we should be able to simply fix our Observable so that it shares one single subscription to the underlying source - the Observable that issues the HTTP call.

+
this.contacts = http.get('contacts.json')
+                    .map(response => response.json().items)
+                    .publish()
+                    .refCount();
+

That should do it, right? Well, almost. We can see that there’s no second request anymore. But there’s still a big issue that makes the code behave quite differently from what we are used with Promises.

+

Consider we’d want the second list to show up after a delay of 500ms. We could change the code to this.

+
@Component({
+  ...
+  template: `
+    1st List
+    <ul>
+      <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
+    </ul>
+    2st List
+    <ul>
+      <li *ngFor="let contact of contacts2 | async">{{contact.name}}</li>
+    </ul>
+    `,
+  ...
+})
+export class AppComponent {
+  contacts: Observable<Array<any>>;
+  contacts2: Observable<Array<any>>;
+  constructor (http: Http) {
+    this.contacts = http.get('contacts.json')
+                        .map(response => response.json().items)
+                        .publish()
+                        .refCount();
+
+    setTimeout(() => this.contacts2 = this.contacts, 500);
+  }
+}
+

Notice that we are still using our one and only Observable but we are reexposing it as contacts2 after 500ms. But when we run the code, we notice that our second list isn’t showing up!

+

If you think about it, it makes perfect sense. With the use of publish we caused the Observable to share a single subscription to the underlying source - we made it hot. But maybe we made it a little too hot. Now when we have a second subscriber starting to listen after 500ms it will only get notified about new values that arrive after its subscription. What we rather want is that new subscribers see exactly the old values that were already emitted earlier. Fortunately we can have exactly that behaviour by using publishLast instead of publish.

+
this.contacts = http.get('contacts.json')
+                    .map(response => response.json().items)
+                    .publishLast()
+                    .refCount();
+

Now when we run that code we see our second list after 500ms but we still don’t see a second request. In other words, we created an Observable that emits upon subscription with the data that was fetched with the first request.

+

Useful shortcut

+

Since using publish and refCount together is such a useful pattern there is an operator that combines them, and it’s called .share()

+
this.contacts = http.get('contacts.json')
+  .map(response => response.json().items)
+  .share();
+

Note: When using multiple async pipes on streams with default values, the .share() operator might cause problems:

+

The share() will publish the first value of the stream on the first subscription. The first async pipe will trigger that subscription and get that initial value. The second async pipe however will subscribe after that value has already been emitted and therefore miss that value.

+

The solution for this problem is the .shareReplay(1) operator, which will keep track of the previous value of the stream. That way all the async pipes will get the last value. Just like the share() operator is a shortcut for publish().refCount(), the shareReplay(1) operator is a shortcut for publishReplay(1).refCount(). We can see the difference between share() and shareReplay(1) in the following plunk. +We should always pass 1 as the parameter value to the shareReplay function. Otherwise RxJS will keep track of all the values of that observable, when we only need the last one.

+

It’s important to note that this problem will only occur when using streams with initial values. +The share operator would not pose problems with regular http requests for instance.

+

Whew! We came a long way. We hope this gives you a clearer picture of what the term hot vs cold actually means when it comes to Observables.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2016/06/22/model-driven-forms-in-angular-2.html b/angular/2016/06/22/model-driven-forms-in-angular-2.html new file mode 100644 index 000000000..ed8ceabb3 --- /dev/null +++ b/angular/2016/06/22/model-driven-forms-in-angular-2.html @@ -0,0 +1,207 @@ +Reactive Forms in Angular | Articles by thoughtram

Angular

Reactive Forms in Angular

Just a couple days ago, we’ve updated our article on Template-driven Forms in Angular, as the APIs have changed for the better in the second release candidate. We think it’s time to talk about model-driven and reactive forms on our blog. This article builds on top of the knowledge we’ve gained in our previous article, so you might want to take a look at it in case you haven’t yet.

+

The goal of reactive/model-driven forms

+

While template-driven forms in Angular give us a way to create sophisticated forms while writing as little JavaScript as possible, model-driven forms enable us to test our forms without being required to rely on end-to-end tests. That’s why model-driven forms are created imperatively, so they end up as actually properties on our components, which makes them easier to test.

+

In addition, it’s important to understand that when building reactive/model-driven forms, Angular doesn’t magically create the templates for us. So it’s not that we just create the form model and then all of a sudden we have DOM generated in our app that renders the form. Reactive forms are more like an addition to template-driven forms, although, depending on what we’re doing some things can be left out here and there (e.g. validators on DOM elements etc.).

+

FormGroup and FormControl

+

Let’s start off again with the same form we used in our previous article, a form to register a new user on a platform:

+
<form>
+  <label>Firstname:</label>
+  <input type="text">
+
+  <label>Lastname:</label>
+  <input type="text">
+
+  <label>Street:</label>
+  <input type="text">
+
+  <label>Zip:</label>
+  <input type="text">
+
+  <label>City:</label>
+  <input type="text">
+
+  <button type="submit">Submit</button>
+</form>
+

Nothing special going on here. We simply ask for a firstname, a lastname and some address information. Now, to make this form model-driven, what we need to do is to create a form model that represents that DOM structure in our component. One way to do that is to use the low level APIs for FormGroup and FormControl.

+

FormGroup always represents a set of FormControls. In fact, a form is always a FormGroup. Let’s create the corresponding form model for our template:

+
import { Component } from '@angular/core';
+import { FormGroup, FormControl } from '@angular/forms';
+
+@Component({
+  selector: 'my-app',
+  ...
+})
+export class AppComponent {
+
+  registerForm = new FormGroup({
+    firstname: new FormControl(),
+    lastname: new FormControl(),
+    address: new FormGroup({
+      street: new FormControl(),
+      zip: new FormControl(),
+      city: new FormControl()
+    })
+  });
+}
+

Uff, that looks quite wordy! We’ll fix that in a second but let’s first discuss what happened. We created a component property registerForm which represents the FormGroup, which is our form. For each field in the form, we’ve created a FormControl and what we can’t see here, is that a FormControl takes a string as first argument in case we want to prefill the form control with a default value.

+

Another nice thing to notice is that FormGroup’s can be nested. address is also a collection of form controls, even though it doesn’t show up in the DOM (yet). We’ll see in a second why that is.

+

Okay, now that we’ve created our first form model, let’s associate it to our template.

+

Binding forms using formGroup, formGroupName and formControlName

+

Currently, there’s nothing in our code that tells Angular that our form model is responsible for the form template. We need to associate the model to our form, and we can do that using the formGroup directive, which takes an expression that evaluates to a FormGroup instance.

+

In order to use that directive we need to import the ReactiveFormsModule into our application module:

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@anglar/platform-browser';
+import { ReactiveFormsModule } from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, ReactiveFormsModule],
+  declarations: [AppComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

Now we can go ahead and use formGroup to connect the model with the form template:

+
<form [formGroup]="registerForm">
+  ...
+</form>
+

Great, the next thing we need to do is to associate the form controls to the model as well, because there’s nothing that tells Angular “Hey these form controls here belong to your form control models!“.

+

This is where the formControlName directive comes into play. It’s pretty much the equivalent of an ngModel and name attribute combination in template-driven forms. Each form control gets a formControlName directive applied so we can register the controls on the outer form:

+
<form [formGroup]="registerForm">
+  <label>Firstname:</label>
+  <input type="text" formControlName="firstname">
+
+  <label>Lastname:</label>
+  <input type="text" formControlName="lastname">
+
+  <label>Street:</label>
+  <input type="text" formControlName="street">
+
+  <label>Zip:</label>
+  <input type="text" formControlName="zip">
+
+  <label>City:</label>
+  <input type="text" formControlName="city">
+
+  <button type="submit">Submit</button>
+</form>
+

And last but not least, since address is created as a FormGroup as well, we can associate a group of form controls to that model using formGroupName. However, we need a surrounding element for that, otherwise there’s no place where we can apply that directive. Let’s surround the address fields with a <fieldset> element and apply formGroupName there.

+
<fieldset formGroupName="address">
+  <label>Street:</label>
+  <input type="text" formControlName="street">
+
+  <label>Zip:</label>
+  <input type="text" formControlName="zip">
+
+  <label>City:</label>
+  <input type="text" formControlName="city">
+</fieldset>
+

It’s time to make our code a bit more readable and replace FormGroup and FormControl with FormBuilder.

+

FormBuilder

+

As mentioned earlier, the creation of our form model looks quite wordy as we have to call new FormGroup() and new FormControl several times to construct the model. Luckily, we’ve used rather low level APIs and we can use a higher level API that makes this task a bit more pleasant.

+

FormBuilder is like a factory that creates FormGroup’s and FormControl’s for us. All we need to do is to import it and us its .group() method like this:

+
import { FormBuilder, FormGroup } from '@angular/forms';
+
+@Component({
+  selector: 'my-app',
+  ...
+})
+export class AppComponent implements OnInit {
+
+  registerForm: FormGroup;
+
+  constructor(private formBuilder: FormBuilder) {}
+
+  ngOnInit() {
+    this.registerForm = this.formBuilder.group({
+      firstname: '',
+      lastname: '',
+      address: this.formBuilder.group({
+        street: '',
+        zip: '',
+        city: ''
+      })
+    });
+  }
+}
+

This looks way better! Let’s recap really quick what happened:

+
    +
  • We imported FormBuilder
  • +
  • We injected it into AppComponent
  • +
  • We created the form model using FormBuilder.group() in ngOnInit() lifecycle
  • +
  • We haven’t done any changes on the template
  • +
+

Adding Validators

+

Now that we have the form model set up, we can start adding validators to our form controls. There are different ways to add them, we can either add then as directives to the template or to the FormControl instance in our model.

+

Let’s say we want to add a validators that makes sure that firstname and lastname is set. Angular comes with a Validators class that has some common validators built-in. We can import and apply them right away:

+
import { 
+  FormBuilder,
+  FormGroup,
+  Validators
+} from '@angular/forms';
+
+...
+
+ngOnInit() {
+  this.registerForm = this.formBuilder.group({
+    firstname: ['', Validators.required],
+    lastname: ['', Validators.required],
+    address: this.formBuilder.group({
+      street: [],
+      zip: [],
+      city: []
+    })
+  });
+}
+

A FormControl takes a value as first, a synchronous validator as second and an asynchronous validator as third parameter. We can also pass a collection of validators which causes Angular to compose them for us. And all we do here is applying a synchronous validator (Validators.required) to our form controls.

+

We can access the validity state of a form control like this:

+
<form [formGroup]="registerForm">
+  <label>Firstname:</label>
+  <input type="text" formControlName="firstname">
+  <p *ngIf="registerForm.controls.firstname.errors">This field is required!</p>
+
+  ...
+</form>
+

We get a reference to the form control by traversing the registerForm instance. If you’re interested in learning how to build a custom validator, you might want to read our article on Custom Validators in Angular.

+

Forms with a single control

+

Sometimes, we don’t need a FormGroup, as our form might only consist of a single form control. Think of a search field that let’s you search for products in an e-commerce application. Technically, we don’t even need a <form> element for that.

+

Angular comes with a directive formControl which doesn’t have to be inside a formGroup. We can simply add it to a single form control and are ready to go:

+
<!-- no surrounding form -->
+<input type="search" [formControl]="seachControl">
+

The cool thing about form controls in Angular is, that we can listen reactively for changes that are happening to that control. Every form controls exposes an Observable propery valuesChanges() that we can subscribe to. So in order to get notified about changes in the example above, all we have to do is:

+
@Component()
+export class SearchComponent implements OnInit {
+
+  searchControl = new FormControl();
+
+  ngOnInit() {
+    this.searchControl.valueChanges.subscribe(value => {
+      // do something with value here
+    });
+  }
+}
+

Hopefully this one gave you a better idea of how reactive/model-driven and template-driven forms relate to each other.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/07/18/guards-in-angular-2.html b/angular/2016/07/18/guards-in-angular-2.html new file mode 100644 index 000000000..9da45fb3f --- /dev/null +++ b/angular/2016/07/18/guards-in-angular-2.html @@ -0,0 +1,123 @@ +Protecting Routes using Guards in Angular | Articles by thoughtram

Angular

Protecting Routes using Guards in Angular

In our last article, Routing in Angular revisited, we talked about the latest changes in the router APIs. While we covered how to set up basic routes, access parameters and link to other components, we haven’t really talked about more sophisticated use cases like protecting routes.

+

Protecting routes is a very common task when building applications, as we want to prevent our users from accessing areas that they’re not allowed to access, or, we might want to ask them for confirmation when leaving a certain area. Angular’s router provides a feature called Navigation Guards that try to solve exactly that problem. In this article, we’d like to take a look at the different types of guards and how to implement them for actual use cases.

+

Guard Types

+

There are four different guard types we can use to protect our routes:

+
    +
  • CanActivate - Decides if a route can be activated
  • +
  • CanActivateChild - Decides if children routes of a route can be activated
  • +
  • CanDeactivate - Decides if a route can be deactivated
  • +
  • CanLoad - Decides if a module can be loaded lazily
  • +
+

Depending on what we want to do, we might need to implement one or the other guard. In some cases, we even need to implement all of them. Let’s take a look at how to define guards.

+

Defining Guards

+

Guards can be implemented in different ways, but after all it really boils down to a function that returns either Observable<boolean>, Promise<boolean> or boolean. In addition, guards are registered using providers, so they can be injected by Angular when needed.

+

As Functions

+

To register a guard we need to define a token and the guard function. Here’s what a super simple guard implementation could look like:

+
@NgModule({
+  ...
+  providers: [
+    provide: 'CanAlwaysActivateGuard',
+    useValue: () => {
+      return true;
+    }
+  ],
+  ...
+})
+export class AppModule {}
+

As we can see, it’s really just a provider with some made up token that resolves to a guard function that returns true (if provider doesn’t mean anything to you, go and check out our article on Dependency Injection in Angular). Since it’s always returning true, this guard is not protecting anything, as it will always activate the route that uses it. However, this is really just to demonstrate a guard implementation. We also notice that we’re using a string token, which works fine but what we really want is an OpaqueToken to not run into name collisions.

+

Once a guard is registered with a token, we can use it in our route configuration. The following route configuration has the CanAlwaysActivateGuard attached, which gets executed when routing to that specific route.

+
export const AppRoutes:RouterConfig = [
+  { 
+    path: '',
+    component: SomeComponent,
+    canActivate: ['CanAlwaysActivateGuard']
+  }
+];
+

As we can see, all we need to do is to define a list of guard tokens that should be called. This also implies that we can have multiple guards protecting a single route. Guards are executed in the order they are defined on the route.

+

As Classes

+

Sometimes, a guard needs dependency injection capabilities. In these cases, it makes sense to define a guard as a class, because dependencies can then be simply injected. Let’s say we want to protect a route and have the user authenticate first. We might want to inject an AuthService to determine if the user is authenticated or not. A class guard would be a perfect fit.

+

When creating a guard class, we implement either the CanActivate, CanDeactivate, or CanActivateChild interface, which requires us to have a method canActivate(), canActivateChild(), or canDeactivate() respectively. Those methods are pretty much the equivalent of a guard function in the previous scenario. The following snippet shows a simple CanActivate guard implementation using classes.

+
import { Injectable } from '@angular/core';
+import { CanActivate } from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class CanActivateViaAuthGuard implements CanActivate {
+
+  constructor(private authService: AuthService) {}
+
+  canActivate() {
+    return this.authService.isLoggedIn();
+  }
+}
+

Pretty straight forward. An injectable class with a canActivate() method that now has access to injected dependencies. Angular will call that method for us when a guard is implemented as a class. Just like the previous guard, this one needs to be registered as a provider:

+
@NgModule({
+  ...
+  providers: [
+    AuthService,
+    CanActivateViaAuthGuard
+  ]
+})
+export class AppModule {}
+

And can then be used on a route:

+
{ 
+  path: '',
+  component: SomeComponent,
+  canActivate: [
+    'CanAlwaysActivateGuard',
+    CanActivateViaAuthGuard
+  ]
+}
+

Deactivating Routes

+

We’ve now seen how CanActivate can work in different scenarios, but as mentioned earlier, we have a few more guard interfaces we can take advantage of. CanDeactivate gives us a chance to decide if we really want to navigate away from a route. This can be very useful, if for example we want to prevent our users from losing unsaved changes when filling out a form and accidently clicking on a button to cancel the process.

+

The CanDeactivate guard also has access to the instance of the active component. With this we can take the user experience even to a higher level. Instead of asking an (unwanted) user confirmation every time, we can do this conditionally by checking if change were made. In the sample below the CanDeactivateComponent implements a methods hasChanges(). This returns a boolean value indicating if the components has detected any changes. This can be done by checking the dirty state of the form, keeping track of the previous model and compare it with the current one, … What ever fits your needs.

+

Implementing a CanDeactivate guard is very similar to implementing a CanActivate guard. All we have to do is to create again, either a function, or a class that implements the CanDeactivate interface. We can implement a super simple safety net for our users like this:

+
import { CanDeactivate } from '@angular/router';
+import { CanDeactivateComponent } from './app/can-deactivate';
+
+export class ConfirmDeactivateGuard implements CanDeactivate<CanDeactivateComponent> {
+
+  canDeactivate(target: CanDeactivateComponent) {
+    if(target.hasChanges()){
+        return window.confirm('Do you really want to cancel?');
+    }
+    return true;
+  }
+}
+

Even though, this is a very trivial implementation, there’s one thing that we didn’t see in the previous example. CanDeactivate<T> uses a generic, so we need to specify what component type we want to deactivate. Honestly, we’re not sure if this is a bug or not. But other than that, it’s very clear what’s going on here. We implement a method canDeactivate(), that is called by Angular’s router internally if needed. Last but not least, also this guard needs to be registered accordingly:

+
@NgModule({
+  ...
+  providers: [
+    ...
+    ConfirmDeactivateGuard
+  ]
+})
+export class AppModule {}
+

Conclusion

+

Guards are great. They enable us to protect certain routes or even protect the user from losing data. In addition, we can have multiple guards protecting a single route, which helps us implementing sophisticated use cases, where a chain of different checks is needed.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/07/27/custom-form-controls-in-angular-2.html b/angular/2016/07/27/custom-form-controls-in-angular-2.html new file mode 100644 index 000000000..bc50aeb5c --- /dev/null +++ b/angular/2016/07/27/custom-form-controls-in-angular-2.html @@ -0,0 +1,493 @@ +Custom Form Controls in Angular | Articles by thoughtram

Angular

Custom Form Controls in Angular

There are many things that Angular helps us out with when creating forms. We’ve covered several topics on Forms in Angular, like model-driven forms and template-driven forms. If you haven’t read those articles yet, we highly recommend you to do so as this one is based on them. Almero Steyn, one of our training students, who later on contributed to the official documentation as part of the Docs Authoring Team for Angular, has also written a very nice introduction to creating custom controls.

+

His article inspired us and we would like to take it a step further and explore how to create custom form controls that integrate nicely with Angular’s form APIs.

+

Custom form control considerations

+

Before we get started and build our own custom form control, we want to make sure that we have an idea of the things come into play when creating custom form controls.

+

First of all, it’s important to realise that we shouldn’t just create custom form controls right away, if there’s a native element (like <input type="number">) that perfectly takes care of the job. It seems native form elements are often underestimated in what they are capable of. While we often just see a text box that we can type into, it does way more work for us. Every native form element is accessible, some inputs have built-in validation and some even provide an improved user experience on different platforms (e.g. mobile browsers).

+

So whenever we think of creating a custom form control we should ask ourselves:

+
    +
  • Is there a native element that has the same semantics?
  • +
  • If yes, can we simply rely on that element and use CSS and/or progressive enhancement to change its appearance/behaviour to our needs?
  • +
  • If not, what will the custom control look like?
  • +
  • How can we make it accessible?
  • +
  • Does it behave differently on different platforms?
  • +
  • How does it validate?
  • +
+

There are probably more things to consider, but these are the most important ones. If we do decide to create a custom form control (in Angular), we should make sure that:

+
    +
  • It properly propagates changes to the DOM/View
  • +
  • It properly propagates changes to the Model
  • +
  • It comes with custom validation if needed
  • +
  • It adds validity state to the DOM so it can be styled
  • +
  • It’s accessible
  • +
  • It works with template-driven forms
  • +
  • It works with model-driven forms
  • +
  • It needs to be responsive
  • +
+

We will discuss different scenarios through out this article to demonstrate how these things can be implemented. We will not cover accessibility in this article though, as there’ll be follow-up articles to talk about that in-depth.

+

Creating a custom counter

+

Let’s start off with a rather simple counter component. The idea is to have a component that lets us increment and decrement a model value. And yes, if we think about the things to consider, we probably realise that an <input type="number"> would do the trick.

+

However, in this article we want to demonstrate how to implement a custom form control and a custom counter component seems trivial enough to make things look not too complicated. In addition, our counter component will have a different appearance that should work the same across all browsers, which is where we might reach the boundaries of a native input element anyways.

+

We start off with the raw component. All we need is a model value that can be changed and two buttons that cause the change.

+
import { Component, Input } from '@angular/core';
+
+@Component({
+  selector: 'counter-input',
+  template: `
+    <button (click)="increment()">+</button>
+    {{counterValue}}
+    <button (click)="decrement()">-</button>
+  `
+})
+class CounterInputComponent {
+
+  @Input()
+  counterValue = 0;
+
+  increment() {
+    this.counterValue++;
+  }
+
+  decrement() {
+    this.counterValue--;
+  }
+}
+

Nothing special going on here. CounterInputComponent has a model counterValue that is interpolated in its template and can be incremented or decremented by the increment() and decrement() methods respectively. This component works perfectly fine, we can already use it once declared on our application module, as it is by putting it into another component like this:

+

app.module.ts

+
@NgModule({
+  imports: [BrowserModule],
+  declarations: [AppComponent, CounterInputComponent],
+  bootstrap: [AppComponent]
+})
+export class AppModule {}
+

app.component.ts

+
import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-component',
+  template: `
+    <counter-input></counter-input>
+  `,
+})
+class AppComponent {}
+

Okay cool, but now we want to make it work with Angular’s form APIs. Ideally, what we end up with is a custom control that works with template-driven forms and reactive/model-driven forms. For example, in the most simple scenario, we should be able to create a template-driven form like this:

+
<!-- this doesn't work YET -->
+<form #form="ngForm" (ngSubmit)="submit(form.value)">
+  <counter-input name="counter" ngModel></counter-input>
+  <button type="submit">Submit</button>
+</form>
+

If that syntax is new to you, check out our article on Template-Driven forms in Angular. Okay but how do we get there? We need to learn what a ControlValueAccessor is, because that’s the thing that Angular uses to build a bridge between a form model and a DOM element.

+

Understanding ControlValueAccessor

+

While our counter component works, there’s currently no way we can connect it to an outer form. In fact, if we try to bind any kind of form model to our custom control, we’ll get an error that there’s a missing ControlValueAccessor. And that’s exactly what we need to enable proper integration with forms in Angular.

+

So, what is a ControlValueAccessor? Well, remember the things we talked about earlier that are needed to implement a custom form control? One of the things we need to make sure is that changes are propagated from the model to the view/DOM, and also from the view, back to the model. This is what a ControlValueAccessor is for.

+

A ControlValueAccessor is an interface that takes care of:

+
    +
  • Writing a value from the form model into the view/DOM
  • +
  • Informing other form directives and controls when the view/DOM changes
  • +
+

The reason why Angular has such an interface, is because the way how DOM elements need to be updated can vary across input types. For example, a normal text input has a value property that needs to be written to, whereas a checkbox comes with a checked property that needs to be updated. If we take a look under the hood, we realise that there’s a ControlValueAccessor for every input type which knows how to update its view/DOM.

+

There’s the DefaultValueAccessor that takes care of text inputs and textareas, the SelectControlValueAccessor that handles select inputs, or the CheckboxControlValueAccessor, which, surprise, deals with checkboxes, and many more.

+

Our counter component needs a ControlValueAccessor that knows how to update the counterValue model and inform the outside world about changes too. As soon as we implement that interface, it’ll be able to talk to Angular forms.

+

Implementing ControlValueAccessor

+

The ControlValueAccessor interface looks like this:

+
export interface ControlValueAccessor {
+  writeValue(obj: any) : void
+  registerOnChange(fn: any) : void
+  registerOnTouched(fn: any) : void
+}
+

writeValue(obj: any) is the method that writes a new value from the form model into the view or (if needed) DOM property. This is where we want to update our counterValue model, as that’s the thing that is used in the view.

+

registerOnChange(fn: any) is a method that registers a handler that should be called when something in the view has changed. It gets a function that tells other form directives and form controls to update their values. In other words, that’s the handler function we want to call whenever counterValue changes through the view.

+

registerOnTouched(fn: any) Similiar to registerOnChange(), this registers a handler specifically for when a control receives a touch event. We don’t need that in our custom control.

+

A ControlValueAccessor needs access to its control’s view and model, which means, the custom form control itself has to implement that interface. Let’s start with writeValue(). First we import the interface and update the class signature.

+
import { ControlValueAccessor } from '@angular/forms';
+
+@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+}
+

Next, we implement writeValue(). As mentioned earlier, it takes a new value from the form model and writes it into the view. In our case, all we need is updating the counterValue property, as it’s interpolated automatically.

+
@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+  writeValue(value: any) {
+    this.counterValue = value;
+  }
+}
+

This method gets called when the form is initialized, with the form model’s initial value. This means it will override the default value 0, which is fine but if we think about the simple form setup we talked about earlier, we realise that there is no initial value in the form model:

+
<counter-input name="counter" ngModel></counter-input>
+

This will cause our component to render an empty string. As a quick fix, we only set the value when it’s not undefined:

+
writeValue(value: any) {
+  if (value !== undefined) {
+    this.counterValue = value;
+  }
+}
+

Now, it only overrides the default when there’s an actual value written to the control. Next, we implement registerOnChange() and registerOnTouched(). registerOnChange() has access to a function that informs the outside world about changes. Here’s where we can do special work, whenever we propagate the change, if we wanted to. registerOnTouched() registers a callback that is excuted whenever a form control is “touched”. E.g. when an input element blurs, it fire the touch event. We don’t want to do anything at this event, so we can implement the interface with an empty function.

+
@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+  propagateChange = (_: any) => {};
+
+  registerOnChange(fn) {
+    this.propagateChange = fn;
+  }
+
+  registerOnTouched() {}
+}
+

Great, our counter input now implements the ControlValueAccessor interface. The next thing we need to do is to call propagateChange() with the value whenever counterValue changes through the view. In other words, if either the increment() or decrement() button is clicked, we want to propagate the new value to the outside world.

+

Let’s update these methods accordingly.

+
@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+  increment() {
+    this.counterValue++;
+    this.propagateChange(this.counterValue);
+  }
+
+  decrement() {
+    this.counterValue--;
+    this.propagateChange(this.counterValue);
+  }
+}
+

We can make this code a little better using property mutators. Both methods, increment() and decrement(), call propagateChange() whenever counterValue changes. Let’s use getters and setters to get rid off the redudant code:

+
@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+  @Input()
+  _counterValue = 0; // notice the '_'
+
+  get counterValue() {
+    return this._counterValue;
+  }
+
+  set counterValue(val) {
+    this._counterValue = val;
+    this.propagateChange(this._counterValue);
+  }
+
+  increment() {
+    this.counterValue++;
+  }
+
+  decrement() {
+    this.counterValue--;
+  }
+}
+

CounterInputComponent is almost ready for prime-time. Even though it implements the ControlValueAccessor interface, there’s nothing that tells Angular that it should be considered as such. We need to register it.

+

Registering the ControlValueAccessor

+

Implementing the interface is only half of the story. As we know, interfaces don’t exist in ES5, which means once the code is transpiled, that information is gone. So after all, it happens that our component implements the interface, but we still need to make Angular pick it up as such.

+

In our article on multi-providers in Angular we learned that there are some DI tokens that Angular uses to inject multiple values, to do certain things with them. For example there’s the NG_VALIDATORS token that gives Angular all registered validators on a form control, and we can add our own validators to it.

+

In order to get hold of a ControlValueAccessor for a form control, Angular internaly injects all values that are registered on the NG_VALUE_ACCESSOR token. So all we need to do is to extend the multi-provider for NG_VALUE_ACCESSOR with our own value accessor instance (which is our component).

+

Let’s do that right away:

+
import { Component, Input, forwardRef } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+@Component({
+  ...
+  providers: [
+    { 
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => CounterInputComponent),
+      multi: true
+    }
+  ]
+})
+class CounterInputComponent {
+  ...
+}
+

If this code doesn’t make any sense to you, you should definitely check out our article on multi-providers in Angular, but the bottom line is, that we’re adding our custom value accessor to the DI system so Angular can get an instance of it. We also have to use useExisting because CounterInputComponent will be already created as a directive dependency in the component that uses it. If we don’t do that, we get a new instance as this is how DI in Angular works. The forwardRef() call is explained in this article.

+

Awesome, our custom form control is now ready to be used!

+

Using it inside template-driven forms

+

We’ve already seen that the counter component works as intended, but now we want to put it inside an actual form and make sure it works in all common scenarios.

+

Activating form APIs

+

As discussed in our article on template-driven forms in Angular, we need to activate the form APIs like this:

+
import { FormsModule} from '@angular/forms';
+
+@NgModule({
+  imports: [BrowserModule, FormsModule], // we're add FormsModule here
+  ...
+})
+export class AppModule {}
+

Without model initialization

+

That’s pretty much it! Remember our AppComponent from ealier? Let’s create a template-driven form in it and see if it works. Here’s an example that uses the counter control without initializing it with a value (it will use its own internal default value which is 0):

+
@Component({
+  selector: 'app-component',
+  template: `
+    <form #form="ngForm">
+      <counter-input name="counter" ngModel></counter-input>
+    </form>
+
+    <pre>{{ form.value | json }}</pre>
+  `
+})
+class AppComponent {}
+
+

Special Tip: Using the json pipe is a great trick to debug a form’s value.

+
+

form.value returns the values of all form controls mapped to their names in a JSON structure. That’s why JsonPipe will out put an object literal with a counter field of the value that the counter has.

+

Model initialization with property binding

+

Here’s another example that binds a value to the custom control using property binding:

+
@Component({
+  selector: 'app-component',
+  template: `
+    <form #form="ngForm">
+      <counter-input name="counter" [ngModel]="outerCounterValue"></counter-input>
+    </form>
+
+    <pre>{{ form.value | json }}</pre>
+  `
+})
+class AppComponent {
+  outerCounterValue = 5;  
+}
+

Two-way data binding with ngModel

+

And of course, we can take advantage of ngModel’s two-way data binding implementation simply by changing the syntax to this:

+
<p>ngModel value: {{outerCounterValue}}</p>
+<counter-input name="counter" [(ngModel)]="outerCounterValue"></counter-input>
+

How cool is that? Our custom form control works seamlessly with the template-driven forms APIs! Let’s see what that looks like when using model-driven forms.

+

Using it inside model-driven forms

+

The following examples use Angular’s reactive form directives, so don’t forget to add ReactiveFormsModule to AppModule as discussed in this article.

+

Binding value via formControlName

+

Once we’ve set up a FormGroup that represents our form model, we can bind it to a form element and associate each control using formControlName. This example binds a value to our custom form control from a form model:

+
@Component({
+  selector: 'app-component',
+  template: `
+    <form [formGroup]="form">
+      <counter-input formControlName="counter"></counter-input>
+    </form>
+
+    <pre>{{ form.value | json }}</pre>
+  `
+})
+class AppComponent implements OnInit {
+
+  form: FormGroup;
+
+  constructor(private fb: FormBuilder) {}
+
+  ngOnInit() {
+    this.form = this.fb.group({
+      counter: 5
+    });
+  }
+}
+

Adding custom validation

+

One last thing we want to take a look at is how we can add validation to our custom control. In fact, we’ve already written an article on custom validators in Angular and everything we need to know is written down there. However, to make things more clear we’ll add a custom validator to our custom form control by example.

+

Let’s say we want to teach our control to become invalid when its counterValue is greater than 10 or smaller than 0. Here’s what it could look like:

+
import { NG_VALIDATORS, FormControl } from '@angular/forms';
+
+@Component({
+  ...
+  providers: [
+    { 
+      provide: NG_VALIDATORS,
+      useValue: (c: FormControl) => {
+        let err = {
+          rangeError: {
+            given: c.value,
+            max: 10,
+            min: 0
+          }
+        };
+
+        return (c.value > 10 || c.value < 0) ? err : null;
+      },
+      multi: true
+    }
+  ]
+})
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+}
+

We register a validator function that returns null if the control value is valid, or an error object when it’s not. This already works great, we can display an error message accordingly like this:

+
<form [formGroup]="form">
+  <counter-input
+    formControlName="counter"
+    ></counter-input>
+</form>
+
+<p *ngIf="!form.valid">Counter is invalid!</p>
+<pre>{{ form.value | json }}</pre>
+

Making the validator testable

+

We can do a little bit better though. When using model-driven forms, we might want to test the component that has the form without the DOM. In that case, the validator wouldn’t exist, as it’s provided by the counter input component. This can be easily fixed by extracting the validator function into its own declaration and exporting it, so other modules can import it when needed.

+

Let’s change our code to this:

+
export function validateCounterRange(c: FormControl) {
+  let err = {
+    rangeError: {
+      given: c.value,
+      max: 10,
+      min: 0
+    }
+  };
+
+  return (c.value > 10 || c.value < 0) ? err : null;
+}
+
+@Component({
+  ...
+  providers: [
+    { 
+      provide: NG_VALIDATORS,
+      useValue: validateCounterRange,
+      multi: true
+    }
+  ]
+})
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+}
+
+
+

Special Tip: To make validator functions available to other modules when building reactive forms, it’s good practice to declare them first and reference them in the provider configuration.

+
+
+

Now, the validator can be imported and added to our form model like this:

+
import { validateCounterRange } from './counter-input';
+
+@Component(...)
+class AppComponent implements OnInit {
+  ...
+  ngOnInit() {
+    this.form = this.fb.group({
+      counter: [5, validateCounterRange]
+    });
+  }
+}
+

This custom control is getting better and better, but wouldn’t it be really cool if the validator was configurable, so that the consumer of the custom form control can decide what the max and min range values are?

+

Making the validation configurable

+

Ideally, the consumer of our custom control should be able to do something like this:

+
<counter-input
+  formControlName="counter"
+  counterRangeMax="10"
+  counterRangeMin="0"
+  ></counter-input>
+

Thanks to Angular’s dependency injection and property binding system, this is very easy to implement. Basically what we want to do is to teach our validator to have dependencies.

+

Let’s start off by adding the input properties.

+
import { Input } from '@angular/core';
+...
+
+@Component(...)
+class CounterInputComponent implements ControlValueAccessor {
+  ...
+  @Input()
+  counterRangeMax;
+
+  @Input()
+  counterRangeMin;
+  ...
+}
+

Next, we somehow have to pass these values to our validateCounterRange(c: FormControl), but per API it only asks for a FormControl. That means we need to create that validator function using a factory that creates a closure like this:

+
export function createCounterRangeValidator(maxValue, minValue) {
+  return function validateCounterRange(c: FormControl) {
+    let err = {
+      rangeError: {
+        given: c.value,
+        max: maxValue,
+        min: minValue
+      }
+    };
+
+    return (c.value > +maxValue || c.value < +minValue) ? err: null;
+  }
+}
+

Great, we can now create the validator function with the dynamic values we get from the input properties inside our component, and implement a validate() method that Angular will use to perform validation:

+
import { Input, OnInit } from '@angular/core';
+...
+
+@Component(...)
+class CounterInputComponent implements ControlValueAccessor, OnInit {
+  ...
+
+  validateFn:Function;
+
+  ngOnInit() {
+    this.validateFn = createCounterRangeValidator(this.counterRangeMax, this.counterRangeMin);
+  }
+
+  validate(c: FormControl) {
+    return this.validateFn(c);
+  }
+}
+

This works but introduces a new problem: validateFn is only set in ngOnInit(). What if counterRangeMax or counterRangeMin change via their bindings? We need to create a new validator function based on these changes. Luckily there’s the ngOnChanges() lifecycle hook that, allows us to do exactly that. All we have to do is to check if there are changes on one of the input properties and recreate our validator function. We can even get rid off ngOnInit() again, because ngOnChanges() is called before ngOnInit() anyways:

+
import { Input, OnChanges } from '@angular/core';
+...
+
+@Component(...)
+class CounterInputComponent implements ControlValueAccessor, OnChanges {
+  ...
+
+  validateFn:Function;
+
+  ngOnChanges(changes) {
+    if (changes.counterRangeMin || changes.counterRangeMax) {
+      this.validateFn = createCounterRangeValidator(this.counterRangeMax, this.counterRangeMin);
+    }
+  }
+  ...
+}
+

Last but not least, we need to update the provider for the validator, as it’s now no longer just the function, but the component itself that performs validation:

+
@Component({
+  ...
+  providers: [
+    ...
+    { 
+      provide: NG_VALIDATORS,
+      useExisting: forwardRef(() => CounterInputComponent),
+      multi: true
+    }
+  ]
+})
+class CounterInputComponent implements ControlValueAccessor, OnInit {
+  ...
+}
+

Believe it or not, we can now configure the max and min values for our custom form control! If we’re building template-driven forms, it simply looks like this:

+
<counter-input
+  ngModel
+  name="counter"
+  counterRangeMax="10"
+  counterRangeMin="0"
+  ></counter-input>
+

This works also with expressions:

+
<counter-input
+  ngModel
+  name="counter"
+  [counterRangeMax]="maxValue"
+  [counterRangeMin]="minValue"
+  ></counter-input>
+

If we’re building model-driven forms, we can simply use the validator factory to add the validator to the form control like this:

+
import { createCounterRangeValidator } from './counter-input';
+
+@Component(...)
+class AppComponent implements OnInit {
+  ...
+  ngOnInit() {
+    this.form = this.fb.group({
+      counter: [5, createCounterRangeValidator(10, 0)]
+    });
+  }
+}
+

Check out the demos below!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/09/14/bypassing-providers-in-angular-2.html b/angular/2016/09/14/bypassing-providers-in-angular-2.html new file mode 100644 index 000000000..811ed58bd --- /dev/null +++ b/angular/2016/09/14/bypassing-providers-in-angular-2.html @@ -0,0 +1,91 @@ +Bypassing Providers in Angular | Articles by thoughtram

Angular

Bypassing Providers in Angular

We covered a lot of different things regarding dependency injection in Angular. However, at our latest training, one of our students came up with a very interesting question:

+
+
+

“Can I bypass a provider to get a dependency from another ancestor provider?”

+
+
+

This was then followed by a very interesting, collaborative discussion with the other students, as we all tried to come up with a solution - and it turned out, there is a solution. In this article we’d like to quickly demonstrate the problem and then show how we can use one of Angular’s provider recipes to solve it in a very elegant way.

+

Understanding the Problem

+

As discussed in other articles, dependencies in Angular are singletons inside their injector containers they belong to. If we need multiple dependency instances, we can take advantage of the injector tree, and provide different instances via different providers.

+

To illustrate what that means, let’s take a look at the following figure:

+

+

What we see here is a tree of components, which is usually what an application in Angular is composed of. We also see that every component comes with its own injector. This allows us to configure how and what is going to be created when we ask for dependencies, on a component level.

+

Let’s say we have an application where we use a DataService to perform actions like fetching data, adding data and deleting data. To make this service injectable, we need to create a provider for it first.

+
class DataService {} // this is usually imported from somewhere
+
+@NgModule({
+  ...
+  providers: [DataService]
+})
+export class AppModule {}
+

Once we created the provider, we can ask for dependencies of that type in our components like this:

+
@Component()
+export class SomeComponent {
+  
+  constructor(private contactService: DataService) {}
+}
+

In fact, all components in our component tree will now get exactly the same instance, because their injectors will keep looking upwards in the tree for a provider, until they find one.

+

+

We can get a different instance of the same service by adding another provider with the same configuration to the injector tree. Providers can be defined on components as well, to configure the corresponding injector.

+
@Component({
+  ...
+  providers: [DataService]
+})
+export class SomeOtherComponent {}
+

This will affect the dependency lookup in the sense that all children components and the SomeOtherComponent component itself will get the DataService instance from SomeOtherComponent’s injector, instead of the one configured in the NgModule.

+

+

As we can see, all components in the left part of the tree get their dependency instance from a different provider than the components in the right part of the tree. Okay cool, nothing new here, this has all been discussed in our guide on DI in Angular.

+

However, now we have a problem. What if we want to get a dependency instance of the root provider (or just another ancestor), essentially bypassing the nearest provider, even though our component is in the left part of the tree? To illustrate the problem, here another figure:

+

+

With the current setup, both providers use the exact same token, so there’s no way for us we can distinguish between the two different dependency instances.

+

Luckily, Angular comes with a couple more provider strategies (useValue, useFactory, …), that define how dependencies are created. One of them is useExisting, and we’ll now take a look at how it solves our problem.

+

Creating alias tokens with useExisting

+

useExisting is a bit different than the other provider strategies. It’s the only strategy that doesn’t actually create an instance, but instead, it points to another token which in turn will create the instance.

+

To give an example, let’s say we want to be able to not only use the DataService type as a token to ask for the dependency, but also the token RootDataService. We can easily do that with the following provider configuration (as always, we can do the same in @Component decorators):

+
class DataService {} // this is usually imported from somewhere
+class RootDataService {} // alias token, also usually imported from somewhere
+
+@NgModule({
+  ...
+  providers: [
+    DataService,
+    { provide: RootDataService, useExisting: DataService }
+  ]
+})
+export class AppModule {}
+

What this does is, it tells Angular when someone asks for a dependency for the token RootDataService, inject the dependency instance that is created for the token DataService. Or in other words, we just created an alias that gives us the exact same instance that we get for the DataService token.

+

We can then go ahead and use the alias token to inject our service instance just like this:

+
@Component()
+export class SomeComponent {
+  
+  constructor(private contactService: RootDataService) {}
+}
+

Again, this is the exact same instance. Now, why is this helpful? Well… now that we have two different tokens to get the same instance, it’s no longer a problem that another provider in the injector tree “shadows” the provider from our root injector. We can now ask for the dependency instance of DataService that is created at the very top of our tree, no matter where we are in the component tree, because the alias token still points to the original instance.

+

One more thing we could do is to get rid off the class definition RootDataService. We only created it so we can use it as a token, other than that, there’s no use for it. Luckily, we can also use strings to create tokens, or even better, we use OpaqueTokens.

+

To see things in action, check out the demos below!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/09/15/angular-2-final-is-out.html b/angular/2016/09/15/angular-2-final-is-out.html new file mode 100644 index 000000000..581d2f38b --- /dev/null +++ b/angular/2016/09/15/angular-2-final-is-out.html @@ -0,0 +1,121 @@ +Angular 2 is out - Get started here | Articles by thoughtram

Angular

Angular 2 is out - Get started here

Last night, one of the biggest announcement in the Angular history has happened. Brad Green announced that Angular 2 Final is out!

+

+ + + a2 final announcement + +

+

Even though it took about 2.5 years to reach this milestone, this is really just the start. There are plans to release the Angular Material implementation for Angular 2, broader support for server-side rendering, more guides and much much more. Head over to the official blog to learn more about the future plans (we’re super excited that we made it into the list of contributors, next to all the great people who made Angular 2 happen). Or, check out Juri’s great write-up of the event.

+

Where to start?

+

Now that the core Angular 2 is considered stable, you might want to jump in and learn about it but aren’t really sure where to start. There are many good resources like the official docs, or Victor Savkin’s articles.

+

We’ve been writing about Angular 2 too, in fact, since over a year by now and we have 30+ articles. This can be a bit overwhelming, which is why we’ve created a little resource guide to get you started.

+

Getting Started

+ +

Routing

+ +

Upgrading

+ +

Angular and ES5

+ +

Dependency Injection

+ +

Angular and Observables

+ +

Forms

+ +

Change Detection

+ +

Animations

+ +

Styling and ViewEncapsulation

+ +

We hope this is useful to you and let us know if you want to have a certain topic covered on our blog!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/09/16/angular-2-animation-important-concepts.html b/angular/2016/09/16/angular-2-animation-important-concepts.html new file mode 100644 index 000000000..239ac281d --- /dev/null +++ b/angular/2016/09/16/angular-2-animation-important-concepts.html @@ -0,0 +1,247 @@ +Angular Animations - Foundation Concepts | Articles by thoughtram

Angular

Angular Animations - Foundation Concepts

Animations features often are scary goals for developers. And Angular’s doctrine

+
+

”… controllers should not directly modify DOM elements!”

+
+

made Animation features intimidating as hell. But Angular animations are not scary! Templates are closely associated/integrated with @Component. We will notice that animations following a similar pattern.

+

Let’s build a component that hides and shows its contents, uses fade animation effects, and allows external components to easily trigger those fade effects.

+

Our Scenario

+

Here is a simple Angular component with hide and show features. This sample, however, does not have animations (yet):

+
@Component({
+  selector: 'my-fader',
+    template: `
+    <div *ngIf="visibility == 'shown'" >
+      <ng-content></ng-content>
+      Can you see me? 
+    </div>
+  `
+})
+export class MyComponent implements OnChanges {
+  visibility = 'shown';
+
+  @Input() isVisible : boolean = true;
+
+  ngOnChanges() {
+   this.visibility = this.isVisible ? 'shown' : 'hidden';
+  }
+}
+

This component simply publishes an @Input() isVisible property; which allows other external components to show/hide the text content… without any animations.

+

Enable Animations Module

+

Since Angular 4.x, there’s a new module BrowserAnimationsModule that introduces all animation capabilities. That’s why we first have add that to our application module’s imports like this:

+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+@NgModule({
+  imports: [
+    ...
+    BrowserAnimationsModule
+  ]
+  ...
+})
+export class AppModule {}
+

Once that is done, we can use any kind of animation API in our application. If we want to use animation function like trigger() or state(), we need to import them also from '@angular/platform-browser/animations' instead of '@angular/core'.

+

Configure Component Animations

+

We want the my-fader component to fade-in or fade-out its text content. And we want to animate those fades effects.

+

To start animating, let’s first add animation metadata to our component.

+
@Component({
+  ...,
+  template : ``,
+  animations: [
+    ...
+  ]
+)]
+class MyComponent() { ... }
+

Above we show that the animations metadata property is defined in the @Component decorator. Just like template metadata property!

+

Since our component has a visibility property whose state can change between shown and hidden, let’s configure animations to trigger and animate during each value change.

+
animations: [
+  trigger('visibilityChanged', [
+  state('shown' , style({ opacity: 1 })), 
+  state('hidden', style({ opacity: 0 }))
+  ])
+]
+

Before we add more metadata, let’s talk about the syntax above. What does it mean… and why is this syntax used?

+

The techno-speak above is saying that when the visibilityChanged property changes and the value == shown, then the target element opacity changes to 1. And when the value changes to == hidden, the target element opacity should change to 0.

+
+

Note: The [@visibilityChanged] binding is on <div> child content element in the <my-fader> component. It is NOT on the <my-fader> element itself. In other words, the animation target in our example is actually the <div> element; not the component host element.

+
+

Now, you might also wonder where visibilityChanged comes from? Because our component property is just called visibility. Hold your wild horses Mr. StageCoach, we’ll clarify that soon!” Let’s first talk about animation durations.

+

We want to animate these changes over a time duration instead of instantly hiding/showing the content. We need to configure a transition to specify animation durations. With Angular this is also suprisingly easy:

+
animations: [
+  trigger(’visibilityChanged', [
+    state('shown' , style({ opacity: 1 })), 
+    state('hidden', style({ opacity: 0 })),
+    transition('* => *', animate('.5s'))
+  ])
+]
+

With the above transition, we added information to the animation configuration so each trigger value change will have a 500 msec transition.

+

So a fade-in (opacity 0 -> 1, with a duration of 500 msec) will occur when the value changes from hidden to shown. And likewise the fade-out (opacity 1 -> 0, with a duration of 500 msec) will occur when the value changes from shown to hidden. By the way, you could also have used animate('500ms') to indicate the millsecond duration explicitly.

+

And what does the transition('* => *', ...) mean? Think of * => * as a transition from one state to another state; where * is a wildcard to mean any state value. If we wanted the fade-out to be slower than the fade-in, here is how we would configure the animation metadata:

+
animations: [
+  trigger(’visibilityChanged', [
+    state('shown' , style({ opacity: 1 })),
+    state('hidden', style({ opacity: 0 })),
+    transition('shown => hidden', animate('600ms')),
+    transition('hidden => shown', animate('300ms')),
+  ])
+]
+

See how easy this is? This notation is so easy to understand.

+

The Essential Concept

+

The essential take-away Animation concept is that Angular Animations are triggered on component state changes. And developers should consider state changes to be equivalent to value changes in a property of the component instance.

+

super-cool

+

Linking Animation to the Component

+
+While we configured the Animation metadata, I am sure you are wondering: +
    +
  • How is the animation property visibilityChanged actually connected to the component?
  • +
  • How are the animations linked to the component’s properties?
  • +
+

Since data-binding features are already supported between the component and its template, Angular uses a special template animation syntax to support triggering the animation after data-binding changes. So in the component template, we can do this:

+
<div [@visibilityChanged]="visibility">
+  Can you see me? I should fade in or out...
+</div>
+

Above the @visibilityChanged is the special template animation property and it uses databinding [@visibilityChanged]=“visibility” to bind the component’s visibility property to the animation trigger property visibilityChanged.

+

Here is the entire component definition updated with Animation features:

+
import { Component, OnChanges, Input } from '@angular/core';
+import { trigger, state, animate, transition, style } from '@angular/platform-browser/animations';
+
+@Component({
+  selector : 'my-fader',
+  animations: [
+  trigger('visibilityChanged', [
+    state('shown' , style({ opacity: 1 })),
+    state('hidden', style({ opacity: 0 })),
+    transition('* => *', animate('.5s'))
+  ])
+  ],
+  template: `
+  <div [@visibilityChanged]="visibility" >
+    <ng-content></ng-content>  
+    <p>Can you see me? I should fade in or out...</p>
+  </div>
+  `
+})
+export class FaderComponent implements OnChanges {
+  @Input() isVisible : boolean = true;
+  visibility = 'shown';
+
+  ngOnChanges() {
+   this.visibility = this.isVisible ? 'shown' : 'hidden';
+  }
+}
+

Reducing Complexity

+

What if - instead of the using the extra visibility property - you just wanted to use the isVisible property directly? This would obviate ngOnChanges() and reduce the code complexity to:

+
@Component({
+  animations: [
+    trigger('visibilityChanged', [
+      state('shown' , style({ opacity: 1 })),
+      state('hidden', style({ opacity: 0 })),
+      transition('* => *', animate('.5s'))
+    ])
+  ],
+  template: `
+  <div [@visibilityChanged]="isVisible" >
+       ...
+  </div>
+  `
+})
+export class FaderComponent {
+  @Input() isVisible : boolean = true;
+}
+

I love the tersity of this code. But this will not work without another important change to the animation metadata!

+
+

Remember that the @visibilityChanged animation trigger property has defined states for the values: shown or hidden.

+
+

If you use the myFader::isVisible boolean property, then your animation state values must be changed to true and false since those are the possible values of the isVisible property.

+
import { Component, OnChanges, Input } from '@angular/core';
+import { trigger, state, animate, transition, style } form '@angular/platform-browser/animations';
+
+@Component({
+  selector : 'my-fader',
+  animations: [
+    trigger('visibilityChanged', [
+      state('true' , style({ opacity: 1, transform: 'scale(1.0)' })),
+      state('false', style({ opacity: 0, transform: 'scale(0.0)'  })),
+      transition('1 => 0', animate('300ms')),
+      transition('0 => 1', animate('900ms'))
+    ])
+  ],
+  template: `
+  <div [@visibilityChanged]="isVisible" >
+    <ng-content></ng-content>
+    <p>Can you see me? I should fade in or out...</p>
+  </div>
+  `
+})
+export class FaderComponent implements OnChanges {
+  @Input() isVisible : boolean = true;
+}
+
+

Extra Bonus: The demo has some extra features. The host my-fader element now has a purple background; when you hide the my-fader content children you will see the host background. This change was added so you can visually see the differences between the host and the target animation elements.

+
+

Our Animation Workflow

+

Above we have an improved the component definition; enhanced with animation features. Here is a workflow of the [animation] process:

+
    +
  1. the input value for isVisible changes,
  2. +
  3. the template databinding updates the @visibilityChanged property,
  4. +
  5. the animation trigger is invoked,
  6. +
  7. the @visibilityChanged value is used to identify the state animations and transitions, and
  8. +
  9. the target element opacity (and other properties) change-animates for xxx msecs
  10. +
+

Animation Philosophy

+

One of the design goals for Angular Animations is to make it easy for developers to configure and use animations. The API and syntax is designed to be:

+
    +
  • intuitive
  • +
  • declarative and
  • +
  • immediately associated with the component using metadata
  • +
+

The best part of Angular Animation design is that the component->template->animation binding solution decouples the animation from the component internals and uses the template as the binding bridge.

+

The developer decides which component properties should bind to which animation triggers, then simply uses the possible component property values to set the animation state values accordingly.

+
+

In most cases, you will never need to write JavaScript animation logic.

+
+

All the mechanics of preparing and managing the animations are hidden from the developer. This ‘separation of concerns’ provides HUGE benefits to allow developers to easily use Angular Animations with custom architectures & custom implementations.

+

Animations with Components Hierarchies

+

Components should never be concerned with the details regarding animation of child components. Parent components can monitor and alter the public state of child components, but should never attempt to modify the internals of those components.

+

In our examples (above), parent components can simply change the state of the child my-fader instances and then magically the contents of the my-fader instance will fadeIn or fadeOut.

+
@Component({
+  selector : 'my-app',
+  template: `
+
+  <my-fader [isVisible]="showFader">
+    Am I visible ?
+  </my-fader>
+
+  <button (click)="showFader = !showFader"> Toggle </button>
+  `
+})
+export class MyAppComponent {  
+  showFader : boolean = true;
+}
+

Summary

+

The Angular Animation engine and compiler does all the hard work preparing, managing, and running the animations. Developers use the @Component metadata to declaratively define the component styles, templates, and [now] animations. And it is the component template that serves as the bridge to link the component instance state to the animation trigger property.

+

Thanks

+

Kudos to Matias Niemelä for the amazing Animation engine in Angular!

+

matiasvikinghair

+

These core animation features [discussed above] are available in the Angular 2.0.0 release. And never fear, Matias and his team are working hard on more amazing, intuitive Animation features. So stay tuned for even MORE cool features and blogs coming soon!

Written by  Author

Thomas Burleson

\ No newline at end of file diff --git a/angular/2016/10/10/resolving-route-data-in-angular-2.html b/angular/2016/10/10/resolving-route-data-in-angular-2.html new file mode 100644 index 000000000..f9baaa27a --- /dev/null +++ b/angular/2016/10/10/resolving-route-data-in-angular-2.html @@ -0,0 +1,201 @@ +Resolving route data in Angular | Articles by thoughtram

Angular

Resolving route data in Angular

Not long ago, we wrote about Navigation Guards and how they let us control the navigation flow of our application’s users. Guards like CanActivate, CanDeactivate and CanLoad are great when it comes to taking the decision if a user is allowed to activate a certain route, leaving a certain route, or even asynchronously loading a route.

+

However, one thing that these guards don’t allow us to do, is to ensure that certain data is loaded before a route is actually activated. For example, in a contacts application where we’re able to click on a contact to view a contact’s details, the contact data should’ve been loaded before the component we’re routing to is instantiated, otherwise we might end up with a UI that already renders its view and a few moments later, the actual data arrives (of course, there are many ways to get around this). Route resolvers allow us to do exactly that and in this article we’re going to explore how they work!

+

Understanding the problem

+

Let’s just stick with the scenario of a contacts application. We have a route for a contacts list, and a route for contacts details. Here’s what the route configuration might look like:

+
import { Routes } from '@angular/router';
+import { ContactsListComponent } from './contacts-list';
+import { ContactsDetailComponent } from './contacts-detail';
+
+export const AppRoutes: Routes = [
+  { path: '', component: ContactsListComponent },
+  { path: 'contact/:id', component: ContactsDetailComponent }
+];
+

And of course, we use that configuration to configure the router for our application:

+
import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+import { AppRoutes } from './app.routes';
+
+@NgModule({
+  imports: [
+    BrowserModule,
+    RouterModule.forRoot(AppRoutes)
+  ],
+  ...
+})
+export class AppModule {}
+

Nothing special going on here. However, if this is all new to you, you might want to read our article on routing.

+

Let’s take a look at the ContactsDetailComponent. This component is responsible of displaying contact data, so it somehow has to get access to a contact object, that matches the id provided in the route URL (hence the :id parameter in the route configuration). In our article on routing in Angular, we’ve learned that we can easily access route parameters using the ActivatedRoute like this:

+
import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { ContactsService } from '../contacts.service';
+import { Contact } from '../interfaces/contact';
+
+@Component({
+  selector: 'contacts-detail',
+  template: '...'
+})
+export class ContactsDetailComponent implements OnInit {
+
+  contact: Contact;
+
+  constructor(
+    private contactsService: ContactsService,
+    private route: ActivatedRoute
+  ) {}
+
+  ngOnInit() {
+    let id = this.route.snapshot.paramMap.get('id');
+    this.contactsService.getContact(id)
+        .subscribe(contact => this.contact = contact);
+  }
+}
+

Okay, cool. So the only thing ContactsDetailComponent does, is to fetch a contact object by the given id and assign that object to its local contact property, which then allows us to interpolate expressions like {%raw%}{{contact.name}}{%endraw%} in the template of the component.

+

Let’s take a look at the component’s template:

+
<h2>{{contact?.name}}</h2>
+
+<dl>
+  <dt>Phone</dt>
+  <dd>{{contact?.phone}}</dd>
+  <dt>Website</dt>
+  <dd>{{contact?.website}}</dd>
+</dl>
+

Notice that we’ve attached Angular’s Safe Navigation Operator to all of our expressions that rely on contact. The reason for that is, that contact will be undefined at the time this component is initialized, since we’re fetching the data asynchronously. The Safe Navigation Operator ensures that Angular won’t throw any errors when we’re trying to read from an object that is null or undefined.

+

In order to demonstrate this issue, let’s assume ContactsService#getContact() takes 3 seconds until it emits a contact object. In fact, we can easily fake that delay right away like this:

+
import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ContactsService {
+
+  getContact(id) {
+    return Observable.of({
+      id: id,
+      name: 'Pascal Precht',
+      website: 'http://thoughtram.io',
+    }).delay(3000);
+  }
+}
+

Depending on our template, adding Safe Navigation Operators everywhere can be quite tiring as well. In addition to that, some constructs don’t support that operator, like NgModel and RouterLink directives. Let’s take a look at how we can solve this using route resolvers.

+

Defining resolvers

+

As mentioned ealier, route resolvers allow us to provide the needed data for a route, before the route is activated. There are different ways to create a resolver and we’ll start with the easiest: a function. A resolver is a function that returns either Observable<any>, Promise<any> or just data. This is great, because our ContactsService#getContact() method returns an Observable<Contact>.

+

Resolvers need to be registered via providers. Our article on Dependency Injection in Angular explains nicely how to make functions available via DI.

+

Here’s a resolver function that resolves with a static contact object:

+
@NgModule({
+  ...
+  providers: [
+    ContactsService,
+    {
+      provide: 'contact',
+      useValue: () => {
+        return {
+          id: 1,
+          name: 'Some Contact',
+          website: 'http://some.website.com'
+        };
+      }
+  ]
+})
+export class AppModule {}
+

Let’s ignore for a second that we don’t always want to return the same contact object when this resolver is used. The point here is that we can register a simple resolver function using Angular’s dependency injection. Now, how do we attach this resolver to a route configuration? That’s pretty straight forward. All we have to do is add a resolve property to a route configuration, which is an object where each key points to a resolver.

+

Here’s how we add our resolver function to our route configuration:

+
export const AppRoutes: Routes = [
+  ...
+  { 
+    path: 'contact/:id',
+    component: ContactsDetailComponent,
+    resolve: {
+      contact: 'contact'
+    }
+  }
+];
+

That’s it? Yes! 'contact' is the provider token we refer to when attaching resolvers to route configurations. Of course, this can also be an OpaqueToken, or a class (as discussed later).

+

Now, the next thing we need to do is to change the way ContactsDetailComponent gets hold of the contact object. Everything that is resolved via route resolvers is exposed on an ActivatedRoute’s data property. In other words, for now we can get rid of the ContactsService dependency like this:

+
@Component()
+export class ContactsDetailComponent implements OnInit {
+
+  contact;
+
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit() {
+    this.contact = this.route.snapshot.data['contact'];
+  }
+}
+

In fact, when defining a resolver as a function, we get access to the ActivatedRouteSnapshot, as well as the RouterStateSnapshot like this:

+
@NgModule({
+  ...
+  providers: [
+    ContactsService,
+    {
+      provide: 'contact',
+      useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
+        ...
+      }
+  ]
+})
+export class AppModule {}
+

This is useful in many scenarios where we need access to things like router parameters, which we actually do. However, we also need a ContactsService instance, which we don’t get injected here. So how do we create resolver that need dependency injection?

+

Resolvers with dependencies

+

As we know, dependency injection works on class constructors, so what we need is a class. We can create resolvers as classes as well! The only thing we need to do, is to implement the Resolve interface, which ensures that our resolver class has a resolve() method. This resolve() method is pretty much the same function we have currently registered via DI.

+

Here’s what our contact resolver could look like as a class implementation:

+
import { Injectable } from '@angular/core';
+import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
+import { ContactsService } from './contacts.service';
+
+@Injectable()
+export class ContactResolve implements Resolve<Contact> {
+
+  constructor(private contactsService: ContactsService) {}
+
+  resolve(route: ActivatedRouteSnapshot) {
+    return this.contactsService.getContact(route.paramMap.get('id'));
+  }
+}
+

As soon as our resolver is a class, our provider configuration becomes simpler as well, because the class can be used as provider token!

+
@NgModule({
+  ...
+  providers: [
+    ContactsService,
+    ContactResolve
+  ]
+})
+export class AppModule {}
+

And of course, we use the same token to configure the resolver on our routes:

+
export const AppRoutes: Routes = [
+  ...
+  { 
+    path: 'contact/:id',
+    component: ContactsDetailComponent,
+    resolve: {
+      contact: ContactResolve
+    }
+  }
+];
+

Angular is smart enough to detect if a resolver is a function or a class and if it’s a class, it’ll call resolve() on it.

+

Hopefully this gave you a better idea of how route resolvers in Angular work!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/10/13/two-way-data-binding-in-angular-2.html b/angular/2016/10/13/two-way-data-binding-in-angular-2.html new file mode 100644 index 000000000..182ea82ea --- /dev/null +++ b/angular/2016/10/13/two-way-data-binding-in-angular-2.html @@ -0,0 +1,128 @@ +Two-way Data Binding in Angular | Articles by thoughtram

Angular

Two-way Data Binding in Angular

If there was one feature in AngularJS that made us go “Wow”, then it was probably its two-way data binding system. Changes in the application state have been automagically reflected into the view and vise-versa. In fact, we could build our own directives with two-way data bound scope properties, by setting a configuration value.

+

Angular >= 2.x doesn’t come with such a (built-in) two-way data binding anymore. However, this doesn’t mean we can’t create directives that support two-way data binding. In this article we’re going to explore how two-way data binding in Angular >= 2.x is implemented and how we can implement it in our own directives.

+

Two-way data binding in a nutshell

+

There’s one directive in Angular >= 2.x that implements two-way data binding: ngModel. On the surface, it looks and behaves as magical as we’re used to (from AngularJS). But how does it really work? It’s not that hard really. In fact, it turns out that two-way data binding really just boils down to event binding and property binding.

+

In order to understand what that means, let’s take a look at this code snippet here:

+
<input [(ngModel)]="username">
+
+<p>Hello {{username}}!</p>
+

Right, this is that one demo that blew our minds back in 2009, implemented in Angular >= 2.x. When typing into the input, the input’s value is written into the username model and then reflected back into the view, resulting in a nice greeting.

+

How does this all work? Well, as mentioned earlier, since version 2.x, two-way data binding in Angular really just boils down to property binding and event binding. There is no such thing as two-way data binding. Without the ngModel directive, we could easily implement two-way data binding just like this:

+
<input [value]="username" (input)="username = $event.target.value">
+
+<p>Hello {{username}}!</p>
+

Let’s take a closer look at what’s going on here:

+
    +
  • [value]=“username” - Binds the expression username to the input element’s value property
  • +
  • (input)=“expression” - Is a declarative way of binding an expression to the input element’s input event (yes there’s such event)
  • +
  • username = $event.target.value - The expression that gets executed when the input event is fired
  • +
  • $event - Is an expression exposed in event bindings by Angular, which has the value of the event’s payload
  • +
+

Considering these observations, it becomes very clear what’s happening. We’re binding the value of the username expression to the input’s value property (data goes into the component).

+

We also bind an expression to the element’s input event. This expression assigns the value of $event.target.value to the username model. But what is $event.target.value? As mentioned, $event is the payload that’s emitted by the event. Now, what is the payload of the input event? It’s an InputEventObject, which comes with a target property, which is a reference to the DOM object that fired the event (our input element). So all we do is, we’re reading from the input’s value property when a user enters a value (data comes out of the component).

+

That’s it. That’s two-way data binding in a nutshell. Wasn’t that hard right?

+

Okay cool, but when does ngModel come into play then? Since a scenario like the one shown above is very common, it just makes sense to have a directive that abstracts things away and safes us some keystrokes.

+

Understanding ngModel

+

If we take a look at the source code, we’ll notice that ngModel actually comes with a property and event binding as well. Here’s what our example looks like using ngModel, but without using the shorthand syntax:

+
<input [ngModel]="username" (ngModelChange)="username = $event">
+
+<p>Hello {{username}}!</p>
+

Same rules apply. The property binding [ngModel] takes care of updating the underlying input DOM element. The event binding (ngModelChange) notifies the outside world when there was a change in the DOM. We also notice that the handler expression uses only $event and no longer $event.target.value. Why is that? As we’ve mentioned earlier, $event is the payload of the emitted event. In other words, ngModelChange takes care of extracting target.value from the inner $event payload, and simply emits that (to be technically correct, it’s actually the DefaultValueAccessor that takes of the extracting that value and also writing to the underlying DOM object).

+

Last but not least, since writing username and ngModel twice is still too much, Angular allows the shorthand syntax using [()], also called “Banana in a box”. So after all, it’s really an implementation detail of ngModel that enables two-way data binding.

+

Creating custom two-way data bindings

+

Using this knowledge, we can now build our own custom two-way data bindings. All we have to do is to follow the same rules that ngModel follows, which are:

+
    +
  • Introduce a property binding (e.g. [foo])
  • +
  • Introduce a event binding with the same name, plus a Change suffix (e.g. (fooChange))
  • +
  • Make sure the event binding takes care of property extraction (if needed)
  • +
+

As you can see, there’s a bit more work involved to make two-way data binding work compared to AngularJS. However, we should also always consider if a custom two-way data binding implementation is really needed, or if we can just take advantage of ngModel. This, for example is the case when building custom form controls.

+

Let’s say we create a custom counter component and ignore of a second that this would rather be a custom form control.

+
@Component({
+  selector: 'custom-counter',
+  template: `
+    <button (click)="decrement()">-</button>
+    <span>{{counter}}</span>
+    <button (click)="increment()">+</button>
+  `
+})
+export class CustomCounterComponent {
+
+  counterValue = 0;
+
+  get counter() {
+    return this.counterValue;
+  }
+
+  set counter(value) {
+    this.counterValue = value;
+  }
+
+  decrement() {
+    this.counter--;
+  }
+
+  increment() {
+    this.counter++'
+  }
+}
+

It has an internal counter property that is used to display the current counter value. In order to make this property two-way data bound, the first thing we have to do is to make it an Input property. Let’s add the @Input() decorator:

+
@Component()
+export class CustomCounterComponent {
+
+  counterValue = 0;
+
+  @Input()
+  get counter() {
+    return this.counterValue;
+  }
+  ...
+}
+

This already enables us to bind expression to that property as a consumer of that component like this:

+
<custom-counter [counter]="someValue"></custom-counter>
+

The next thing we need to do, is to introduce an @Output() event with the same name, plus the Change suffix. We want to emit that event, whenever the value of the counter property changes. Let’s add an @Output() property and emit the latest value in the setter interceptor:

+
@Component()
+export class CustomCounterComponent {
+
+  ...
+  @Output() counterChange = new EventEmitter();
+
+  set counter(val) {
+    this.counterValue = val;
+    this.counterChange.emit(this.counterValue);
+  }
+  ...
+}
+

That’s it! We can now bind an expression to that property using the two-way data binding syntax:

+
<custom-counter [(counter)]="someValue"></custom-counter>
+
+<p>counterValue = {{someValue}}</p>
+

Again, please keep in mind that a component like a custom counter, would better serve as a custom form control and takes advantage of ngModel to implement two-way data binding as explained in this article.

+

Conclusion

+

Angular doesn’t come with built-in two-way data binding anymore, but with APIs that allow to implement this type of binding using property and event bindings. ngModel comes as a built-in directive as part of the FormsModule to implement two-way data binding and should be preferred when building components that serve as custom form controls.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/11/28/testing-services-with-http-in-angular-2.html b/angular/2016/11/28/testing-services-with-http-in-angular-2.html new file mode 100644 index 000000000..96fe69937 --- /dev/null +++ b/angular/2016/11/28/testing-services-with-http-in-angular-2.html @@ -0,0 +1,279 @@ +Testing Services with Http in Angular | Articles by thoughtram

Angular

Testing Services with Http in Angular

Testing is important. That’s why Angular comes with a testing story out-of-the-box. Due to its dependency injection system, it’s fairly easy to mock dependencies, swap out Angular modules, or even create so called “shallow” tests, which enable us to test Angular components, without actually depending on their views (DOM). In this article we’re going to take a look at how to unit test a service that performs http calls, since there’s a little bit more knowledge required to make this work.

+

The Service we want to test

+

Let’s start off by taking a look at the service want to test. Sure, sometimes we actually want to do test-driven development, where we first create the test and then implement the actual service. However, from a learning point of view, it’s probably easier to grasp testing concepts when we first explore the APIs we want to test.

+

At thoughtram, we’re currently recording screencasts and video tutorials, to provide additional content to our blog readers. In order to make all these videos more explorable, we’re also building a little application that lets users browse and watch them. The video data is hosted on Vimeo, so we’ve created a service that fetches the data from their API.

+

Here’s what our VideoService roughly looks like:

+
import { Injectable, Inject } from '@angular/core';
+import { VIMEO_API_URL } from '../config';
+
+import 'rxjs/add/operator/map';
+
+@Injectable()
+export class VideoService {
+
+  constructor(private http: Http, @Inject(VIMEO_API_URL) private apiUrl) {}
+
+  getVideos() {
+    return this.http.get(`${this.apiUrl}/videos`)
+                    .map(res => res.json().data);
+  }
+}
+

getVideos() returns an Observable<Array<Video>>. This is just an excerpt of the actual service we use in production. In reality, we cache the responses so we don’t peform an http request every single time we call this method.

+
+

Special Tip: If the @Inject() decorator is new to you, make sure to checkout this article

+
+

To use this service in our application, we first need to create a provider for it on our application module, later we will use it in our tests:

+
@NgModule({
+  imports: [HttpModule]
+  providers: [VideoService]
+  ...
+})
+export class AppModule {}
+

Since the API returns an Observable, we need to subscribe to it to actually perform the http request. That’s why the call side of the method looks something like this:

+
@Component()
+export class VideoDashboard {
+  
+  private videos = [];
+
+  constructor(private videoService: VideoService) {}
+
+  ngOnInit() {
+    this.videoService.getVideos()
+        .subscribe(videos => this.videos = videos);
+  }
+}
+

Notice how we’re passing a callback function to access the video data that is emitted by the Observable. We need to keep that in mind when testing these methods, because we can’t call them synchronously. To get an introduction to Observables in conjunction with Angular, make sure to read this article.

+

Alright, now that we know what the service we want to test looks like, let’s take a look at writing the tests.

+

Configuring a testing module

+

Before we can start writing test specs for our service APIs, we need to configure a testing module. This is needed because in our tests, we want to make sure that we aren’t performing actual http requests and use a MockBackend instead. Our goal is to isolate the test scenario as much as we can without touching any other real dependencies. Since NgModules configure injectors, a testing module allows us to do exactly that.

+

When testing services or components that don’t have any dependencies, we can just go ahead an instantiate them manually, using their constructors like this:

+
it('should do something', () => {
+  let service = new VimeoService();
+
+  expect(service.foo).toEqual('bar');
+});
+
+

Special Tip: When testing components and services that don’t have any dependencies, we don’t necessarily need to create a testing module.

+
+

To configure a testing module, we use Angular’s TestBed. TestBed is Angular’s primary API to configure and initialize environments for unit testing and provides methods for creating components and services in unit tests. We can create a module that overrides the actual dependencies with testing dependencies, using TestBed.configureTestingModule().

+
import { TestBed } from '@angular/core/testing';
+
+describe('VideoService', () => {
+
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      ...
+    });
+
+  });
+});
+

This will create an NgModule for every test spec, as we’re running this code as part of a beforeEach() block. This is a Jasmine API. If you aren’t familiar with Jasmine, we highly recommend reading their documentation.

+

Okay, but what does a configuration for such a testing module look like? Well, it’s an NgModule, so it has pretty much the same API. Let’s start with adding an import for HttpModule and a provider for VideoService like this:

+
import { HttpModule } from '@angular/http';
+import { VideoService } from './video.service';
+import { VIMEO_API_URL } from '../config';
+...
+TestBed.configureTestingModule({
+  imports: [HttpModule],
+  providers: [
+    { provide: VIMEO_API_URL, useValue: 'http://example.com' },
+    VideoService
+  ]
+});
+...
+

This configures an injector for our tests that knows how to create our VideoService, as well as the Http service. However, what we actually want is an Http service that doesn’t really perform http requests. How do we do that? It turns out that the Http service uses a ConnectionBackend to perform requests. If we find a way to swap that one out with a different backend, we get what we want.

+

To give a better picture, here’s what the constructor of Angular’s Http service looks like:

+
@Injectable()
+export class Http {
+
+  constructor(
+    protected _backend: ConnectionBackend,
+    protected _defaultOptions: RequestOptions
+  ) {}
+
+  ...
+}
+

By adding an HttpModule to our testing module, providers for Http, ConnectionBackend and RequestOptions are already configured. However, using an NgModule’s providers property, we can override providers that have been introduced by other imported NgModules! This is where Angular’s dependency injection really shines!

+

Overriding the Http Backend

+

In practice, this means we need to create a new provider for Http, which instantiates the class with a different ConnectionBackend. Angular’s http module comes with a testing class MockBackend. That one not only ensures that no real http requests are performed, it also provides APIs to subscribe to opened connections and send mock responses.

+
import { HttpModule, Http, BaseRequestOptions, XHRBackend } from '@angular/http';
+import { MockBackend } from '@angular/http/testing';
+...
+TestBed.configureTestingModule({
+  ...
+  providers: [
+    ...
+    { provide: XHRBackend, useClass: MockBackend }
+  ]
+});
+...
+

Wow, that was easy! We simply tell our injector to inject an instance of MockBackend whenever someone asks for an XHRBackend, which is what Angular’s Http module does behind the scenes.

+

Awesome! We’ve created a testing module that uses an Http service with a MockBackend. Now let’s take a look at how to actually test our service.

+

Testing the service

+

When writing unit tests with Jasmine, every test spec is written as an it() block, where an assertion is made and then checked if that assertion is true or not. We won’t go into too much detail here, since there’s a lot of documentation for Jasmine out there. We want to test if our VideoService returns an Observable<Array<Video>>, so let’s start with the following it() statement:

+
describe('VideoService', () => {
+  ...
+  describe('getVideos()', () => {
+
+    it('should return an Observable<Array<Video>>', () => {
+      // test goes here
+    });
+  });
+});
+

We’ve also added another nested describe() block so we can group all tests that are related to that particular method we test. Okay, next we need to get an instance of our VideoService. Since we’ve created a testing module that comes with all providers for our services, we can use dependency injection to inject instances accordingly.

+

Injecting Services

+

Angular’s testing module comes with a helper function inject(), which injects service dependencies. This turns out to be super handy as we don’t have to take care of getting access to the injector ourselves. inject() takes a list of provider tokens and a function with the test code, and it returns a function in which the test code is executed. That’s why we can pass it straight to our spec and remove the anonymous function we’ve introduced in the first place:

+
import { TestBed, inject } from '@angular/core/testing';
+...
+it('should return an Observable<Array<Video>>',
+  inject([/* provider tokens */], (/* dependencies */) => {
+  // test goes here
+}));
+

Cool, now we have all the tools we need to inject our services and write a test. Let’s go ahead an do exactly that. Once we have our service injected, we can call getVideos() and subscribe to the Observable it returns, to then test if the emitted value is the one we expect.

+
it('should return an Observable<Array<Video>>',
+  inject([VideoService], (videoService) => {
+
+    videoService.getVideos().subscribe((videos) => {
+      expect(videos.length).toBe(4);
+      expect(videos[0].name).toEqual('Video 0');
+      expect(videos[1].name).toEqual('Video 1');
+      expect(videos[2].name).toEqual('Video 2');
+      expect(videos[3].name).toEqual('Video 3');
+    });
+}));
+

This test is not finished yet. Right now we’re having a test that expects some certain data that is going to be emitted by getVideos(), however, remember we’ve swapped out the Http backend so there’s no actual http request performed? Right, if there’s no request performed, this Observable won’t emit anything. We need a way to fake a response that is emitted when we subscribe to our Observable.

+

Mocking http responses

+

As mentioned earlier, MockBackend provides APIs to not only subscribe to http connections, it also enables us to send mock responses. What we want is, when the underlying Http service creates a connection (performs a request), send a fake http response with the data we’re asserting in our getVideos() subscription.

+

We can subscribe to all opened http connections via MockBackend.connections, and get access to a MockConnection like this:

+
it('should return an Observable<Array<Video>>',
+  inject([VideoService, XHRBackend], (videoService, mockBackend) => {
+    mockBackend.connections.subscribe((connection) => {
+      // This is called every time someone subscribes to
+      // an http call.
+      //
+      // Here we want to fake the http response.
+    });
+
+    ...
+}));
+

The next thing we need to do, is to make the connection send a response. We use MockConnection.mockRespond() for that, which takes an instance of Angular’s Response class. In order to define what the response looks like, we need to create ResponseOptions and define the response body we want to send (which is a string):

+
...
+const mockResponse = {
+  data: [
+    { id: 0, name: 'Video 0' },
+    { id: 1, name: 'Video 1' },
+    { id: 2, name: 'Video 2' },
+    { id: 3, name: 'Video 3' },
+  ]
+};
+
+mockBackend.connections.subscribe((connection) => {
+  connection.mockRespond(new Response(new ResponseOptions({
+    body: JSON.stringify(mockResponse)
+  })));
+});
+...
+

Cool! With this code we now get a fake response inside that particular test spec. Even though it looks like we’re done, there’s one more thing we need to do.

+

Making the test asynchronous

+

Because the code we want to test is asynchronous, we need to inform Jasmine when the asynchronous operation is done, so it can run our assertions.

+

Johannes Hoppe pointed out that MockConnection.mockRespond() actually emits synchronously, not asynchronously, which means that the rest of this section is not needed for our particular test (Thank you Johannes!). However, it’s still a useful gem if we are writing tests that need asynchronous operations, read on!

+

When testing asynchronous code, assertions might be executed later in another tick. That’s why we can explicitly tell Jasmine that we’re writing an asynchronous test. We just need to also tell Jasmine, when the actual code is “done”.

+

Jasmine usually provides access to a function done() that we can ask for inside our test spec and then call it when we think our code is done. This looks something like this:

+
it('should do something async', (done) => {
+  setTimeout(() => {
+    expect(true).toBe(true);
+    done();
+  }, 2000);
+});
+

Angular makes this a little bit more convenient. It comes with another helper function async() which takes a test spec and then runs done() behind the scenes for us. This is pretty cool, because we can write our test code as if it was synchronous!

+

How does that work? Well… Angular takes advantage of a feature called Zones. It creates a “testZone”, which automatically figures out when it needs to call done(). If you havent heard about Zones before, we wrote about them here and here.

+
+

Special Tip: Angular’s async() function executes test specs in a test zone!

+
+

The complete code

+

Putting it all together, here’s what the test spec for the getVideos() method looks like:

+
import { TestBed, async, inject } from '@angular/core/testing';
+import {
+  HttpModule,
+  Http,
+  Response,
+  ResponseOptions,
+  XHRBackend
+} from '@angular/http';
+import { MockBackend } from '@angular/http/testing';
+import { VideoService } from './video.service';
+import { VIMEO_API_URL } from '../config';
+
+describe('VideoService', () => {
+
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      imports: [HttpModule],
+      providers: [
+        { provide: VIMEO_API_URL, useValue: 'http://example.com' },
+        VideoService,
+        { provide: XHRBackend, useClass: MockBackend },
+      ]
+    });
+  });
+
+  describe('getVideos()', () => {
+
+    it('should return an Observable<Array<Video>>',
+        inject([VideoService, XHRBackend], (videoService, mockBackend) => {
+
+        const mockResponse = {
+          data: [
+            { id: 0, name: 'Video 0' },
+            { id: 1, name: 'Video 1' },
+            { id: 2, name: 'Video 2' },
+            { id: 3, name: 'Video 3' },
+          ]
+        };
+
+        mockBackend.connections.subscribe((connection) => {
+          connection.mockRespond(new Response(new ResponseOptions({
+            body: JSON.stringify(mockResponse)
+          })));
+        });
+
+        videoService.getVideos().subscribe((videos) => {
+          expect(videos.length).toBe(4);
+          expect(videos[0].name).toEqual('Video 0');
+          expect(videos[1].name).toEqual('Video 1');
+          expect(videos[2].name).toEqual('Video 2');
+          expect(videos[3].name).toEqual('Video 3');
+        });
+
+    }));
+  });
+});
+

This pattern works for pretty much every test that involves http operations. Hopefully this gave you a better understanding of what TestBed, MockBackend and async are all about.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2016/12/27/angular-2-advance-testing-with-custom-matchers.html b/angular/2016/12/27/angular-2-advance-testing-with-custom-matchers.html new file mode 100644 index 000000000..5a8ce561a --- /dev/null +++ b/angular/2016/12/27/angular-2-advance-testing-with-custom-matchers.html @@ -0,0 +1,651 @@ +Testing Angular Directives with Custom Matchers | Articles by thoughtram

Angular

Testing Angular Directives with Custom Matchers

Preface

+

A few weeks ago, Pascal Precht wrote a blog article on +Testing Services with HTTP with Angular. +In this article, we want to discuss more advanced topics on DRY Angular testing techniques +using Custom Matchers and Special Helpers.

+

These are techniques to make your unit-tests incredibly easy to read and to maintain. And to achieve +our learning goals, there are three (3) important testing topics that we want to cover:

+ +
+

These techniques are practically undocumented… yet they dramatically improve the quality of +our tests.

+
+

To whet your appetite, here are some sample DRY tests that we will be showing you how to write:

+

+ + Unit Test with Matchers and Helpers +

+

+ + Testing Nested DOM Styles +

+

Background

+

There a several excellent resources already available that developers can read to +learn about Angular testing:

+ +

The biggest take-aways from these articles is the singular concept that instead of manually +instantiating and testing classes, Angular developers should consider using +the TestBed.configureTestingModule() to prepare an entire test Angular DI environment for each +test module (*.spec.ts).

+
+

We would not use TestBed.configureTestingModule() when we are testing a service that +doesn’t have any dependencies. It’d be easier and less verbose to just instantiate using new. The +TestBed is for useful for dependencies and injections.

+
+

Traditional Testing

+

Consider the traditional approach of manually instantiating and testing a Service or component:

+
import { ServiceA } from './services/ServiceA';
+
+describe('ServiceA', () => {
+  it('should emit greeting event', () => {
+    let inst = new ServiceA();
+    inst.greeting.subscribe(g => {
+       expect(g).toEqual({greeting:'hello'});
+    });
+    inst.sayHello();
+  });
+});
+

It’s fine to do it this way, because ServiceA obviously does not need anything +else to be instantiated; it is a self-contained service without external dependencies.

+
+

So assuming that this service won’t get any dependencies in the future, this test is the one we want to write.

+
+

Introducing Angular TestBed

+

The Angular TestBed allows developers to configure ngModules that provide instances/values and use +Dependency Injection. This is the same approach developers use in their regular Angular applications.

+

With the TestBed and its support for for component-lifecycle features, Angular components can be +easily instantiated and tested. Here is an example - shown below - that we will continue to +use in this article:

+
import { MyComponent } from './viewer/MyComponent';
+
+/**
+ * Configure testbed to register components and prepare services
+ */
+beforeEach(() => {
+  TestBed.configureTestingModule({
+    imports: [ FlexLayoutModule.forRoot() ],
+    declarations: [MyComponent],
+    
+    // 'MatchMedia' construction requires BreakPointRegistry
+    // 'MyComponent' construction requires MatchMedia parameter
+    
+    providers: [
+      BreakPointRegistry,
+      BreakPointsProvider,
+      {
+        provide: MatchMedia,
+        useClass: MockMatchMedia
+      }
+    ]
+  })
+});
+
+/**
+ * Helper function to easily build a component Fixture using the specified template
+ */
+function createTestComponent(template: string): ComponentFixture<Type<any>>
+{
+  return TestBed
+    .overrideComponent(MyComponent, {set: {template: template}} )
+    .createComponent(MyComponent);
+}
+

Before each test, we want configure a new, fresh testing module with only the providers, +components, and directives we need for the current test module.

+
+

And notice that we just created a reusable Helper function: createTestComponent(). +This cool utility function will construct an instance of MyComponent [using the configured TestBed] +using any custom HTML template you specify.

+
+

At first, this complexity may seem like overkill. But let’s consider two critical requirements +shown in the sample above:

+
    +
  • MatchMedia instantiation requires an injected BreakPointRegistry instance
  • +
  • MyComponent instantiation requires an injected MatchMedia instance
  • +
+

Even with these requirements, testing developers should NOT have to worry about all those internals just +to test MyComponent. Using ngModule, DI, and Angular… we now don’t have to worry about those +details.

+

This is just like those real-world scenarios where our components, directives, and services will +have complex dependencies upon providers and non-trivial construction processes.

+

And this is where TestBed demonstrates its real value!

+
+

We are not using external templates nor any other resources or services that are asynchronous. +So we do not discuss the async() nor the the TestBed::compileComponents() functions.

+
+

1) Testing Directives

+

With relative ease, developers can find literature on testing Angular Services and Components. +Yet the How-to’s for testing Directives is oddly not well documented.

+

Unlike Components with their associated templates, Directives do not have templates. This means +that we cannot simply import a Directive and manually instantiate it.

+

The solution is rather easy! We only need to:

+
    +
  • configure a TestBed that imports and declares the directive(s),
  • +
  • prepare a shell test Component, and
  • +
  • use a custom template which uses the Directive selector(s) as attribute(s).
  • +
+

Since Directives usually affect the host element, our tests will check if those changes to the host +element are present and valid.

+

So let’s use the Angular Flex-Layout library as the basis +for the following discussions on Directives, Matchers, and TestBed Helpers.

+
+

Real-world solutions often provide great examples for reusable techniques.

+
+

We will be using both the fxLayout directive and excerpts from the unit test for that directive +to explore testing ideas, techniques, and solutions that we can also use in our own tests.

+

First, let’s import the FlexLayout library into our own tests and prepare to test the fxLayout +directive.

+
+

You can see the actual testing code in layout.spec.ts. +
But don’t jump there yet! Wait until you have finished reading this article.

+
+

Configuring the TestBed

+

Very similar to the TestBed sample shown above, we will configure a testing module but we will +not import an external test component. My test component TestLayoutComponent is itself defined +within our test (*.spec.ts) module.

+
+

Using an internal test component enables each test module
e.g.
<directive>.spec.ts
to define +and use its own custom test component with custom properties.

+
+

Here is the initial configuration:

+
import {Component, OnInit} from '@angular/core';
+import {ComponentFixture, TestBed } from '@angular/core/testing';
+import {
+  FlexLayoutModule,
+  MockMatchMedia,
+  MatchMedia,
+  BreakPointsProvider,
+  BreakPointRegistry
+} from '@angular/flex-layout';
+
+describe('layout directive', () => {
+
+  beforeEach(() => {
+
+    // Configure testbed to prepare services
+    TestBed.configureTestingModule({
+      imports: [FlexLayoutModule.forRoot()],
+      declarations: [TestLayoutComponent],
+      providers: [
+        BreakPointRegistry, BreakPointsProvider,
+        {provide: MatchMedia, useClass: MockMatchMedia}
+      ]
+    })
+  });
+
+  it('should add correct styles for default `fxLayout` usage', () => {
+			// TEST LOGIC HERE
+  });
+
+});
+
+/*
+ * Shell component with property 'direction' that will be used
+ * with our tests
+ */
+@Component({
+  selector: 'test-layout',
+  template: `<span>PlaceHolder HTML to be Replaced</span>`
+})
+export class TestLayoutComponent implements OnInit {
+  direction = "column";
+  constructor() {   }
+  ngOnInit() {   }
+}
+
+/*
+ * Custom Helper function to quickly create a `fixture` instance based on
+ * the 'TestLayoutComponent' class
+ */
+function createTestComponent(template: string): ComponentFixture<TestLayoutComponent>
+{
+  return TestBed
+    .overrideComponent(TestLayoutComponent, {set: {template: template}} )
+    .createComponent(TestLayoutComponent);
+}
+

We now have everything we need to write a Directive test quickly.

+
it('should compile with custom directives', () => {
+  let fixture = createTestComponent('<div fxLayout></div>');
+  expect( fixture ).toBeDefined();
+});
+

Wow! That is pretty easy.

+
+

The component has been constructed and prepared with the same +processes and DI that your real world application will use.

+
+

Let’s first write our test using the traditional long-form… one without custom matchers and the +more advanced helper methods.

+

Since the fxLayout directive will add custom flexbox CSS to the host element, our test logic +here will confirm that the initial CSS is correctly assigned.

+

Non-DRY Testing

+

The traditional approach would probably implement something like this:

+
it('should add correct styles for `fxLayout` with template bindings', () => {
+  let template = '<div [fxLayout]="direction"></div>';
+  let fixture = createTestComponent(template);
+  let el = fixture.debugElement.children[0].nativeElement;
+  
+    fixture.detectChanges();
+    
+  expect( el.style['display'] ).toBe('flex');
+  expect( el.style['box-sizing'] ).toBe('border-box');
+  expect( el.style['flex-direction'] ).toBe('column');
+ 
+    fixture.componentInstance.direction = 'row';
+    fixture.detectChanges();
+    
+  expect( el.style['flex-direction'] ).toBe('row'); 
+});
+

In the code above, we

+
    +
  • defined a custom template with bindings to the component property direction,
  • +
  • use deeply nested references to get access to the native element,
  • +
  • test each style individually.
  • +
+

All this in one (1) single test. And truly it is not easily read.

+

Now imagine that our test module has more than 20 individual it(...) tests!

+
+

That would be a lot of duplicate code.
And there is certainly nothing DRY (“do not repeat yourself”) about +such code!

+
+

2) Testing with Helpers

+

Above we explored the standard approach to implementing unit tests… an approach which resulted +in verbose, non-reusable code.

+

Let’s contrast that with the DRY version that we want:

+

+ + Testing Directives - DRY Code +

+

Now this test is much more readable, maintainable, and DRY. We hide all the +complexities of:

+
    +
  • forcing change detection,
  • +
  • accessing the native element, and
  • +
  • confirming 1…n DOM CSS styles
  • +
+

Those complexities are now encapsulated in a Helper function and a Custom Matcher (respectively).

+

The custom Helper function expectNativeEl( ) is similar to the standard expect( ) function. +In fact, it is wrapper function that internalizes the expect( ) call.

+
/**
+ * Note: this helper only accesses the 1st-level child within a component
+ *       A different helper method is need to access deep-level DOM nodes
+ */
+export function expectNativeEl(fixture: ComponentFixture<any>): any {
+  fixture.detectChanges();
+
+  // Return response of `expect(...)`
+  return expect(fixture.debugElement.children[0].nativeElement);
+}
+
+

It is important to note that these helper methods always return the value of an expect(...) call!

+
+

And the resulting code change uses a similar notation to our standard training. So

+
expect(...).toBe
+

becomes

+
expectNativeEl(...).toBe
+

Testing Nested DOM

+

For more complex DOM access, we can use the DebugElement’s query feature to select nested DOM nodes.

+

Angular’s DebugElement has several query features:

+
    +
  • query(predicate: Predicate<DebugElement>): DebugElement;
  • +
  • queryAll(predicate: Predicate<DebugElement>): DebugElement[];
  • +
  • queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[];
  • +
+
+

Please note that all debugging apis are currently experimental.

+
+

Consider the following helper function expectDomForQuery( ):

+

+ + pic6 +

+

In this example, we actually want to test the nested DOM node that hosts the attribute fxFlex. +Using another helper method expectDomForQuery( ) makes that easy.

+
/**
+ * Create component instance using custom template, the query for the native DOM node
+ * based on the query selector
+ */
+function expectDomForQuery(template:string, selector:string) : any {
+  let fixture = createTestComponent(template);
+      fixture.detectChanges();
+
+  // Return response of `expect(...)`
+  return expect( queryFor(fixture,selector).nativeElement );
+};
+
+/**
+ * Don't forget to import the `By` utilities
+ */
+import {By} from '@angular/platform-browser';
+
+/**
+ * Reusable Query helper function
+ */
+function  queryFor(fixture:ComponentFixture<any>, selector:string):any {
+  return fixture.debugElement.query(By.css(selector))
+}
+

And the resulting code change is again a similar notation to our standard training:

+
expect(...).toBe
+

becomes

+
expectDomForQuery(...).toBe
+

More Special Helpers

+

Earlier, we showed a code snapshot that had a special helper activateMediaQuery( ):

+

+ + Unit Test with Matchers and Helpers +

+

The Flex-Layout library has a responsive engine that supports change detection when a mediaQuery +activates (aka when the viewport size changes). The API uses selectors with dot-notation to +indicate which values should be used for which mediaQuery:

+
<div fxLayout="row" fxLayout.md="column"></div>
+

Testing these features presents several additional requirements:

+
    +
  • mock the window API window.matchMedia(...)
  • +
  • simulate a mediaQuery activation
  • +
  • trigger fixture.detectChange() after a simulated activation
  • +
  • hide all these details from individual tests (DRY)
  • +
+

Thankfully the Flex-Layout library actually publishes a MockMatchMedia class. And we use this class +in our TestBed configuration below:

+
let fixture: ComponentFixture<any>;
+
+/**
+ * Special Helper
+ */
+const activateMediaQuery = (alias) => {
+  let injector = fixture.debugElement.injector;
+  let matchMedia : MockMatchMedia = injector.get(MatchMedia);
+  
+  // simulate mediaQuery change and trigger fxLayout changes
+  matchMedia.activate(alias);
+};
+
+beforeEach(() => {
+  // Configure testbed to prepare services
+  TestBed.configureTestingModule({
+    imports: [FlexLayoutModule.forRoot()],
+    declarations: [TestLayoutComponent],
+    providers: [
+      BreakPointRegistry, BreakPointsProvider,
+      {provide: MatchMedia, useClass: MockMatchMedia}   //Special mock config
+    ]
+  })
+});
+

Let’s explore three (3) very interesting things are happening in this code:

+
    +
  • configure Dependency Injection providers to override a class with a mock,
  • +
  • dynamic injection using injector.get(MatchMedia), and
  • +
  • use special helper activateMediaQuery( ) function that hides all these details
  • +
+

(1) Overriding DI Providers

+
{ provide: MatchMedia, useClass: MockMatchMedia }
+

tells the TestBed DI system to provide an instance of the MockMatchMedia whenever any code +asks for an instance of the MatchMedia token to be injected via DI.

+
+

You can read more about the Angular DI systems here:
Dependency Injection in Angular

+
+

(2) Dynamic Injection

+

Our special helper activateMediaQuery() needs a dynamic injected instance of the MatchMedia token. +Using the fixture instance, we can access the injector service for our components. With the injector, +we can dynamically get a MockMatchMedia instance using the provider token MatchMedia.

+
+

In this case, MatchMedia is both a class and a provider token used for D-injection.

+
+

Notice that all this complexity [and construction details] on preparing a MockMatchMedia instance is +encapsulated in our TestBed… and the use of the injector is hidden within our special helper.

+

Now our individual tests simply use the easy special helper function activateMediaQuery( ):

+
activateMediayQuery('md');
+

That is very, very cool!

+

3) Custom Matchers

+

We have only one more tool [in our testing toolkit] to discuss: Custom Jasmine Matchers.

+

For those developers not familiar with the concepts of Jasmine matchers, we recommend the online +Jasmine documentation:

+ +![pic9](https://cloud.githubusercontent.com/assets/210413/21524374/334a2f68-ccdb-11e6-816c-5059fc91d806.png) + +

Remember that matchers are used after the expect() call and should encapsulate complex logic +and reduce code-clutter in our test code.

+
+

This allows our test(s) to remain terse, concise, readable, maintainable, and DRY.

+
+

And we should always give our custom matcher functions clear, readable names. E.g. toHaveCssStyles().

+

Building a TypeScript Matcher

+

Similar to expect(...).toBeTruthy(), we want a custom matcher toHaveCSSStyle( ):

+
expectNativeEl(fixture).toHaveCssStyle({
+  'display': 'flex',
+  'flex-direction': 'row',
+  'box-sizing': 'border-box'
+});
+

Creating a matcher in JavaScript is documented on the Jasmine site. Our challenge is the harder +goal of creating a custom matcher implemented in TypeScript using well-defined types.

+
    +
  • we need to enhance the expect() API, and
  • +
  • we need to implement custom matchers.
  • +
+

The global Jasmine expect() method normally returns <any> value. To use custom matchers +(with types), we want the expect( ) function to support returning either a standard matcher or +a custom matcher.

+

Here is a teaser that shows how that is done:

+
export const expect: (actual: any) => NgMatchers = <any> _global.expect;
+
+export interface NgMatchers extends jasmine.Matchers { /* ... */ };
+

We assigned the global expect function reference to a new export that states that expect() calls +now return references to a NgMatchers interface.

+

Here are the full implementation details of our custom-matchers.ts module:

+
declare var global: any;
+const _global = <any>(typeof window === 'undefined' ? global : window);
+
+/**
+ * Extend the API to support chaining with custom matchers
+ */
+export const expect: (actual: any) => NgMatchers = <any> _global.expect;
+
+/**
+ * Jasmine matchers that support checking custom CSS conditions.
+ * !! important to add your custom matcher to the interface
+ */
+export interface NgMatchers extends jasmine.Matchers {
+  toHaveCssStyle(expected: {[k: string]: string}|string): boolean;
+  not: NgMatchers;
+}
+
+/**
+ * Implementation of 1...n custom matchers
+ */
+export const customMatchers: jasmine.CustomMatcherFactories = {
+  // Here is our custom matcher; cloned from @angular/core
+  toHaveCssStyle: function () {
+    return {
+      compare: function (actual: any, styles: {[k: string]: string}|string) {
+        let allPassed: boolean;
+        if (typeof styles === 'string') {
+          allPassed = getDOM().hasStyle(actual, styles);
+        } else {
+          allPassed = Object.keys(styles).length !== 0;
+          Object.keys(styles).forEach(prop => {
+            allPassed = allPassed && getDOM().hasStyle(actual, prop, styles[prop]);
+          });
+        }
+
+        return {
+          pass: allPassed,
+          get message() {
+            const expectedValueStr = typeof styles === 'string' ? styles : JSON.stringify(styles);
+            return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the
+                      CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${expectedValueStr}"`;
+          }
+        };
+      }
+    };
+  }
+
+};
+

With the above definitions, we can now use expect(...).toHaveCssStyles(...) without any +TypeScript complaints.

+

Wait!

+

We need one to discuss one more addition to our custom-matcher code. Did you notice the call to +getDOM().hasStyle() ? But where does getDOM come from?

+

After some inspection of the @angular/core code, we were able to +get a reference to the special Angular DOM Adapter:

+
import {__platform_browser_private__} from '@angular/platform-browser';
+const getDOM = __platform_browser_private__.getDOM;
+
+

Please note that the getDOM() is a private Angular API and may change in the future.

+
+

Using a Custom Matcher

+

Now we are golden with features. Let’s import and use our custom Jasmine matcher.

+
import {customMatchers} from '../utils/testing/custom-matchers';
+
+describe('layout directive', () => {
+
+  beforeEach(() => {
+    jasmine.addMatchers(customMatchers);
+
+    // TestBed stuff here...
+  });
+
+});
+

Notice that we must register the custom matchers in a beforeEach() call to configure the +matchers for each subsequent test. And now everything is ready for the individual tests:

+
expect(...).toHaveCssStyle({
+  'display': 'flex',
+  'flex-direction': 'row',
+  'box-sizing': 'border-box'
+});
+

Protractor + e2e

+

It should be noted that the above sample tests confirm whether CSS styles have been applied +correctly to the DOM element. Unit tests perform tests logic and state… but those same +tests cannot easily test how those values affect renderings in the UI.

+

Jasmine unit tests do not test whether the CSS styles or states render the elements in the +browser as expected. Nor do they test renderings across different browsers. Those types of visual +tests are best performed in e2e testing with Protractor and visual differencing tools.

+

Summary

+

Perhaps you will say: “Wow, this is cool… but totally overkill!” If you are tempted to say that, +then look at all the DRY tests here: +layout.spec.ts. +You will quickly see the value of using helpers and custom matchers.

+

You now have the techniques and tools to create your own custom matchers and deliver quality, terse +unit tests.

+

Enjoy!

+

Resources

+ +
+

The Flex-Layout Helper functions are actually partial applications (function currying). Here is +How to use them.

+
Written by  Author

Thomas Burleson

\ No newline at end of file diff --git a/angular/2017/01/09/a-revamped-angular-master-class.html b/angular/2017/01/09/a-revamped-angular-master-class.html new file mode 100644 index 000000000..2c5128181 --- /dev/null +++ b/angular/2017/01/09/a-revamped-angular-master-class.html @@ -0,0 +1,60 @@ +A revamped Angular Master Class | Articles by thoughtram

Angular

A revamped Angular Master Class

At thoughtram, we’re constantly trying to provide the best training experience we can possibly offer. This includes things like updating the courseware to the latest Angular version, as well as adding new learning modules to the overall Angular Master Class program, so you can pick whatever topic you’re most interested in in our classes.

+

Today we’re super happy to announce that we’ve recreated our courseware from the ground up to integrate the brand new Angular Material library, as well as the even newer Angular Flex Layout Module!

+

+ + website topic box +

+

Why is this great?

+

While our courseware content has already been battle-tested since more than two (2) years and proven to be a solid and well working setup, with the latest changes you’ll benefit from the following additional features:

+
    +
  • Understand how to integrate Angular Material and Angular Flex Layout into your project
  • +
  • Learn how to use Material components (like Dialogs) to build rich and accessible user interfaces
  • +
  • Take advantage of Angular Flex Layout and learn how to build dynamic and responsive application layouts
  • +
+

These features will from now on come built-in with the Angular Master Class experience. Not only is this an improved experience for you as a student and your learning result when you’ve taken one of our classes, it also lays the foundation for future learning modules we’re adding to the Angular Master Class, that will go more in-depth into Angular’s Material components and Flex Layout.

+

Cool, when can I attend a class?

+

Great question! As of right now we’re planning in-house trainings for Q1 2017. We’re also giving a Taming Forms in Angular training at ng-conf later this year in April, and we might plan another public training in Q1, so make sure to follow us on twitter for latest updates and news about the next trainings!

+

What else…? Oh right, free ng-conf ticket!

+

Ng-conf is already sold out and it’s almost impossible to get a ticket for the biggest Angular conference on this planet… Unless you’re reading our blog. That’s right. Somewhere on this website we’ve hidden a free ng-conf ticket. Find it, and it’s yours. Too bad! Some lucky person already found it!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/02/02/making-your-angular-app-fast.html b/angular/2017/02/02/making-your-angular-app-fast.html new file mode 100644 index 000000000..50a21291f --- /dev/null +++ b/angular/2017/02/02/making-your-angular-app-fast.html @@ -0,0 +1,359 @@ +Making your Angular apps fast | Articles by thoughtram

Angular

Making your Angular apps fast

Angular claims to be very fast by default. What does “fast” really mean? Of course, this always depends on the context. What does our application do? How many different things is it doing at a certain point? How is our application’s component tree structured and how many bindings does it introduce? This and other questions come into play when trying to figure out, how we can make our applications faster.

+

A couple of weeks ago, I had the honour to give a talk about Angular and React - Friends learning from each other at NG-BE together with Oliver Zeigermann, and we were discussing a demo application in which we compared the default performance and what we can do to make it faster. In this article we’d like to take this demo and show some tips and tricks to make it blazingly fast. One or the other trick might help speeding up your application as well.

+

UPDATE: We’ve written a follow-up article that discusses another option to make your app faster using Zones in Angular

+

Rendering 10000 draggable SVG boxes

+

Let’s start off with the demo itself. We wanted to go with a scenario in which the framework reaches its boundaries, so that possible performance improvements are easier to visualise. This doesn’t have to be a real world scenario, but rather challenging enough to showcase what we can do. That’s why we decided to render 10000 draggable SVG boxes. Rendering 10000 SVG boxes isn’t necessarily a hard nor realistic task, however, it gets quite interesting when each of these boxes need to be draggable, because… well whenever there’s a mousemove event being fired, Angular has to perform change detection and rerender what needs to be rerendered. With 10000 boxes, this can be quite a lot of work.

+
+

Special Tip: Learn in our article about Angular’s Change Detection why it’s performed on every mousemove event

+
+

The application consists of two components - AppComponent and BoxComponent - here’s what AppComponent looks like:

+
@Component({
+  selector: 'demo-app',
+  template: `
+    <svg (mousedown)="mouseDown($event)"
+         (mouseup)="mouseUp($event)"
+         (mousemove)="mouseMove($event)">
+
+      <svg:g box *ngFor="let box of boxes" [box]="box">
+      </svg:g>
+
+    </svg>
+  `
+})
+export class AppComponent implements OnInit {
+
+  currentId = null;
+  boxes = [];
+  offsetX;
+  offsetY;
+
+  ngOnInit() {
+    this.boxes = ... // generate 10000 random box coordinates
+  }
+
+  mouseDown(event) {
+    const id = Number(event.target.getAttribute("dataId"));
+    const box = this.boxes[id];
+    this.offsetX = box.x - event.clientX;
+    this.offsetY = box.y - event.clientY;
+    this.currentId = id;
+  }
+
+  mouseMove(event) {
+    event.preventDefault();
+    if (this.currentId !== null) {
+      this.updateBox(this.currentId, event.clientX + this.offsetX, event.clientY + this.offsetY);
+    }
+  }
+
+  mouseUp() {
+    this.currentId = null;
+  }
+
+  updateBox(id, x, y) {
+    const box = this.boxes[id];
+    box.x = x;
+    box.y = y;
+  }
+}
+

Let’s not get too overwhelmed by the code. The only really important parts here are:

+
    +
  • We have an SVG element with event handlers for mousedown, mousemove and mouseup
  • +
  • We generate 10000 random coordinates for the boxes, which are rendered using ngFor
  • +
  • We update the box that is being dragged in mouseMove()
  • +
+

Okay, next we take a look at the BoxComponent.

+
@Component({
+  selector: '[box]',
+  template: `
+    <svg:rect
+      [attr.dataId]="box.id"
+      [attr.x]="box.x"
+      [attr.y]="box.y"
+      width="20"
+      height="20"
+      stroke="black"
+      [attr.fill]="selected ? 'red' : 'transparent'"
+      strokeWidth="1"></svg:rect>
+  `
+})
+export class BoxComponent {
+  @Input() box;
+  @Input() selected;
+}
+

This one really just renders an SVG rect element using a couple of bindings to set the coordinates from the given box object.

+

By clicking and dragging a box, we can see and feel that the app is quite janky. Time to measure how fast it really is!

+

Measuring the app’s performance

+

We’re interested in runtime performance and how long it takes Angular to perform a task when the mousemove event has been fired, as this is the work Angular has to do when we drag and drop a box.

+

Measuring these kind of things nowadays is fairly easy. Chrome’s devtools for example come with an excellent Timeline Tool that lets us profile JavaScript execution and paint times per frame in the browser with a couple of mouse clicks.

+

Try it out yourself, all you need to do is:

+
    +
  • Open the devtools (ALT+CMD+I)
  • +
  • Choose Timeline
  • +
  • Press the record button in the top right corner or (CMD+E)
  • +
  • Click and drag a box
  • +
  • Stop the recording by clicking the red record button again
  • +
+

Ideally, any kind of measuring is done in an incognito browser tab so the recorded numbers won’t be affected by resources used by browser extensions or other browser tabs. Also, try to measure not only once but rather 3-5 times as the results will always vary a little bit. This helps us coming up with a decent average value.

+
+

Special Tip: Always profile in incognito tabs to makes sure recorded numbers aren’t affected by browser extensions or tabs taking up resources.

+
+

Here are the numbers we measured on a MacBook Air (1,7 GHz Intel Core i7, 8 GB DDR3) in Chrome (Version 55.0.2883.95 (64-bit)):

+

+ + + dnd perf profile + +

+
    +
  • 1st Profile, Event (mousemove): ~40ms, ~52ms (fastest, slowest)
  • +
  • 2nd Profile, Event (mousemove): ~45ms, ~61ms (fastest, slowest)
  • +
  • 3rd Profile, Event (mousemove): ~41ms, ~52ms (fastest, slowest)
  • +
+

Please note that there are multiple mousemove events being fired when moving the mouse, that’s why we’re showing fastest and slowest numbers. Also, these numbers might look different on your local machine. Okay, so it takes Angular roughly ~42ms - ~55ms in average to render 10000 boxes. That’s not too bad, considering that this is completely unoptimized. However, we want to learn how to make it faster. Let’s take a look!

+

How to make it fast(er)

+

Again, Angular is very fast right out of the box. However, it turns out that there are a lot of things we can do to make our code even faster. The following are ideas for performance improvements we’ve came up with in collaboration with Angular’s core team member Tobias, who mainly works on the compiler and kind of knows how to make things fast.

+
    +
  • ChangeDetectionStrategy.OnPush - Use Angular’s OnPush change detection strategy to save view bindings per change detection task
  • +
  • “Simple” NgFor - Angular’s NgFor directive is sometimes a little bit too smart. A simpler version of it could be faster
  • +
  • Detach change detectors - Another option is to detach all change detectors from the tree and only perform change detection for the components that is actually changing
  • +
+

Use OnPush change detection strategy

+

Probably the most obvious thing to do. In our article on Change Detection in Angular we talked about how Angular’s OnPush change detection strategy enables us to reduce the number of checks Angular has to make when a change in our application happens.

+

The idea is to make Angular only check a component’s view bindings if one of its inputs have changed. In our demo, we have only two components - AppComponent and BoxComponent - and only BoxComponent is receiving inputs. If we set BoxComponent’s change detection strategy to OnPush, we should be able to save 4 bindings per box (4 bindings, because there are 4 property bindings in BoxComponent’s view). That is 39996 bindings in total (one box is changing). In order to use OnPush properly, we need to make two tiny changes in our code: Set the change detection strategy and make sure that BoxComponent’s input values are immutable.

+

Here’s how we set the change detection strategy to OnPush:

+
import { 
+  Component,
+  Input,
+  ChangeDetectionStrategy
+} from '@angular/core';
+
+@Component({
+  selector: '[box]',
+  changeDetection: ChangeDetectionStrategy.OnPush, // set to OnPush
+  ...
+})
+export class BoxComponent {
+  @Input() box;
+  @Input() selected;
+}
+

To make all inputs immutable, we simply create new references every time we update a box:

+
@Component(...)
+class AppComponent {
+  ...
+  updateBox(id, x, y) {
+    this.boxes[id] = { id, x, y }; // new references instead of mutation
+  }
+}
+

At this point it gets rather hard to notice an actual difference. This is because the previous unoptimized demo was already pretty fast. Let’s measure again and see if our application is faster (these profiles are made on the same machine as the previous ones).

+

+
    +
  • 1st Profile, Event (mousemove): ~25ms, ~35ms (fastest, slowest)
  • +
  • 2nd Profile, Event (mousemove): ~21ms, ~44ms (fastest, slowest)
  • +
  • 3rd Profile, Event (mousemove): ~23ms, ~37ms (fastest, slowest)
  • +
+

As we can see, using OnPush does indeed improve our runtime performance. We may not notice a huge visual difference, but as we can see in the numbers, our application is now about as twice as fast. Considering the small change we needed to do, this is a great result!

+

It turns out, we could improve this even further. Right now, all 10000 boxes are checked, only the views are skipped - you know, the 39996 bindings. This is because in order to find out if an input has changed or not (to skip a component’s view), Angular needs to check the component. We could introduce some kind of segmentation model that divides the 10000 boxes into, let’s say ten, segments with 1000 boxes each. This would reduce the number of components to be checked to just 999.

+

However, this would not only make our code way more complicated, it also changes the application algorithm, which is what we’re trying to avoid in the first place.

+

Using a simpler NgFor

+

Another thing that is rather not obvious in our demo, is that NgFor might take up more time than it should. Now, what does that mean? Well, if we take a look at NgFor’s source code, we can see that it not only creates DOM items it iterates through, it also keeps track of the position of each item, in case things have been moved around. This is great for animations as we can animate-in and -out naturally.

+

In our demo however, we don’t really move the items in the collection itself. In fact, we don’t really touch it at all. So what if we can use a simpler NgFor that doesn’t care about item positions in the collection? Creating such a directive requires a bit more work and it would not fit in the scope of this article, which is why we will discuss the implementation of SimpleNgFor in another article.

+

+ + + dnd perf profile 3 + +

+

SimpleNgFor (without OnPush)

+
    +
  • 1st Profile, Event (mousemove): ~45ms, ~50ms (fastest, slowest)
  • +
  • 2nd Profile, Event (mousemove): ~43ms, ~53ms (fastest, slowest)
  • +
  • 3rd Profile, Event (mousemove): ~42ms, ~50ms (fastest, slowest)
  • +
+

SimpleNgFor (with OnPush)

+
    +
  • 1st Profile, Event (mousemove): ~22ms, ~32ms (fastest, slowest)
  • +
  • 2nd Profile, Event (mousemove): ~22ms, ~39ms (fastest, slowest)
  • +
  • 3rd Profile, Event (mousemove): ~21ms, ~30ms (fastest, slowest)
  • +
+

We’ve performed six measurements in total, three for the demo with just the SimpleNgFor directive and another three with OnPush enabled. As we can see, SimpleNgFor improves the numbers a little tiny bit. Again, if you profile on your local machine, those numbers might be different. Considering the amount of work that needs to be done to create SimpleNgFor, it’s probably not worth it if we only save ~5ms in average.

+

Detach change detectors from change detector tree

+

Angular is pretty powerful when it comes to giving us developers control over how things are processed in the platform. OnPush enables us to decide when and where in the component tree change detection is skipped when a change has happened. While this is already super powerful, it turns out that there can be cases where even that is not enough. That’s why Angular gives us access to ChangeDetectorRef’s of each component, with which we can enable or disable change detection entirely. We touched on that in our article on change detection in Angular as well, but let’s discuss how this is useful in our demo application.

+

We’re dealing with 10000 draggable SVG boxes and all of them are checked on every change (every mousemove event). As discussed earlier, as of how our application is constructed right now, OnPush doesn’t prevent Angular from checking the boxes themselves, only their views. However, what if we could turn off change detection entirely for all components and only perform change detection for the box component that is actually being moved? This would obviously result in way less work per task as we aren’t checking 10000 boxes anymore, but only one.

+

How do we get there? The first thing we do is, we detach the component’s change detectors from the tree. We can inject a component’s ChangeDetectorRef using DI and use its detach() method for that. The only thing we need to keep in mind is that we only want to detach the change detectors after change detection has been performed for the first time, otherwise we won’t see any boxes. To call detach() in the right moment, we can take advantage of Angular’s AfterViewInit life cycle hook.

+

Here’s what that looks like:

+
import { AfterViewInit, ChangeDetectorRef } from '@angular/core';
+
+@Component(...)
+class AppComponent implements AfterViewInit {
+  ...
+  constructor(private cdr: ChangeDetectorRef) {}
+
+  ngAfterViewInit() {
+    this.cdr.detach();
+  }
+}
+

We do exactly the same for the all box components.

+
@Component(...)
+class BoxComponent implements AfterViewInit {
+  ...
+  constructor(private cdr: ChangeDetectorRef) {}
+
+  ngAfterViewInit() {
+    this.cdr.detach();
+  }
+}
+

Okay cool, now we should see all the boxes but the dragging and dropping doesn’t work anymore. That makes sense because change detection is turned off entirely and no handlers for any events are executed anymore.

+

The next thing we need to do, is to make sure that change detection is performed for the box that is being dragged. We can extend our BoxComponent to have a method update() which performs change detection just like this:

+
@Component(...)
+class BoxComponent implements AfterViewInit {
+  ...
+  update() {
+    this.cdr.detectChanges();
+  }
+}
+

Cool, now we need to call this method whenever needed. This is essentially in our mouseDown(), mouseMove() and mouseUp() handlers. But how to do we get access to that one particular box component instance that is touched? Simply relying on this.boxes[id] doesn’t do the trick anymore because it’s not an instance of BoxComponent. We need to extend the event object with such an instance so we access it accordingly.

+

This is where it gets a liiiittle hacky. We could expect the BoxComponent instance on the DOM event object we get from the mousedown event like this:

+
@Component(...)
+export class AppComponent implements AfterViewInit {
+  ...
+  mouseDown(event) {
+    const boxComponent = <BoxComponent> event.target['BoxComponent'];
+
+    const box = boxComponent.box;
+    const mouseX = event.clientX;
+    const mouseY = event.clientY;
+    this.offsetX = box.x - mouseX;
+    this.offsetY = box.y - mouseY;
+
+    this.currentBox = boxComponent;
+
+    boxComponent.selected = true;
+    boxComponent.update();
+  }
+}
+

To make the box component instance available on event.target, where target is the underlying SVG rect element, we can access it as a view child of BoxComponent and extend it with a new property that has the BoxComponent instance. We also add a local template variable #rect to the SVG element, so that ViewChild('rect') can query it accordingly.

+
@Component({
+  selector: '[box]',
+  template: `
+    <svg:rect #rect ...></svg:rect>
+  `
+})
+export class BoxComponent implements AfterViewInit {
+
+  @ViewChild('rect')
+  set rect(value: ElementRef) {
+    if (value) {
+      value.nativeElement['BoxComponent'] = this;
+    }
+  }
+  ...
+}
+

Great! We can now go ahead and use BoxComponent.update() in all other methods where needed.

+

+ + + dnd perf profile 4 + +

+

JavaScript execution time is now always below 1ms, which makes sense as Angular has to only check the box component that is dragged by the user. This shows very nicely how much control Angular gives us as developers and how we can take advantage of that for our specific needs.

+

Conclusion

+

Angular is very fast by default and still, it gives us tools to fine tune the performance of our applications by handing over a lot of control over how change detection is performed. Is there anything else we can do? It turns out yes.

+

All demos have been executed in Angular’s dev mode. We can turn on production mode, which should increase performance a little bit further as it makes sure to run change detection only once, as opposed to twice in dev mode.

+

We should keep in mind though, that none of the shown tricks is a silver bullet. They may or may not work for your particular use case.

+

Hopefully you’ve learned one or the other thing about how to make your apps faster!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/02/21/using-zones-in-angular-for-better-performance.html b/angular/2017/02/21/using-zones-in-angular-for-better-performance.html new file mode 100644 index 000000000..a84d48a86 --- /dev/null +++ b/angular/2017/02/21/using-zones-in-angular-for-better-performance.html @@ -0,0 +1,166 @@ +Using Zones in Angular for better performance | Articles by thoughtram

Angular

Using Zones in Angular for better performance

In our latest article, we talked about how to make our Angular apps fast by exploring Angular’s ChangeDetectionStrategy APIs as well as tricks on how to detach change detectors and many more. While we were covering many different options to improve the demo application’s performance, we certainly haven’t talked about all possible options.

+

That’s why Jordi Collell pointed out that another option would be to take advantage of Zone APIs, to execute our code outside the Angular zone, which will prevent Angular from running unnecessary change detection tasks. He even put time and energy into creating a demo plunk that shows how to do exactly that.

+

We want to say thank you for his contribution and think that the solution he came up with deserves its own article. So in this article we’re going to explore his plunk and explain how Jordi used Zones to make our demo application perform at almost 60 fps.

+

Seeing it in action

+

Before we jump right into the code, let’s first take a look at the demo plunk with the running application. As a quick recap: The idea was to render 10.000 draggable SVG boxes. Rendering 10.000 boxes is not a super sophisticated task, however, the challenge lies in making the dragging experience as smooth as possible. In other words, we aim for 60 fps (frames per second), which can be indeed challenging, considering that Angular re-renders all 10.000 boxes by default when an event has fired (that we bound to).

+

Even though the difference is rather subtle, the optimized version performs much better in terms of JavaScript execution per frame. We’ll take a look at some numbers later, but let’s quickly recap Zones and then dive into the code and discuss how Jordi used Angular’s NgZone APIs to achieve this performance first.

+

The idea of Zones

+

Before we can use Zone APIs and specifically the ones from Angular’s NgZone, we need to get an understanding of what Zones actually are and how they are useful in the Angular world. We won’t go into too much detail here as we’ve already written two articles on this topic:

+
    +
  • Understanding Zones - Discusses the concept of Zones in general and how they can be used to e.g. profile asynchronous code execution
  • +
  • Zones in Angular - Explores how the underlying Zone APIs are used in Angular to create a custom NgZone, which enables consumers and Angular itself to run code inside or outside Angular’s Zone
  • +
+

If you haven’t read these articles yet, we definitely recommend you to do so as they give a very solid understanding of what Zones are and what they do. The bottom line is, however, Zones wrap asynchronous browser APIs, and notify a consumer when an asynchronous task has started or ended. Angular takes advantage of these APIs to get notified when any asynchronous task is done. This includes things like XHR calls, setTimeout() and pretty much all user events like click, submit, mousedown, … etc.

+

Once notified, Angular knows that it has to perform change detection because any of the asynchronous operations might have changed the application state. This, for instance, is always the case when we use Angular’s Http service to fetch data from a remote server. The following snippet shows how such a call can change application state:

+
@Component(...)
+export class AppComponent {
+
+  data: any; // initial application state
+
+  constructor(private dataService: DataService) {}
+
+  ngOnInit() {
+    this.dataService.fetchDataFromRemoteService().subscribe(data => {
+      this.data = data // application state has changed, change detection needs to run now
+    });
+  }
+}
+

The nice thing about this is that we as developers don’t have to care about notifying Angular to perform change detection, because Zones will do it for us as Angular subscribes to them under the hood.

+

Okay, now that we touched on that, let’s take a look at how they can be used to make our demo app fast.

+

Running outside Angular’s Zone

+

We know that change detection is performed whenever an asynchronous event happened and an event handler was bound to that event. This is exactly the reason why our initial demo performs rather jankee. Let’s look at AppComponent’s template:

+
@Component({
+  ...
+  template: `
+    <svg (mousedown)="mouseDown($event)"
+         (mouseup)="mouseUp($event)"
+         (mousemove)="mouseMove($event)">
+
+      <svg:g box *ngFor="let box of boxes" [box]="box">
+      </svg:g>
+
+    </svg>
+  `
+})
+class AppComponent {
+  ...
+}
+

Three (3) event handlers are bound to the outer SVG element. When any of these events fire and their handlers have been executed then change detection is performed. In fact, this means that Angular will run change detection, even when we just move the mouse over the boxes without actually dragging a single box!

+

This is where taking advantage of NgZone APIs comes in handy. NgZone enables us to explicitly run certain code outside Angular’s Zone, preventing Angular to run any change detection. So basically, handlers will still be executed, but since they won’t run inside Angular’s Zone, Angular won’t get notified that a task is done and therefore no change detection will be performed. We only want to run change detection once we release the box we are dragging.

+

Okay, how do we achieve this? In our article on Zones in Angular, we already discussed how to run code outside Angular’s Zone using NgZone.runOutsideAngular(). All we have to do is to make sure that the mouseMove() event handler is only attached and executed outside Angular’s zone. In addition to that, we know we want to attach that event handler only if a box is being selected for dragging. In other words, we need to change our mouseDown() event handler to imperatively add that event listener to the document.

+

Here’s what that looks like:

+
import { Component, NgZone } from '@angular/core';
+
+@Component(...)
+export class AppComponent {
+  ...
+  element: HTMLElement;
+
+  constructor(private zone: NgZone) {}
+
+  mouseDown(event) {
+    ...
+    this.element = event.target;
+
+    this.zone.runOutsideAngular(() => {
+      window.document.addEventListener('mousemove', this.mouseMove.bind(this));
+    });
+  }
+
+  mouseMove(event) {
+    event.preventDefault();
+    this.element.setAttribute('x', event.clientX + this.clientX + 'px');
+    this.element.setAttribute('y', event.clientX + this.clientY + 'px');
+  }
+}
+

We inject NgZone and call runOutsideAngular() inside our mouseDown() event handler, in which we attach an event handler for the mousemove event. This ensures that the mousemove event handler is really only attached to the document when a box is being selected. In addition, we save a reference to the underlying DOM element of the clicked box so we can update its x and y attributes in the mouseMove() method. We’re working with the DOM element instead of a box object with bindings for x and y, because bindings won’t be change detected since we’re running the code outside Angular’s Zone. In other words, we do update the DOM, so we can see the box is moving, but we aren’t actually updating the box model (yet).

+

Also, notice that we removed the mouseMove() binding from our component’s template. We could remove the mouseUp() handler as well and attach it imperatively, just like we did with the mouseMove() handler. However, it won’t add any value performance-wise, so we decided to keep it in the template for simplicity’s sake:

+
<svg (mousedown)="mouseDown($event)"
+      (mouseup)="mouseUp($event)">
+
+  <svg:g box *ngFor="let box of boxes" [box]="box">
+  </svg:g>
+
+</svg>
+

In the next step, we want to make sure that, whenever we release a box (mouseUp), we update the box model, plus, we want to perform change detection so that the model is in sync with the view again. The cool thing about NgZone is not only that it allows us to run code outside Angular’s Zone, it also comes with APIs to run code inside the Angular Zone, which ultimately will cause Angular to perform change detection again. All we have to do is to call NgZone.run() and give it the code that should be executed.

+

Here’s the our updated mouseUp() event handler:

+
@Component(...)
+export class AppComponent {
+  ...
+  mouseUp(event) {
+    // Run this code inside Angular's Zone and perform change detection
+    this.zone.run(() => {
+      this.updateBox(this.currentId, event.clientX + this.offsetX, event.clientY + this.offsetY);
+      this.currentId = null;
+    });
+
+    window.document.removeEventListener('mousemove', this.mouseMove);
+  }
+}
+

Also notice that we’re removing the event listener for the mousemove event on every mouseUp. Otherwise, the event handler would still be executed on every mouse move. In other words, the box would keep moving even after the finger was lifted, essentially taking the drop part out of drag and drop. In addition to that, we would pile up event handlers, which could not only cause weird side effects but also blows up our runtime memory.

+

Measuring the performance

+

Alright, now that we know how Jordi implemented this version of our demo application, let’s take a look at some numbers! The following numbers have been recorded using the exact same techniques on the exact same machine as in our previous article on performance.

+

+ + + dnd perf profile 5 + +

+
    +
  • 1st Profile, Event (mousemove): ~0.45ms, ~0.50ms (fastest, slowest)
  • +
  • 2nd Profile, Event (mousemove): ~0.39ms, ~0.52ms (fastest, slowest)
  • +
  • 3rd Profile, Event (mousemove): ~0.38ms, ~0.45ms (fastest, slowest)
  • +
+

Conclusion

+

Using Zones is a great way to escape Angular’s change detection, without detaching change detectors and making the application code too complex. In fact, it turns out that Zones APIs are super easy to use, especially NgZone’s APIs to run code outside or inside Angular. Based on the numbers, we can even say that this version is about as fast as the fastest solution we came up with in our previous article. Considering that the developer experience is much better when using Zones APIs, since they are easier to use than manually detaching and re-attaching change detector references, it’s definitely the most “beautiful” performance improvement we have so far.

+

However, we shouldn’t forget that this solution also comes with a couple (probably fixable) downsides. For example, we’re relying on DOM APIs and the global window object, which is something we should always try to avoid. If we wanted to use this code with on the server-side then direct access of the window variable would be problematic. We will discus these server-side specific issues in a future article. For the sake of this demo, this isn’t a big deal though.

+

Again, a huge shout-out goes to Jordi Collell who not only made us adding this option, but also taking the time to actually implement a first version of this demo!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html b/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html new file mode 100644 index 000000000..2bcc3b82a --- /dev/null +++ b/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html @@ -0,0 +1,231 @@ +Three things you didn't know about the AsyncPipe | Articles by thoughtram

Angular

Three things you didn't know about the AsyncPipe

You sure heard about Angular’s AsyncPipe haven’t you? It’s this handy little pipe that we can use from within our templates so that we don’t have to deal with unwrapping data from Observables or Promises imperatively. Turns out the AsyncPipe is full of little wonders that may not be obvious at first sight. In this article we like to shed some light on the inner workings of this useful little tool.

+

Subscribing to long-lived Observables

+

Often when we think about the AsyncPipe, we only think about values that resolve from some http call. We issue an http call, get an Observable<Response> back, apply some transformations (e.g. map(...).filter(...)) and finally expose an Observable to the template of our component. Here is what that typically looks like.

+
...
+@Component({
+  ...
+  template: `
+    <md-list>
+      <a md-list-item
+        *ngFor="let contact of contacts | async"
+        title="View {{contact.name}} details">
+        <img md-list-avatar [src]="contact.image" alt="Picture of {{contact.name}}">
+        <h3 md-line>{{contact.name}}</h3>
+      </a>
+    </md-list>`,
+})
+export class ContactsListComponent implements OnInit {
+
+  contacts: Observable<Array<Contact>>;
+
+  constructor(private contactsService: ContactsService) {}
+
+  ngOnInit () {
+    this.contacts = this.contactsService.getContacts();
+  }
+}
+

In the described scenario our Observable is what we like to refer to as short-lived. The Observable emits exactly one value - an array of contacts in this case - and completes right after that. That’s the typical scenario when working with http and it’s basically the only scenario when working with Promises.

+

However, we can totally have Observables that emit multiple values. Think about working with websockets for instance. We may have an array that builds up over time! Let’s simulate an Observable that emits an array of numbers. But instead of emitting just a single array once, it will emit an array every time a new item was added. To not let the array grow infinitely we will limit it to the last five items.

+
...
+@Component({
+  selector: 'my-app',
+  template: `
+    <ul>
+      <li *ngFor="let item of items | async">{{item}}</li>
+    </ul>`
+})
+export class AppComponent {
+  items = Observable.interval(100)
+                    .scan((acc, cur)=>[cur, ...acc].slice(0, 5), []);             
+}
+

Notice how our list is kept nicely in sync without further ado thanks to the AsyncPipe!

+

Keeping track of references

+

Let’s back of and refactor above code to what it would look like without the help of the AsyncPipe. But while we’re at it, let’s introduce a button to restart generating numbers and pick a random background color for the elements each time we regenerate the sequence.

+
...
+@Component({
+  selector: 'my-app',
+  template: `
+    <button (click)="newSeq()">New random sequence</button>
+    <ul>
+      <li [style.background-color]="item.color"
+          *ngFor="let item of items">{{item.num}}</li>
+    </ul>`
+})
+export class AppComponent {
+  items = [];
+
+  constructor () {
+    this.newSeq();
+  }
+
+  newSeq() {
+
+    // generate a random color
+    let color = '#' + Math.random().toString(16).slice(-6);
+
+    Observable.interval(1000)
+          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])
+          .subscribe(items => this.items = items);
+  }
+}
+

Let’s refactor the code to track subscriptions and tear down our long-lived Observable every time that we create a new one.

+
...
+export class AppComponent {
+  ...
+  subscription: Subscription;
+
+  newSeq() {
+
+    if (this.subscription) {
+      this.subscription.unsubscribe();
+    }
+
+    // generate a random color
+    let color = '#' + Math.random().toString(16).slice(-6);
+
+    this.subscription = Observable.interval(1000)
+          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])
+          .subscribe(items => this.items = items);
+  }
+}
+

Every time we subscribe to our Observable we save the subscription in an instance member of our component. Then, when we run newSeq again we check if there’s a subscription that we need to call unsubscribe on. That’s why we don’t see our list flipping between different colors anymore no matter how often we click the button.

+

Now meet the AsyncPipe again. Let’s change the ngFor again to apply the AsyncPipe and get rid of all the manual bookkeeping.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <button (click)="newSeq()">New random sequence</button>
+    <ul>
+      <li [style.background-color]="item.color"
+          *ngFor="let item of items | async">{{item.num}}</li>
+    </ul>`
+})
+export class AppComponent {
+  items: Observable<any>;
+
+  constructor () {
+    this.newSeq();
+  }
+
+  newSeq() {
+
+    // generate a random color
+    let color = '#' + Math.random().toString(16).slice(-6);
+
+    this.items = Observable.interval(1000)
+                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []);
+  }
+}
+

I’m sure you’ve heard that the AsyncPipe unsubscribes from Observables as soon as the component gets destroyed. But did you also know it unsubscribes as soon as the reference of the expression changes? That’s right, as soon as we assign a new Observable to this.items the AsyncPipe will automatically unsubscribe from the previous bound Observable! Not only does this make our code nice and clean, it’s protecting us from very subtle memory leaks.

+

Marking things for check

+

Alright. We have one last nifty AsyncPipe feature for you! If you’ve read our article about Angular’s change detection you sure know that you can speed up Angular’s blazingly fast change detection even further using the OnPush strategy. Let’s refactor our example and introduce a SeqComponent to display the sequences while our root component will manage the data and pass it on via an input binding.

+

Let’s start creating the SeqComponent which is pretty straight forward.

+
@Component({
+  selector: 'my-seq',
+  template: `
+    <ul>
+      <li [style.background-color]="item.color" 
+          *ngFor="let item of items">{{item.num}}</li>
+    </ul>`
+})
+export class SeqComponent {
+  @Input()
+  items: Array<any>;
+}
+

Notice the @Input()decorator for items which means the component will receive those from the outside via a property binding. +Our root component maintains an array seqs and pushes new long-lived Observables into it with the click of a button. It uses an *ngFor to pass each of these Observables on to a new SeqComponent instance. Also notice that we are using the AsyncPipe in our property binding expression ([items]="seq | async") to pass on the plain array instead of the Observable since that’s what the SeqComponent expects.

+
@Component({
+  selector: 'my-app',
+  template: `
+    <button (click)="newSeq()">New random sequence</button>
+    <ul>
+      <my-seq *ngFor="let seq of seqs" [items]="seq | async"></my-seq>
+    </ul>`
+})
+export class AppComponent {
+  seqs = [];
+  
+  constructor () {
+    this.newSeq();
+  }
+  
+  newSeq() {
+    
+    // generate a random color
+    let color = '#' + Math.random().toString(16).slice(-6);
+    
+    this.seqs.push(Observable.interval(1000)
+                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []));
+  }
+}
+

So far, we haven’t made any changes to the underlying change detection strategy. If you click the button a couple of times, notice how we get multiple lists that update independently at a different timing.

+

However, in terms of change detection, it means that all components are checked each time any of the Observables fire. That’s a waste of resources. We can do better by setting the change detection for our SeqComponent to OnPush which means it will only check it’s bindings if the inputs - the array in our case - changes.

+
@Component({
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  selector: 'my-seq',
+  ...
+})
+

That works and seems to be an easy quick win. But here comes the thing: It only works because our Observable creates an entirely new array each time it emits a new value. And even though that’s actually not too bad and in fact beneficial in most scenarios, let’s consider we use a different implementation which mutates the existing array rather than recreating it every time.

+
Observable.interval(1000)
+          .scan((acc, num)=>{
+            acc.splice(0, 0, {num, color});
+            if (acc.length > 5) {
+              acc.pop()
+            }
+            return acc;
+          }, [])
+

If we try that out OnPush doesn’t seem to work anymore because the reference of items simply won’t change anymore. In fact, when we try that out we see each list doesn’t grow beyond its first element.

+

Meet the AsyncPipe again! Let’s change our SeqComponent so that it takes an Observable instead of an array as its input.

+
@Component({
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  selector: 'my-seq',
+  template: `
+    <ul>
+      <li [style.background-color]="item.color" 
+          *ngFor="let item of items | async">{{item.num}}</li>
+    </ul>`
+})
+export class SeqComponent {
+  @Input()
+  items: Observable<Array<any>>;
+}
+

Also note that it now applies the AsyncPipe in its template since it’s not dealing with a plain array anymore. Our AppComponent needs to be changed as well to not apply the AsyncPipe in the property binding anymore.

+
<ul>
+  <my-seq *ngFor="let seq of seqs" [items]="seq"></my-seq>
+</ul>
+

Voila! That seems to work!

+

Let’s recap, our array instance doesn’t change nor does the instance of our Observable ever change. So why would OnPush work in this case? The reason can be found in the source code of the AsyncPipe itself

+
private _updateLatestValue(async: any, value: Object): void {
+  if (async === this._obj) {
+    this._latestValue = value;
+    this._ref.markForCheck();
+  }
+}
+

The AsyncPipe marks the ChangeDetectorRef of our component to be checked, effectively telling the change detection that there may be a change in this component. If you like to get a more detailed understanding of how that works we recommend reading our in-depth change detection article.

+

Conclusion

+

We use to look at the AsyncPipe as a nifty little tool to save a couple of lines of code in our components. In practice though, it hides a lot of complexity from us that comes with managing async tasks. It’s pure gold.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angular/2017/05/08/angular-master-class-redux-and-ngrx.html b/angular/2017/05/08/angular-master-class-redux-and-ngrx.html new file mode 100644 index 000000000..9054d734b --- /dev/null +++ b/angular/2017/05/08/angular-master-class-redux-and-ngrx.html @@ -0,0 +1,85 @@ +Angular Master Class - Redux and ngrx | Articles by thoughtram

Angular

Angular Master Class - Redux and ngrx

Despite some appearances at ng-conf and Jazoon Conference, it’s been a little bit quiet around us since the last time we blogged. You might have read our announcement about the upcoming Angular Master Class in Denmark, however, apart from that, many people have been wondering what we’ve been up to these days and what’s the reason for not publishing Angular articles at the pace we usually do. Let’s get straight to the point: We’ve been working super hard on some very exciting things. One of these things is - you probably guessed it - brand new Angular Master Class courseware, and we can’t wait to see it in action in our upcoming classes!

+

+ + + ngrx slide logo + +

+

What’s new?

+

We’ve seen a lot of interest in topics around architecture and state management in bigger Angular applications. Common questions regarding this topic are ”How to manage state across many components that deal with different kinds of data?” or ”How do we ensure a robust and predictable data flow?”. That’s why we decided to extend the Angular Master Class experience with additional courseware on Redux and ngrx. You’ve probably heard of both, if not, here’s a brief primer:

+

Redux is a pattern that let’s us implement a predictable state container for our applications. ngrx is a library for Angular that implements Redux in a reactive fashion by using Observable APIs.

+

Hence, the courseware is split up into two parts. While the first part focusses on understanding Redux as a design pattern, how an implementation could look like, and how the redux library can be used in an application to take advantage of this pattern, the second part explores ngrx as the most appropriate solution for Angular applications, as it integrates perfectly with its reactive APIs.

+

To give you a better idea, here’s a rough overview of part one:

+
    +
  • Understanding Redux, Stores, Dispatchers and Actions
  • +
  • Implementing a custom Redux store
  • +
  • Using redux as implemented library in an Angular application
  • +
  • Applying middleware for side effects
  • +
  • Learning how to deal with deferred and asynchronous actions
  • +
+

Part two discusses the following things:

+
    +
  • What is ngrx and how does it compare to redux
  • +
  • Setting up ngrx stores and creating an dispatching actions
  • +
  • Building and using store selectors
  • +
  • Improving performance with reselect queries
  • +
  • Applying third-party and custom middleware
  • +
  • Dealing asynchronous actions using ngrx Effects
  • +
  • Creating facade architectures for better reusability
  • +
  • Devtools
  • +
+

Who is this training for?

+

We consider this additional courseware as one of the more advanced topics, especially because the ngrx requires knowledge that goes far beyond the basics. This means, this training is for you if you’ve already attended one of our Angular Master Classes.

+

When will we teach it?

+

It’s really up to you! As always, we put a lot of time into figuring out how the new courseware can fit into the existing one and we think we came up with the best possible solution for our setup.

+

The courseware is built in a way that it can either form a full isolated single-day training, or, being part of the bigger Angular Master Class experience. This means, if you’ve already attended one of our 3-day classes, this is the right time to reach out to us to discuss training on Redux and ngrx on top of that!

+

What else are we working on?

+

Of course we don’t stop here. There’s even more courseware material in the making that’s almost finished, and we know, almost everyone needs it.

+

Make sure to follow us on Twitter and Facebook or subscribe to our blog, to get notified when we announce it in the next couple of weeks!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/05/23/custom-themes-with-angular-material.html b/angular/2017/05/23/custom-themes-with-angular-material.html new file mode 100644 index 000000000..528ab1e6c --- /dev/null +++ b/angular/2017/05/23/custom-themes-with-angular-material.html @@ -0,0 +1,491 @@ +Custom themes with Angular Material | Articles by thoughtram

Angular

Custom themes with Angular Material

When building bigger applications, we always strive for flexibility, extensibility and reusability. This applies not only to the actual application logic, but also to style sheets. Especially nowadays, where things like CSS variables and modules exist. These tools are great and they solve many different problems in a very elegant way. However, one thing that’s still super hard to do these days is theming. Being able to use existing, or create new components, but easily changing their look and feel without changing their code. This is often required when we build things that can be reused across different projects, or if the project we’re working on should simply enable the user to change the color scheme.

+

The Angular Material project comes with a built-in story for theming, including using any of Material Design’s own predefined themes, but also creating custom themes that will be used not only by components provided by Angular Material, but also our own custom ones.

+

In this article we’ll explore how theming is implemented, how pre-built themes can be used and how we can make our own custom components theme-able so they pick up the configured theme as well!

+

What is a theme?

+

“Theme” can mean many different things to different people, so it’s good to clarify what a theme in the context of Angular Material means. Let’s get right into it.

+

The official theming guide is pretty much to the point here:

+
+

A theme is a set of colors that will be applied to the Angular Material components.

+
+

To be more specific, a theme is a composition of color palettes. That’s right, not just a single color palette, multiple color palettes. While this might sound unnecessary first, it turns out that this is a very powerful setup to define themes in a very flexible way.

+

Alright, but what color palettes are needed to compose a theme? As for Angular Material, it boils down to five different color palettes with each being used for different parts of the design:

+
    +
  • Primary - Main colors most widely used across all screens and components.
  • +
  • Accent - Also known as the secondary color. Used for floating action buttons and interactive elements.
  • +
  • Warn - Colors to convey error state.
  • +
  • Foreground - Used for text and icons.
  • +
  • Background - Colors used for element backgrounds.
  • +
+

If you want to dive deeper into the whole color usuability story in Material Design, we recommend checking out the Material Design Specification for colors, as it describes the topic in very deep detail.

+

Using pre-built themes

+

As mentioned earlier, Angular Material already comes with a set of pre-built themes that can be used right out of the box. Available pre-built themes are: deeppurple-amber, indigo-pink, pink-bluegrey and purple-green.

+

Using them is as easy as including or importing the dedicated CSS file that comes with all Angular Material builds. So assuming we’ve installed Angular Material in our Angular CLI project using:

+
$ yarn|npm install --save @angular/material
+

We can go ahead and add any of the pre-built CSS files to our global styles by configuring our .angular-cli.json accordingly:

+
{
+  ...
+  "styles": [
+    "../node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
+    "styles.scss"
+  ]
+  ...
+}
+

Or, if we don’t want to fiddle around in our angular-cli.json file, we can also import any pre-built theme right into the projects styles.scss file like this:

+
@import '../node_modules/@angular/material/prebuilt-themes/indigo-pink.css';
+

We can easily try it out by having our application using Angular Material components. So first we add MaterialModule to our AppModule’s imports:

+
import { MaterialModule } from '@angular/material';
+
+@NgModule({
+  imports: [
+    ...
+    MaterialModule
+  ],
+  ...
+})
+export class AppModule {}
+

Then we go ahead and render, for example, Angular Material’s tool bar component:

+
@Component({
+  selector: 'my-app',
+  template: `
+    <md-toolbar>Awesome toolbar</md-toolbar>
+  `
+})
+export class AppComponent {}
+

+ + + md toolbar + +

+

Looks cool right? Another thing that’s worth mentioning is that some Material components offer properties to configure whether they use the current theme’s primary, accent or warn color:

+
<md-toolbar color="primary">Awesome toolbar</md-toolbar>
+

+ + + md toolbar primary + +

+

Custom theme using built-in color palettes

+

Alright, using pre-built themes is a pretty cool thing as we get good looking components right away without doing any serious work. Let’s talk about how to create a custom theme using Angular Material’s predefined color palettes.

+

In order to create a custom theme, we need to do a couple of things:

+
    +
  • Generate core styles - These are theme independent styles, including styles for elevation levels, ripple effects, styles for accessibility and overlays
  • +
  • Primary color palette - Generate color palette for the theme’s primary color
  • +
  • Accent color palette - Generate color palette for the theme’s accent color
  • +
  • Warn color palette - Generate color palette for the theme’s warn color
  • +
  • Theme generation - Given the color palettes we generated, we create a theme, which can be used by Angular Material, or custom components
  • +
+

While this looks like a lot of work, it turns out Angular Material gives us many tools to make these tasks a breeze. Let’s start off by creating a new custom-theme.scss file and import it in our root styles.scss instead of the pre-built theme. After that we’ll go through the list step by step:

+
@import './custom-theme';
+

Generate core styles

+

This is a pretty easy one. Angular Material provides many very powerful SCSS mix-ins that do all the ground work for us. The mix-in that generates Material’s core styles is called mat-core. All we have to do is to import and call it.

+

Here’s what that looks like (custom-theme.scss):

+
@import '../node_modules/@angular/material/theming';
+
+@include mat-core();
+

Generate color palettes

+

The next thing we need to do is to generate color palettes, which can then be composed to an actual theme. To generate a color palette, we can use Angular Material’s mat-palette mix-in. mat-palette takes a base palette (yes, that’s another palette, more on that in a second), and returns a new palette that comes with Material specific hue color values for “light”, “dark” and “contrast” colors of the given base palette.

+

But what is this base palette? The base palette is just another color palette that compromises primary and accent colors of a single color. Wait, this sounds super confusing! Let’s take Material Design’s red color palette as an example:

+

+ + + material design red palette + +

+

Here we see all color codes for lighter and darker versions of the color red, as part of the Material Design specification. The values 50 - 900 represent the hue values or the “strength” of the color, or how light or dark it is. 500 is the recommended value for a theme’s primary color. There are much more defined color palettes and they are very nicely documented right here.

+

So now that we know what a base palette is, we need to figure out how to create such a thing. Do we have to define and write them ourselves? The answer is yes and no. If we want to use our own custom color palettes, we need to define them manually. However, if we want to use any of Material Design colors, Angular Material comes with predefined palette definitions for all of them! If we take a quick look at the source code, we can see how to palette for the color red is implemented:

+
$mat-red: (
+  50: #ffebee,
+  100: #ffcdd2,
+  200: #ef9a9a,
+  300: #e57373,
+  400: #ef5350,
+  500: #f44336,
+  600: #e53935,
+  700: #d32f2f,
+  800: #c62828,
+  900: #b71c1c,
+  A100: #ff8a80,
+  A200: #ff5252,
+  A400: #ff1744,
+  A700: #d50000,
+  contrast: (
+    50: $black-87-opacity,
+    100: $black-87-opacity,
+    200: $black-87-opacity,
+    300: $black-87-opacity,
+    400: $black-87-opacity,
+    500: white,
+    600: white,
+    700: white,
+    800: $white-87-opacity,
+    900: $white-87-opacity,
+    A100: $black-87-opacity,
+    A200: white,
+    A400: white,
+    A700: white,
+  )
+);
+

It’s basically just a map where each key (tone value) maps to a color code. So if we ever want to define our own custom color palette, this is what it could look like.

+

Okay, let’s create a palette for our primary, accent and warn colors. All we have to do is to call the mat-palette mix-in with a base color palette. Let’s use $mat-light-blue for primary, $mat-orange for accent and $mat-red for warn colors. We can simply reference these variables because we imported Angular Material’s theming capabilities in the previous step:

+
$custom-theme-primary: mat-palette($mat-light-blue);
+$custom-theme-accent: mat-palette($mat-orange, A200, A100, A400);
+$custom-theme-warn: mat-palette($mat-red);
+

Oh wait, what’s that? Why do we pass additional values to mat-palette when generating our accent color palette? Well… Let’s take a closer look at what mat-palette actually does.

+

Understanding mat-palette

+

We’ve already mentioned that mat-palette generates a Material Design color palette out of a base color palette. But what does that actually mean? In order to get a better picture of what’s going on in that mix-in, let’s take a look at its source code:

+
@function mat-palette($base-palette, $default: 500, $lighter: 100, $darker: 700) {
+  $result: map_merge($base-palette, (
+    default: map-get($base-palette, $default),
+    lighter: map-get($base-palette, $lighter),
+    darker: map-get($base-palette, $darker),
+
+    default-contrast: mat-contrast($base-palette, $default),
+    lighter-contrast: mat-contrast($base-palette, $lighter),
+    darker-contrast: mat-contrast($base-palette, $darker)
+  ));
+
+  // For each hue in the palette, add a "-contrast" color to the map.
+  @each $hue, $color in $base-palette {
+    $result: map_merge($result, (
+      '#{$hue}-contrast': mat-contrast($base-palette, $hue)
+    ));
+  }
+
+  @return $result;
+}
+

A mix-in is just a function - it takes arguments and returns something. mat-palette takes a base color palette (which is a map like $mat-red) and optional default values for the generated color palette’s default, lighter and darker colors. Eventually it returns a new color palette that has some additional map values. Those additional values are the mentioned default, lighter and darker colors, as well as their corresponding default-contrast, lighter-contrast and darker-contrast colors. On top of that it generates keys for contrast values for each base hue tone (50 - 900).

+

As we can see, we basically end up with a color palette that comes with everything the base palette provides, plus some additional keys for easy accessibility. So coming back to the question why we pass additional values to mat-palette for our accent color, we now know that all we do is configuring the default, lighter and darker color tone.

+

Generating themes

+

A theme lets us apply a consistent tone to our application. It specifies the darkness of the surfaces, level of shadow and appropriate opacity of ink elements. The Material Design specification describes two different variations of themes - dark and light.

+

Angular Material implements another set of mix-ins to generate either light or dark themes using mat-light-theme and mat-dark-theme respectively. Now that we have all of our color palettes in place, we can do exactly that. Let’s create a light theme object like this:

+
$custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn);
+

If we take a quick look at mat-light-theme’s source code, we can see that ti really just prepares another map object that can be later easily consumed for theming:

+
@function mat-light-theme($primary, $accent, $warn: mat-palette($mat-red)) {
+  @return (
+    primary: $primary,
+    accent: $accent,
+    warn: $warn,
+    is-dark: false,
+    foreground: $mat-light-theme-foreground,
+    background: $mat-light-theme-background,
+  );
+}
+

That’s it! We can now use that generated theme object and feed it to Angular Material’s angular-material-theme mix-in, which really just passes that theme object to other mix-ins for each component, so they can access the color values from there:

+
@include angular-material-theme($custom-theme);
+

Here’s the complete code for our custom theme, using $mat-light-blue and $mat-orange:

+
@import '../node_modules/@angular/material/theming';
+
+@include mat-core();
+
+$custom-theme-primary: mat-palette($mat-light-blue);
+$custom-theme-accent: mat-palette($mat-orange, A200, A100, A400);
+$custom-theme-warn: mat-palette($mat-red);
+
+$custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn);
+
+@include angular-material-theme($custom-theme);
+

Theming custom components

+

There’s one thing we haven’t talked about yet: theming custom components. So far we’ve only changed the look and feel of Angular Material’s components. That’s because we’re calling the angular-material-theme mix-in with our custom theme object. If we’d remove that call, we’d end up with all Material components in their base colors. This becomes more clear when we take a look at what angular-material-theme does:

+
@mixin angular-material-theme($theme) {
+  @include mat-core-theme($theme);
+  @include mat-autocomplete-theme($theme);
+  @include mat-button-theme($theme);
+  @include mat-button-toggle-theme($theme);
+  @include mat-card-theme($theme);
+  @include mat-checkbox-theme($theme);
+  @include mat-chips-theme($theme);
+  @include mat-datepicker-theme($theme);
+  @include mat-dialog-theme($theme);
+  @include mat-grid-list-theme($theme);
+  @include mat-icon-theme($theme);
+  @include mat-input-theme($theme);
+  @include mat-list-theme($theme);
+  @include mat-menu-theme($theme);
+  @include mat-progress-bar-theme($theme);
+  @include mat-progress-spinner-theme($theme);
+  @include mat-radio-theme($theme);
+  @include mat-select-theme($theme);
+  @include mat-sidenav-theme($theme);
+  @include mat-slide-toggle-theme($theme);
+  @include mat-slider-theme($theme);
+  @include mat-tabs-theme($theme);
+  @include mat-toolbar-theme($theme);
+  @include mat-tooltip-theme($theme);
+}
+

Every component in Angular Material comes with a dedicated theme mix-in, that takes a theme object to access its values for theme specific styles. We can use exactly the same pattern to theme our own custom components. This turns out to be very powerful because it enables us to easily change a theme in our entire application just by changing the theme object!

+

Let’s say we have a custom component FileTreeComponent as we created it in MachineLabs (a project you might want to check out!). FileTreeComponent renders a list of files and we want that component to respond to the configured theme. Here’s what its template looks like (simplified):

+
<ul class="ml-file-list">
+  <li class="ml-file-list-item" *ngFor="let file of files">
+    <md-icon>description</md-icon> {{file.name}}
+  </li>
+</ul>
+

It also comes with a base CSS file that introduces just enough styles so that the component is usable and accessible. No colors applied though. We won’t go into much detail here because there’s nothing new to learn. However, just to give a better idea, here are some corresponding base styles for FileTreeComponent:

+
.ml-file-list {
+  max-height: 250px;
+  overflow: scroll;
+  list-style: none;
+  margin: 0;
+  padding-left: 2em;
+  padding-right: 2em;
+  padding-top: 1.2em;
+  padding-bottom: 1.2em;
+}
+
+.ml-file-list-item {
+  font-size: 0.9em;
+  font-weight: 400;
+  padding: 0.4em 0.4em;
+  line-height: 1.4;
+  position: relative;
+
+  md-icon {
+    font-size: 1.2em;
+    vertical-align: -22%;
+    height: 18px;
+    width: 18px;
+  }
+
+  &:hover { cursor: pointer; }
+}
+

The component looks something like this:

+

+ + + ml file tree + +

+

We want to add theming capabilities to the following elements inside FileTreeComponent when a theme is applied:

+
    +
  • .ml-file-list needs a border in the “foreground” color of the configured theme
  • +
  • .ml-file-list-item needs the theme’s background hover color when hovering over it
  • +
  • When ml-file-list-item is selected, we need to give it a lighter version of the theme’s primary color
  • +
+

These rules can be easily implemented, simply by following the same pattern that Angular Material is using for its own components. We define a mix-in for FileTreeComponent that takes a theme object and uses that to access theme values using map-get and mat-color mix-ins.

+

Let’ start off by creating a ml-file-tree-theme mix-in and pull out the color palettes from the given theme we’re interested in (file-tree-theme.scss):

+
@mixin ml-file-tree-theme($theme) {
+
+  $primary: map-get($theme, primary);
+  $warn: map-get($theme, warn);
+  $background: map-get($theme, background);
+  $foreground: map-get($theme, foreground);
+
+}
+

Remember how mat-light-theme created additional values for foreground and background for our theme? With map-get we can access any value by its key of a given map. In other words, we’re pulling out color palettes for the theme’s primary, warn, background and foreground colors.

+

Once that is done, we can start using color values of these color palettes in our style sheets using the mat-color mix-in. mat-color takes a color palette and a hue value (or one of the descriptive names like lighter) returns the color corresponding color. If we want .ml-file-list to have a border in the divider foreground color of the given theme, it’d look something like this:

+
.ml-file-list {
+  border-bottom: 1px solid mat-color($foreground, divider);
+}
+

We use exactly the same technique to theme the background color of .ml-file-list-item like this:

+
.ml-file-list-item {
+
+  &:hover, &:active, &:focus {
+    background-color: mat-color($background, hover);
+  }
+
+  &.selected {
+    background-color: mat-color($primary, lighter, 0.5);
+    color: mat-color($foreground, text);
+  }
+}
+

One thing to note here is that mat-color takes an optional third argument to configure the color’s opacity.

+

That’s it! FileTreeComponent is now fully theme-aware and its look and feel responds to the configured theme. Here’s the complete code:

+
@mixin ml-file-tree-theme($theme) {
+
+  $primary: map-get($theme, primary);
+  $warn: map-get($theme, warn);
+  $background: map-get($theme, background);
+  $foreground: map-get($theme, foreground);
+
+  .ml-file-list {
+    border-bottom: 1px solid mat-color($foreground, divider);
+  }
+
+  .ml-file-list-item {
+
+    &:hover, &:active, &:focus {
+      background-color: mat-color($background, hover);
+    }
+
+    &.selected {
+      background-color: mat-color($primary, lighter, 0.5);
+      color: mat-color($foreground, text);
+    }
+  }
+}
+

And here’s what our component looks like now:

+

+ + + ml file tree themed + +

+

Last but not least, we have to call the ml-file-tree-theme mixing with our custom theme object. We do that by importing the mix-in in our custom-theme.scss file and execute it like this:

+
...
+@import 'app/lab-editor/file-tree/file-tree-theme.scss';
+
+...
+@include ml-file-tree-theme($custom-theme);
+

In fact, we can take it one level further and create a meta theme mix-in that executes all theme mix-ins for our custom components, the same way Angular Material does it with angular-material-theme. To do that we create a new mix-in custom-theme, which would look like this:

+
@mixin custom-theme($theme) {
+  @include ml-file-tree-theme($theme);
+}
+
+@include custom-theme($custom-theme);
+

Here again, the complete code of our custom-theme.scss file:

+
@import '../node_modules/@angular/material/theming';
+@import 'app/lab-editor/file-tree/file-tree-theme.scss';
+
+@include mat-core();
+
+$custom-theme-primary: mat-palette($mat-light-blue);
+$custom-theme-accent: mat-palette($mat-orange, A200, A100, A400);
+$custom-theme-warn: mat-palette($mat-red);
+
+$custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn);
+
+@mixin custom-theme($theme) {
+  @include ml-file-tree-theme(theme);
+}
+
+@include angular-material-theme($custom-theme);
+@include custom-theme($custom-theme);
+

Conclusion

+

Angular Material’s theming capabilities are very powerful and as of right now, it seems to be the only UI component library that gets it fairly right. Color palettes can be easily changed and reused and custom components can be enabled to consume a configured theme to match the look and feel of the entire application.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/07/26/a-web-animations-deep-dive-with-angular.html b/angular/2017/07/26/a-web-animations-deep-dive-with-angular.html new file mode 100644 index 000000000..8af6b03c5 --- /dev/null +++ b/angular/2017/07/26/a-web-animations-deep-dive-with-angular.html @@ -0,0 +1,419 @@ +A web animations deep dive with Angular | Articles by thoughtram

Angular

A web animations deep dive with Angular

Motion is an important aspect when building modern web applications. In fact, it’s important for all kinds of software products that involve user interfaces and interactions. Good user interfaces with well-designed animations help users understand the flow between two states. Imagine we are on a simple website that only has one button. We click that button and without any motion a box appears. Isn’t that boring? Also, we as a user might think that the box appeared because of our action. However, it could have been the result of something else like an http call in the background. In addition, animations can be used to make the user interface more snappy and responsive. They also explain changes in the arrangement of elements on the screen as some user actions may change the UI.

+

This could easily drift off into a whole new discussion about user experience and why motion matters. The bottom line is that motion not only makes a site more usable but also more fun. They tell stories, add a perceptible time dimension and improve the overall user experience of applications.

+

In this article we’ll briefly look at different ways to approach motion in modern web applications, specifically imperative and declarative animations. We’ll cover the basics of CSS and JavaScript animations before diving diving into a sophisticated animation in the context of an Angular application.

+

Let’s start with a deeper breath of how animations in the web generally work.

+

Understanding state transitions

+

With animations we aim to guide users between views so they feel comfortable using the site, draw attention to some parts of application, increase spacial awareness, indicate if data is being loaded, and probably the most important point - smoothly transition users between states. All this could be achieved by using CSS (declarative) or JavaScript (mostly imperative) to animate certain elements within your page.

+

What’s a transition you may ask. Very good question! The Oxford Dictionary defines a transition as follows:

+
+

The process or a period of changing from one state or condition to another.

+
+

Applied to animations, a transition is the visualization of some state changing over time. A state could be a person sitting at the airport waiting for his plane to be boarded. It’s a condition something or someone is in at a specific point in time. A button on website could have 4 different states - idle, hover, focus and pressed, where the latter is a combination of focus and active. We could use a finite state machine or simply state transition system to visualize how it works:

+

button state machine

+

The point is, a “system” or some element on the page can have multiple states. Instead of simply going from state A to B, we’d like to interpolate the values in between. Therefore, we can listen for state changes and act accordingly using animated transitions from one state to another.

+

What can we use to animate our UI?

+

Nowadays, with modern browsers in mind, we have many technologies at hand to animate our UI including CSS3 or JavaScript. The power of CSS animations (transitions or keyframes) is that they allow animating most HTML elements without using JavaScript. CSS animations are also quite fast and allow hardware acceleration. However, there are some limitations to this approach. For instance, you can’t animate an element along a certain path, use physics-based motion or animate the scroll position with CSS alone. While CSS animations are pretty good for simple transitions between states (e.g. hover-effects), JavaScript-based animations provide far more flexibility.

+

As a matter of fact, we can take advantage of the same hardware acceleration in JavaSript too. It’s as easy as setting a CSS property with a 3D characteristic, such as translate3d() or matrix3d(). This will push the particular element onto another layer which is then processed by the GPU. The GPU itself is highly optimized for moving pixels making it much for effective for animations compared to the CPU. For more information, check out this great article by Paul Lewis and Paul Irish on high performance animations.

+

CSS animations do not require any 3rd party libraries. However, there are some tools that make your life much easier, for example libraries that provide pre-defined keyframe animations like Animate.css.

+

Looking at JavaScript, we can either use vanilla JavaScript or jQuery to animate our UI. Maintaining vanilla JavaScript animations and manually setting up elements on the stage could be quite a hassle. That’s one reason why many of us switched to jQuery at some point. jQuery makes it easy to query an element on the page. Then, in order to add motion, all we do is to call .animate() and specify the properties (e.g. opacity or transform) we’d like to animate. Here is how we’d move a div to the right by 200px animating its left property:

+
$("button").click(function(){
+  $("div").animate({
+    left: '200px'
+  }, 'slow');
+});
+

While this works, it’s better to stick with either the transform or opacity property as those are the only things a browser can animate cheaply. Note that we use the string slow to specify the duration of the animation. It is an equivalent for supplying a duration of 600 milliseconds.

+

Turns out there is another cool kid on the block called GreenSock, a high-performance HTML5 animation library for the modern web. It allows us to define simple animations as well as complex timeline compositions, drag and drop features or even smoothly morph any SVG shape into any other shape. According to GreenSock, GSAP performs 20 times faster than jQuery. If you’d like to see this in action, there is a speed comparison to stress test the performance of various common JavaScript libraries including jQuery, GSAP or Web Animations.

+

Let’s create the same animation as before, but this time using GSAP. More specifically we’ll use TweenLite, a lightweight animation tool that serves as the foundation of GSAP.

+
var button = document.querySelector('button');
+
+button.addEventListener('click', () => {
+  TweenLite.to('div', 0.6, {
+    left: 200
+  });
+});
+
+

Note that the code above requires a plugin called CSSPlugin. This plugin allows us to animate almost any CSS property.

+
+

When working with frontend frameworks like Angular it could be a whole new story as some of them handle animations differently.

+

That doesn’t mean it’s not using some of the core concepts under the hood. They often come with their own animation system. Take Angular’s animation system for example, that is built on top of the Web Animations API (WAAPI). It’s fairly new and currently being implemented in Chrome and Firefox. It’s goal is to unite CSS, JS and SVG. More specifically, it aims to provide the power of CSS performance, add the benefits and flexibility of JS and SVG as well as leave the fundamental work to the browser without the need for additional dependencies.

+

If you want to learn more about the Web Animation API, check out this series of posts which goes a lot more into detail while covering advanced features like running multiple animations in parallel or in sequence, animating elements along a motion path or controlling animations using the AnimationPlayer.

+

Here is a snippet showing the WAAPI in action:

+
var button = document.querySelector('button');
+
+var wrapper = document.querySelector('div');
+wrapper.style.position = 'relative';
+
+button.addEventListener('click', () => {
+  wrapper.animate([
+    { left: getComputedStyle(elem).left },
+    { left: '200px' }
+  ], { duration: 600, /* and more like easing, delay etc. */ });
+});
+

Remember, the WAAPI is still a work in progress and things like additive animations are not fully supported yet. That’s why we use getComputedStyle() to calculate the very first KeyframeEffect. A KeyframeEffect is used to specify the values for the properties we’d like to animate. Each effect represents one keyframe and the values are basically interpolated over time. In other words, the array is a collection of keyframes. Here is an equivalent CSS keyframe animation:

+
@keyframes moveToRight {
+  from {
+    left: 0px;
+  }
+  to {
+    left: 200px;
+  }
+}
+
+div {
+  position: relative;
+  animation: moveToRight 600ms forwards;
+}
+

Similar to the WAAPI, we also need to set the initial value when animating the left property. This is not needed if we were translating the element on the X-axis to another location via its transform property. With CSS keyframe animations we normally define when the change will happen with either percentage values or the keywords from and to, which are the same as 0% and 100%.

+

We do this with the WAAPI by defining an offset for each set of property values (keyframe). Keyframes without any offset will have offsets computed, e.g. the first keyframe has an offset of 0, the last will be 1.

+

So far, we have seen a little bit of CSS and JavaScript animations and how to leverage them to animate parts of our application. In the next section we’ll look at at a specific case study implementing a real-world user profile animation. The goal is to implement the exact same animation both imperatively and declaratively using GSAP and Angular’s built-in animation system. Yes, there’s going to be a lot of Angular!

+

Case study: An animated modal-based user profile

+

Enough about theory! Let’s build a simple modal-based user profile and apply animations to improve the user experience and draw focused-attention to the dialog.

+

Here’s a preview of what we are going to build:

+

animation preview

+

Our application is going to be very simple and mainly consists of two components:

+
    +
  • DashboardComponent
  • +
  • ProfileDetailsComponent
  • +
+

The DashboardComponent is the entry point (root component) of our application. It contains almost no logic and merely represents a wrapper composing the ProfileDetailsComponent.

+

Inside the DashboardComponent we initialize the data for the user profile and toggle the visibility of the dialog. An ngIf will then show and hide the template. This is important for our animation because we use it as a trigger.

+

Here is the template of the DashboardComponent:

+
<div>
+  <header>
+    <span class="title">Dashboard</span>
+    <div class="image-container" (click)="toggleProfileDetails()" data-tooltip="Profile" >
+      <img class="profile-button" src="..." />
+    </div>
+  </header>
+
+  <profile-details [user]="user" *ngIf="showProfileDetails"></profile-details>
+</div>
+

So far so good. Let’s look at the template of ProfileDetailsComponent:

+
<div class="wrapper">
+  <header>
+    <div class="profile-image-wrapper">
+      <div class="profile-image-border"></div>
+      <img class="profile-image" src="..." />
+    </div>
+    <div class="profile-header-content">
+      <span class="username">{{ user.name }}</span>
+      <span class="username-title">{{ user.title }}</span>
+    </div>
+  </header>
+  <main>
+    <ul class="stats">
+      <li class="stats-item">
+        <span class="stats-icon icon-eye"></span>
+        <span>{{ user.views }}</span>
+      </li>
+      <li class="stats-item">
+        <span class="stats-icon icon-location"></span>
+        <span>{{ user.location }}</span>
+      </li>
+      <li class="stats-item">
+        <span class="stats-icon icon-heart"></span>
+        <span>{{ user.hearts }}</span>
+      </li>
+    </ul>
+  </main>
+</div>
+

To achieve the desired animation we need to set some initial CSS properties to enable 3D-space for the children elements inside ProfileDetailsComponent. We do that by setting the perspective on the host element. CSS host selectors are a great way to apply styles without introducing an additional wrapper element.

+

Nonetheless, for our animation we still need a wrapper element because the perspective property doesn’t affect how the host element is rendered; it simply enables 3D-space for children elements.

+
:host {
+  perspective: 500px;
+  ...
+}
+
+.wrapper {
+  transform-origin: top center;
+  ...
+}
+

Again, the perspective only affects children elements and only those that are transformed in a three-dimensional space, e.g. rotation about X-axis or translation along Z-axis. The value for the perspective determines the strength of the 3D-effect. In other words, it describes the distance between the object and the viewer. Therefore, if the value is very small the effect will be quite impressive as we are extremely close to the object. On the other hand, if the value is high the distance between the object and the viewer will be large and therefore the animation looks rather subtle. That said, we need to set the perspective property in order to achieve 3D-effects.

+

In addition, we have to specify a point of origin for our upcoming transformation. By default the point of origin is exactly the center of any element. The rest is just simple styling for the dialog.

+

Alright. Now that we got this in place, let’s implement our animation imperatively using GreenSocks’s timeline feature.

+

Imperative implementation using GreenSock

+

In order to achieve this animation with GSAP we need to use its timeline feature. Make sure to either use TweenMax or add TimelineLite separately to your project.

+

A timeline is basically a container where we place tweens over the course of time. Tweening is the process of generating intermediate frames between two states. With GSAP’s timeline we can easily build sequences of animations and animate an element .to() or .from() a certain state. In addition, we get a lot of control over our animations. As such, we can stop, pause, resume, or even reverse them. Here is a simple example:

+
window.onload = function () {
+  var timeline = new TimelineLite();
+
+  var h1 = document.getElementById('first');
+
+  timeline
+    .add('start')
+    .from(h1, 0.7, { opacity: 0, ease: Power2.easeIn }, 'start')
+    .from(h1, 1, { x: 200, ease: Bounce.easeOut }, 'start')
+    .to('#second', 0.3, { backgroundColor: '#ffeb3b' })
+    .to('#third', 0.3, { x: 200, repeat: 1, yoyo: true }, '-=0.3')
+    .play();
+
+  var button = document.getElementsByTagName('button');
+
+  button[0].addEventListener('click', function() {
+    timeline.restart();
+  });
+}
+

With TimelineLite we have complete control over where tweens are placed on the timeline and they can overlap as much as we want. Notice how we use .add() to add a label to the timeline. We can use labels to start multiple animations at the same time. For instance, we use this mechanism to run two animations in parallel. The h1 will fade and translate in at the same time. Both animations could easily be combined in a single animation, but they have different easing functions. It solely demonstrates how to use labels.

+

Let’s see how we can do that in our Angular application. First off, we get all the elements using Angular’s built-in @ViewChild() and @ViewChildren() decorators. We leverage those to query specific elements within the view of a component.

+

@ViewChild() returns an ElementRef, whereas @ViewChildren() returns a QueryList. Essentially it’s an object that stores a list of elements and implements the iterable interface. This makes it possible to be used in combination with ngFor. The cool thing is that it’s based on Observables. This means we can subscribe to changes and get notified whenever an element is added, removed, or moved.

+

For more information check out Minko’s blog post about the difference between view children and content children in Angular.

+

Here’s how we use it in our Angular application to grab the elements we need:

+
@ViewChild('wrapper') wrapper: ElementRef;
+@ViewChild('main') main: ElementRef;
+...
+

The decorator takes either a type or a template reference variable. In most cases, such template reference variable is a reference to a DOM element inside a component’s template. The following snippet shows how we’d get a reference to the wrapper element:

+
<div class="wrapper" #wrapper>
+...
+</div>
+

See the #wrapper? That’s how we declare a local template reference for that specific element. We do this for all the elements we need for the animation. With that in place, we can instantiate our timeline.

+

Usually we use ngOnInit to implement our initialization logic. However, this is a little bit too early in a component’s lifecycle because we have to wait for the component to be fully initialized in order to use the DOM elements we collected. There’s a lifecycle hook called ngAfterViewInit which is the perfect moment in a component’s initialization process in which we have everything we need to set up the timeline.

+
ngAfterViewInit() {
+  this.timeline = new TimelineLite();
+  ...
+}
+

Cool! But before we can construct the timeline for our profile animation there’s one thing left to do. We need to apply an initial transformation to the wrapper element via CSS in order to achieve the fancy 3D-effect:

+
.wrapper {
+  transform: rotateX(-90deg) translateY(150px) translateZ(50px);
+  ...
+}
+

We can now apply the concepts we learned to build the timeline:

+
this.timeline
+  .add('start')
+  .from(this.wrapper.nativeElement, .15, { opacity: 0 }, 'start')
+  .to(this.wrapper.nativeElement, .3, { rotationX: 0, y: 0, z: 0,  ease: Power3.easeIn}, 'start')
+  .add('image', '-=0.1')
+  .add('main', '-=0.15')
+  .add('icons', '-=0.1')
+  .add('text', '-=0.05')
+  .from(this.profileImageBorder.nativeElement, .3, { scale: 0 }, 'image')
+  .from(this.profileImage.nativeElement, .3, { scale: 0, delay: .05 }, 'image')
+  .from(this.main.nativeElement, .4, { y: '100%' }, 'main')
+  .staggerFrom([this.username.nativeElement, this.title.nativeElement], .3, { opacity: 0, left: 50 }, 0.1, 'image')
+  .staggerFrom(this.statsIcons, .3, { opacity: 0, top: 10 }, 0.1, 'icons')
+  .staggerFrom(this.statsTexts, .3, { opacity: 0 }, 0.1, 'text')
+  .play();
+

Woah! This looks pretty overwhelming at first glance. But all we have to do is to orchestrate our animation using GreenSock’s timeline API. With the help of labels we can run multiple animations in parallel and precisely control the timing of certain animations.

+

One thing we haven’t talked about so far is .staggerFrom(). A stagger is an animation that contains a delay between each successive animation.

+

The whole animation can be illustrated as follows:

+

+ + + animation timeline + +

+

Here’s the full-fledge solution. Take a look and fiddle with it.

+ +

Declarative implementation using Angular Animations

+

In the previous section we have seen how to implement the profile animation with GreenSock in an imperative way. There are some drawbacks to that solution. First, it’s quite some boilerplate and work to collect all the elements and manually set up the timeline. That said, an animation platform like GSAP requires the DOM to be ready. A framework can make much more assumptions about the instructions (animation data) and the environment (app and browser) before animating things. Second, it can be very beneficial if there’s a framework backing the animation engine, like with Angular. GSAP and other animation libraries cannot easily handle DOM insertions or removals because they do not own the DOM transactions. Angular on the other hand has full control over the DOM.

+

If you are completely new to animations with Angular, check out this post by Thomas Burleson. It covers the fundamentals and shows an example of a more complex fade animation.

+

Alright, let’s refactor our profile animation using the latest animation features introduced with Angular 4.2. To get started, we first have to import the BrowserAnimationsModule from @angular/platform-browser/animations and add it to the imports of our application:

+
@NgModule({
+  imports: [
+    BrowserAnimationsModule
+    ...
+  ],
+  ...
+})
+export class DashboardModule {}
+

Remember, animations in Angular are based on the WAAPI and work in browsers that support it including Chrome and Firefox. However, currently Internet Explorer and Safari do not. In this case a polyfill is required to achieve similar results. Once animations are properly imported and enabled, we can go ahead and start defining the profile animation.

+

To quickly recap, animations are declared with the animations metadata property within the @Component() decorator. Each animation is defined by a trigger which takes a name and a list of state and transition entries. Angular’s animation engine works basically like a state machine. That’s right. This should sound familiar. We have seen it in the beginning of this post, remember? The first argument of transition allows you to specify a direction from one state to another, also known as state-change-expression. Here are some common values:

+
    +
  • * => * captures a state change between any states
  • +
  • void => * captures the entering of elements
  • +
  • * => void captures the leaving of elements
  • +
+

The last two are so common that they have their own aliases:

+
    +
  • :enter for void => *
  • +
  • :leave for * => void
  • +
+

Angular 4.2 introduces several new animation features and extends Angular’s animation DSL. Here’s a quick overview of what’s new:

+
    +
  • query() can be used to find one or more elements within the element that’s being animated
  • +
  • stagger() animates a bunch of elements with a delay in between each animation
  • +
  • group() specifies a list of animations that are run in parallel
  • +
  • sequence() specifies a list of animations that are run one at a time
  • +
  • animation() can be used to create reusable animations with input parameters
  • +
  • useAnimation() invokes reusable animations created with animation()
  • +
  • animateChild() will invoke child animations which are normally blocked
  • +
+

Neat! Let’s use that to re-implement our profile animation with Angular’s animation DSL. In order to demonstrate most of the above animation helpers, especially animateChild(), we need to refactor our application a bit.

+

First of all, we create a new component called ProfileStatsComponent which now contains the ul that was previously part of the DashboardComponent. The template of the DashboardComponent now looks like this:

+
<div class="wrapper">
+  <header>
+    <div class="profile-image-wrapper">
+      <div class="profile-image-border"></div>
+      <img class="profile-image" src="https://api.adorable.io/avatars/90/me@you.com.png" />
+    </div>
+    <div class="profile-header-content">
+      <span class="username">{{ user.name }}</span>
+        <span class="username-title">{{ user.title }}</span>
+    </div>
+  </header>
+  <main>
+    <profile-stats [user]="user"></profile-stats>
+  </main>
+</div>
+

The dashboard now composes the ProfileStatsComponent which will later define its own animation. For now, let’s focus on the profile animation and talk about child animations in a minute.

+

Here’s how we define our profileAnimation:

+
animations: [
+  trigger('profileAnimation', [
+    transition(':enter', group([
+      ...
+    ]))
+  ])
+]
+

Within our profileAnimation we define one transition and on :enter (when the dialog enters the DOM) we run several animations in parallel. Next, we use query() to grab the DOM elements we need for our animation and set some initial styles using the styles helper:

+
animations: [
+  trigger('profileAnimation', [
+    transition(':enter', group([
+      query('.wrapper', style({ opacity: 0, transform: 'rotateX(-90deg) translateY(150px) translateZ(50px)' })),
+      query('.profile-image-border, .profile-image', style({ transform: 'scale(0)' })),
+      query('.username, .username-title', style({ opacity: 0, transform: 'translateX(50px)' })),
+      query('main', style({ transform: 'translateY(100%)' }))
+    ]))
+  ])
+]
+

Remember how we collected the DOM elements using @ViewChild() and @ViewChildren()? We don’t need to do that anymore. Plus, we can get rid of all the local template references because that is now handled by query(). Quite powerful, huh?

+

Before we implement the profile animation, let’s create a reusable fade animation that we can use elsewhere in different places with full input parameter support:

+
export const fadeAnimation = animation([
+  animate('{{ duration }}', style({ opacity: '{{ to }}' }))
+], { params: { duration: '1s', to: 1 }});
+

The fadeAnimation can now be imported into our application, adjusted via input parameter and invoked using useAnimation(). The values we specified for the input parameters are default values.

+

Once we have that in place, let’s add the missing pieces to our animation:

+
animations: [
+  trigger('profileAnimation', [
+    transition(':enter', group([
+      // Initial Styles
+      ...
+
+      query('.wrapper', group([
+        useAnimation(fadeAnimation, {
+          params: {
+            duration: '150ms',
+            to: 1
+          }
+        }),
+        animate('300ms cubic-bezier(0.68, 0, 0.68, 0.19)', style({ transform: 'matrix(1, 0, 0, 1, 0, 0)' }))
+      ])),
+
+      query('.profile-image-border', [
+        animate('200ms 250ms ease-out', style('*'))
+      ]),
+
+      query('.profile-image', [
+        animate('200ms 300ms ease-out', style('*'))
+      ]),
+
+      query('.username, .username-title', stagger('100ms', [
+        animate('200ms 250ms ease-out', style('*'))
+      ])),
+
+      query('main', [
+        animate('200ms 250ms ease-out', style('*'))
+      ])
+
+      ...
+    ]))
+  ])
+]
+

In the code above, we query a bunch of elements and use several animation helpers to achieve the desired effect. All animations will run in parallel because they are defined within a group(). Also, there are no “labels” or a similar feature to what GreenSock provides with .add(). Turns out, Angular has no timeline support yet and we need to fiddle with delays in order to orchestrate the animation.

+

If we take a closer look we can see that there’s actually more to it. For instance for the wrapper, we run two animations in parallel one of which is the reusable animation we defined earlier. We can invoke a reusable animation with the useAnimation() method. While AnimationOptions are optional, we specify them to override the default input paramters.

+

Furthermore, we can spot this special style property of style('*'). This will basically remove all of the special styling we have added (e.g. initial styles) and reset the state of the element. It’s an equivalent of setting each value to *. This means that Angular will figure out the values at runtime. On top of that use the stagger() animation helper method to animate multiple elements with a time gap in between each animated element.

+

Applying animations using @HostBinding()

+

Ok, but how do we use the animation? For that we can either attach the trigger to the element within the component’s template or use a @HostBinding(). In our case, we use the @HostBinding() because we want to attach the trigger to the host element:

+
export class ProfileStatsComponent {
+  ...
+
+  @HostBinding('@profileAnimation')
+  public animateProfile = true;
+
+  ...
+}
+

Understanding child animations

+

In a real-world scenario you most likely end up having multiple components and animations on different levels, e.g. parent or child animations. Turns out that parent animations will always get priority and any child animation will be blocked. That’s a shame. But don’t bury your head in the sand yet because Angular got you covered! We can query inner elements and use animateChild() to allow child animations to run. The cool thing is we can do that at any point in the animation sequence within a defined transition.

+

In our example, we created a component called ProfileStatsComponent. Let’s see this in action and start off by creating a child animation for this component using everything we know by now:

+
animations: [
+  trigger('statsAnimation', [
+    transition('* => *', group([
+      query('.stats-icon', style({ opacity: 0, transform: 'scale(0.8) translateY(10px)' })),
+      query('.stats-text', style({ opacity: 0 })),
+
+      query('.stats-icon', stagger('100ms', [
+        animate('200ms 250ms ease-out', style('*'))
+      ])),
+
+      query('.stats-text', stagger('100ms', [
+        animate('200ms 250ms ease-out', style('*'))
+      ])),
+    ])
+  ])
+]
+

Easy, right? Now we can go ahead and use the animateChild() helper as mentioned earlier in our profileAnimation:

+
animations: [
+  trigger('profileAnimation', [
+    transition(':enter', group([
+      // Initial Styles
+      ...
+
+      // Animation
+      ...
+      query('profile-stats', animateChild())
+    ]))
+  ])
+]
+

That’s it. We fully re-implemented the profile animation using Angular’s built-in animation system. It’s very intuitive, easy to use and declarative.

+

Here’s the demo. Try it out and fiddle with it!

+ +

If you want to find out more about the new animation features in Angular 4.2+, check out this excellent post by Matias Niemela.

+

Special Thanks

+

Kudos to Matias Niemelä for the amazing work on the Angular Animation system!

Written by  Author

Dominic Elm

\ No newline at end of file diff --git a/angular/2017/11/13/easy-dialogs-with-angular-material.html b/angular/2017/11/13/easy-dialogs-with-angular-material.html new file mode 100644 index 000000000..ab7c44ca4 --- /dev/null +++ b/angular/2017/11/13/easy-dialogs-with-angular-material.html @@ -0,0 +1,276 @@ +Easy Dialogs with Angular Material | Articles by thoughtram

Angular

Easy Dialogs with Angular Material

Angular comes with a dedicated UI library that implements Google’s Material Design with Angular components - Angular Material. The idea is to have a consistent design language across all apps that not only looks and feels good, but also comes with a great user experience and built-in accessibility. This turns out to be very powerful as we as consumers of the platform get things like custom theming and high-quality components for free.

+

One component that’s often needed in different applications but not trivial to implement, is a dialog or modal. Modals need to magically create an overlay and somehow position themselves correctly so that they are always exactly in the middle of the screen. In addition to that, we often want to react to when a user interacts with a modal. Whether they are pressing a button that will close the modal, or simply hitting the escape key to dismiss it right away.

+

In this article we’ll take a look at how to leverage Angular Material’s built-in MatDialog to create easy to maintain dialogs and modals in our apps!

+

Motivation

+

The best way to learn a new skill is when we have a concrete use case for the thing we want to learn. For the sake of this article, we’ll use a scenario that has actually seen the light of a real world production app.

+

You may or may not have heard that we’re working on MachineLabs, a platform to run Machine Learning experiments in the browser. One essential UI component there is the file tree of the in-browser editor. In fact, we touched on that component in our article on custom theming. Users can create files and folders, edit them and delete them. Every time a user adds or edits a file or folder, a dialog pops open where users have a chance to decide on a name for the thing they edit or create.

+

+

This is a great scenario to learn about Angular Material’s dialog as we not only get to learn how to create and close them, but also how to use the same dialog for different actions, feeding it with different data depending on a certain context.

+

Let’s get right to it!

+

Creating dialogs with MatDialog

+

Angular Material comes with a very easy to use API to create dialogs using the MatDialog service. In order to get hold of it we first need to import the MatDialogModule into our application’s NgModule like this:

+
import { NgModule } from '@angular/core';
+import { MatDialogModule } from '@angular/material';
+
+@NgModule({
+  ...
+  imports: [
+    ...
+    MatDialogModule
+  ]
+})
+export class AppModule {}
+

Remember that as of Angular Material 2.0.0-beta.10 MaterialModule is deprecated, which is why we’re importing MatDialogModule here right away. However, best practice is rather to create your own MaterialModule that imports and exports the needed material modules. We keep it simple here for the sake of this article.

+

Now that we’ve imported MatDialogModule we can start using its services and directives. Let’s take a look at where and when we want to create a dialog. As mentioned, the file tree allows users to add or edit files and folders. Too keep things simple, let’s start of with opening a dialog when the “add file” button is clicked.

+

Here’s what the template of the file tree could look like:

+
<ul>
+  <li *ngFor="file of files">
+    {{file.name}}
+  <li>
+</ul>
+<button (click)="openAddFileDialog()">Add file</button>
+

The corresponding component looks something like this (again, simplified):

+
@Component(...)
+export class FileTreeComponent {
+
+  openAddFileDialog() {
+
+  }
+}
+

Great. Now all we have to do is to use Angular Material’s MatDialog service to create a dialog. To do that we need to inject an instance of that service into our component and tell it what component type to use to create such a dialog using its open() method. Let’s say we create a component FileNameDialogComponent which takes care of showing an input control so users can enter a name of a new file.

+
import { MatDialog, MatDialogRef } from '@angular/material';
+import { FileNameDialogComponent } from '../file-name-dialog';
+
+@Component(...)
+export class FileTreeComponent {
+
+  fileNameDialogRef: MatDialogRef<FileNameDialogComponent>;
+
+  constructor(private dialog: MatDialog) {}
+
+  openAddFileDialog() {
+    this.fileNameDialogRef = this.dialog.open(FileNameDialogComponent);
+  }
+}
+

Another thing we need to do is to add FileNameDialogComponent to our application module’s entryComponents, since it’s dynamically created at runtime.

+
@NgModule({
+  ...
+  declarations: [
+    ...
+    FileNameDialogComponent
+  ],
+  imports: [
+    ...
+    MatDialogModule
+  ],
+  entryComponents: [FileNameDialogComponent]
+})
+export class AppModule {}
+

There are a couple of things to note here. We’re injecting MatDialog into our component and call its open() method with the FileNameDialogComponent type when a user wants to add a file. MatDialog#open() returns a MatDialogRef which is, as the name states, a reference to the now created dialog. Yeap, the dialog has already been created with just this little amount of code.

+

The dialog reference is important because it lets us react to when something with our dialog happens. Also, it turns out that we have access to the same reference inside the dialog itself, enabling us to control the dialog from there. We’ll see in a second when this is useful.

+

Let’s reward ourselves first and take a look at what we’ve already created:

+ +

Configuring dialogs

+

Every dialog created using MatDialog already comes with a decent default behaviour and default configuration. However, we can still tune things to our needs. This includes things like the width or height of the dialog. Or whether the dialog should have a backdrop or not. Dialog configuration can be easily passed as a second argument to MatDialog#open() as an object like this:

+
@Component(...)
+export class FileTreeComponent {
+  ...
+  openAddFileDialog() {
+    this.fileNameDialogRef = this.dialog.open(FileNameDialogComponent, {
+      hasBackdrop: false
+    });
+  }
+}
+

For a full list of configuration options checkout the dedicated API documentation.

+

Adding Material look & feel

+

Let’s take care of giving our dialog a more Material look & feel. This is obviously not a requirement as we’re free to style our dialogs the way we want, but for the sake of this article we stick with what Angular Material has to offer right out of the box.

+

To make our dialogs look more like they come straight our of Google’s offices, Angular Material comes with a couple of directives that we can use, which take care of adding a Material Design look & feel to our component. Those directives are:

+
    +
  • [mat-dialog-title] - Renders a nice looking dialog title in Material Design
  • +
  • mat-dialog-content - Takes care of rendering a consistent dialog content area for things like texts, forms etc.
  • +
  • mat-dialog-actions - Good for action elements like buttons to confirm or close a dialog
  • +
+

Alright, that’s use those in our FileNameDialogComponent:

+
@Component({
+  template: `
+    <h1 mat-dialog-title>Add file</h1>
+    <mat-dialog-content>
+      Content goes here
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button>Add</button>
+      <button mat-button>Cancel</button>
+    </mat-dialog-actions>
+  `
+})
+export class FileNameDialogComponent {}
+

Okay, this already looks much better. Next we take care of accessing data returned by a dialog.

+ +

Returning data from dialogs

+

Now that our dialog looks good as well, we need to find a way to let the user enter a file name and once confirmed, taking that name and create a new file object in our application. As mentioned earlier, each MatDialogRef gives us APIs to react to events emitted by a dialog. When a dialog gets closed, either by hitting the escape key or by closing it using APIs, the afterClosed() Observable emits. From within the dialog, we can control if and what gets emitted by the dialog when it’s closed, giving us all the tools we need complete implementing this feature.

+

Let’s first take care of emitting the file name entered by the user after closing the dialog. To do that we create a small form within our FileNameDialogComponent which will close the dialog once it’s submitted.

+
import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material';
+import { FormGroup, FormBuilder } from '@angular/forms';
+
+@Component({
+  templateUrl: './file-name-dialog.component'
+})
+export class FileNameDialogComponent {
+
+  form: FormGroup;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private dialogRef: MatDialogRef<FileNameDialogComponent>
+  ) {}
+
+  ngOnInit() {
+    this.form = this.formBuilder.group({
+      filename: ''
+    })
+  }
+
+  submit(form) {
+    this.dialogRef.close(`${form.value.filename}`);
+  }
+}
+

Notice how we inject MatDialogRef<FileNameDialogComponent>. Yes, this is exactly the same reference we have access to from within our FileTreeComponent. MatDialogRef has a method close() which will essentially close the dialog. Any data that is passed to that method call will be emitted in its afterClosed() stream. Since the template got a little bigger now, we’ve extracted it into its own template file.

+

Here’s what it looks like:

+
<form [formGroup]="form" (ngSubmit)="submit(form)">
+  <h1 mat-dialog-title>Add file</h1>
+  <mat-dialog-content>
+    <mat-form-field>
+      <input matInput formControlName="filename" placeholder="Enter filename">
+    </mat-form-field>
+  </mat-dialog-content>
+  <mat-dialog-actions>
+    <button mat-button type="submit">Add</button>
+    <button mat-button type="button" mat-dialog-close>Cancel</button>
+  </mat-dialog-actions>
+</form>
+

One thing to point out here is that we use the mat-dialog-close directive, which is kind of the equivalent to (click)="dialogRef.close()", just that we don’t have to type it out every time. If all these forms APIs are new to you, we recommend checkout out our articles on Template-driven Forms and Reactive Forms in Angular.

+

Great! Now that our dialog emits the entered file name, we can access it from within FileTreeComponent and create a new file object. In order to do that, we subscribe to fileNameDialogRef.afterClosed(). We also need to make sure that we only perform our file object creation when the emittion has an actual value and isn’t an empty string. This can be done easily by using Reactive Extensions and its filter operator (obviously we should add some validation for that but let’s not get distracted too much here).

+
...
+import { filter } from 'rxjs/operators';
+
+@Component(...)
+export class FileTreeComponent {
+  ...
+  openAddFileDialog() {
+    this.fileNameDialogRef = this.dialog.open(FileNameDialogComponent, {
+      hasBackdrop: false
+    });
+
+    this.fileNameDialogRef
+        .afterClosed()
+        .pipe(filter(name => name))
+        .subscribe(name => this.files.push({ name, content: '' }));
+  }
+}
+

That’s it! We can now add new files to our file tree via our brand new dialog. As mentioned earlier, we would also obviously take care of some validation, such as not allowing the user to submit the form when no file name has been entered. Another thing we might want to ensure is that no duplicated files can be created. However, this is out of the scope of this article.

+

Here’s our app in action:

+ +

Sharing data with dialogs

+

There’s one more thing we need to implement to make our dialog also work for scenarios where users want to edit an existing file name - sharing data between dialogs. When users edit a file name, we most likely want to simply reuse the same dialog we’ve just created because it’s essentially exactly the same form with the same rules and same behaviour, just that it should be pre-filled with the name of the file to edit. In other words, we need to find a way to pass data to the dialog that’s going to be opened.

+

Luckily, this is quite easy because Angular Material got us covered! It turns out that we can pass any data we want to a dialog using its configuration when the dialog is created. All we have to do is to attach the data we need inside the dialog to the configuration’s data property.

+

Since we want to use the same dialog for both actions, let’s also rename openAddFileDialog() to openFileDialog() and give it an optional file parameter. Here’s what that would look like:

+
<ul>
+  <li *ngFor="file of files">
+    {{file.name}}
+    <button (click)="openFileDialog(file)">Edit file</button>
+  <li>
+</ul>
+<button (click)="openFileDialog()">Add file</button>
+

Now, we also need to check inside our component whether a file has been passed to that method or not, and pass it on to the dialog like this:

+
@Component(...)
+export class FileTreeComponent {
+  ...
+  openFileDialog(file?) {
+    this.fileNameDialogRef = this.dialog.open(FileNameDialogComponent, {
+      hasBackdrop: false,
+      data: {
+        filename: file ? file.name : ''
+      }
+    });
+    ...
+  }
+}
+

All we need to do now is taking this data in our dialog and pre-fill our the form control accordingly. We can inject any data that is passed like that using the MAT_DIALOG_DATA injection token.

+
import { Component, OnInit, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+
+@Component({
+  templateUrl: './file-name-dialog.component'
+})
+export class FileNameDialogComponent implements OnInit {
+
+  form: FormGroup;
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private dialogRef: MatDialogRef<FileNameDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) private data
+  ) {}
+
+  ngOnInit() {
+    this.form = this.formBuilder.group({
+      filename: this.data ? this.data.name : ''
+    })
+  }
+}
+

Last but not least we need to ensure that when the dialog is closed, we update the file that has been edited instead of adding a new one. We keep it simple and just look for the index of the file that’s being edited and replace it with the updated one.

+
...
+
+@Component(...)
+export class FileTreeComponent {
+  ...
+  openAddFileDialog(file?) {
+    ...
+
+    this.fileNameDialogRef.afterClosed().pipe(
+      filter(name => name)
+    ).subscribe(name => {
+      if (file) {
+        const index = this.files.findIndex(f => f.name == file.name);
+        if (index !== -1) {
+          this.files[index] = { name, content: file.content }
+        }
+      } else {
+        this.files.push({ name, content: ''});
+      }
+    });
+  }
+}
+

Again, this is a trivial file tree implementation. In a real-world app we probably want to take care of having nested directories as well, which changes the level of complexity dramatically here. However, since this article is really all about how easy it is to create dialogs using Angular Material, we stick with what we have.

+ +

Where to go from here

+

This was it! Even though something like creating dialogs is usually rather tricky, with a UI library like Angular Material, this task really becomes a breeze. In fact, over at MachineLabs we have created several dialogs in different places of the application because it’s such an easy thing to do with given tools at hand.

+

Where do we go from here? Using the built-in dialog APIs, we get pretty far and only more sophisticated scenarios require a bit more brain work. For example, one thing we also did at MachineLabs was creating our own custom overlay so we could create Google Drive-like file preview.

+

In our next article we’ll explore how to create custom overlays and overlay services using the Angular Component Development Kit!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angular/2017/11/20/custom-overlays-with-angulars-cdk.html b/angular/2017/11/20/custom-overlays-with-angulars-cdk.html new file mode 100644 index 000000000..d95041633 --- /dev/null +++ b/angular/2017/11/20/custom-overlays-with-angulars-cdk.html @@ -0,0 +1,380 @@ +Custom Overlays with Angular's CDK | Articles by thoughtram

Angular

Custom Overlays with Angular's CDK

You have probably heared of Angular Material haven’t you? If you haven’t, it’s a library that provides you with high-quality Material Design components for Angular. Material Design itself is a visual design language that aims for consistency of user experience across all platforms and device sizes. That’s cool but what if your company has its own opinions about styles and the overall look and feel of the UI? How do we get the best of Angular Material without adopting the Material Design visual language?

+

Tada 🎉! That’s where Angular Material’s Component Dev Kit (CDK for short) comes into play. The CDK provides us with tools to build awesome and high-quality Angular components without adopting the Material Design visual language. Its goal is to make our life as developers easier and extract common behaviors and patterns shared between multiple Angular Material components. For instance, the datepicker, snackbar, or tooltip have something in common; they need to dynamically open up some floating panel on the screen. But that’s just the tip of the ice berg. There are many different packages for all sorts of things such as a11y that helps us improve the accessibility of our UI components. There’s even a layout package with utilities to build responsive UIs that react to screen-size changes. For a more complete list, please check out the official documentation.

+

Over at MachineLabs, we thought it would be useful to provide a way to preview generated output files (mostly images), so users don’t have to download it every single time just to take a quick look. So we sat down to build a Google Drive like overlay with the CDK. This post is meant to share our knowledge with the community and to make you comfortable using the CDK for your own purposes.

+

In this post, we’ll use the CDK to build a Google Drive-like custom overlay that looks and feels much like the one built for MachineLabs. Here’s how it looks like:

+

overlay preview

+

The building blocks

+

Let’s start simple and work our way up to the final, fully-fledged solution which will have a very similar API as the MatDialog service provided by Angular Material. It’s not important to know exactly how the MatDialog works but it’s definitely helpful. If this is new to you, we recommend to check out our post on Easy Dialogs with Angular Material.

+

Our solution will be a little less flexible but specifically made for showing a file preview inspired by Google Drive. That said, we’d like to have a nice toolbar at the top and the image being rendered in the middle of the screen.

+

In general, the MatDialog is great for showing content in a dialog box but as soon as we want a little bit of a custom look and feel, something that does not look like a white box with content inside, we would need to roll our own overlay. Luckily, we can use the overlay package from the CDK that has most of the core logic for opening floating panels already baked in. More on that in just a second.

+

Here are the core building blocks of our application:

+

+ + + custom overlays architecture + +

+

As we can see, we have two components, one service and a class that represents a remote control to an opened overlay. The AppComponent is the root (or entry point) of our application. This component contains a toolbar and the list of files that we can preview. In addition, it has access to a FilePreviewOverlayService which provides us with the core logic for opening an overlay. At the same time it’s an abstraction for some “heavy” lifting that should be implemented in a resuable manner. Don’t be scared, it’s not going to be super heavy and we’ll break it down into comprehensible chunks. Last but not least, there’s a FilePreviewOverlayRef which, as mentioned, is a handle used to control (e.g. close) a particular overlay.

+

For the overlay we choose to render a component, so we can attach some logic and also add animations to our overlay to engage our users and make them happy. We call this component FilePreviewOverlayComponent.

+

That’s about it. Now that we have the basic structure in place, we’re ready to look at some code.

+

Note that this post is the first part out of two in which we lay the foundation for our custom overlay. We’ll build on top of this in the next part and add keyboard support, image preloading and animations.

+

Setup

+

Before we can start implementing the custom overlay we need to install the CDK. Simply run npm install @angular/cdk and we’re all set!

+

Our first overlay

+

From the MatDialog we know that when we open an overlay we must specify a component type that is then created dynamically at runtime. This means it is not created by using the component tags inside an HTML template. Also, we know that whenever a component is created at runtime, we must add it to our application module’s entryComponents.

+

Let’s do that and add the FilePreviewOverlayComponent to the array of entryComponents. In addition, we need to add the OverlayModule to the imports list of the root AppModule:

+
import { OverlayModule } from '@angular/cdk/overlay';
+...
+
+@NgModule({
+  imports: [ ... ],
+  declarations: [ ..., FilePreviewOverlayComponent ],
+  bootstrap: [ AppComponent ],
+  providers: [ ... ],
+  entryComponents: [
+    // Needs to be added here because otherwise we can't
+    // dynamically render this component at runtime
+    FilePreviewOverlayComponent
+  ]
+})
+export class AppModule { }
+

From there, creating an overlay is easy. First, we inject the Overlay service. This service has a create() function that we need to call in order to create a PortalHost for our FilePreviewOverlayComponent. Finally we need to create a ComponentPortal from this component and attach it to the PortalHost. Wait, what? Let’s give it a moment and look at some code before taking it apart:

+
@Injectable()
+export class FilePreviewOverlayService {
+
+  // Inject overlay service
+  constructor(private overlay: Overlay) { }
+
+  open() {
+    // Returns an OverlayRef (which is a PortalHost)
+    const overlayRef = this.overlay.create();
+
+    // Create ComponentPortal that can be attached to a PortalHost
+    const filePreviewPortal = new ComponentPortal(FilePreviewOverlayComponent);
+
+    // Attach ComponentPortal to PortalHost
+    overlayRef.attach(filePreviewPortal);
+  }
+}
+

The first step is to create a PortalHost. We do that by calling create() on the Overlay service. This will return an OverlayRef instance which is basically a remote control for the overlay. One unique attribute of this OverlayRef is that it’s a PortalHost, and once created, we can attach or detach Portals. We can think of a PortalHost as a placeholder for a component or template. So in our scenario, we are creating a ComponentPortal that takes a component type as its fist argument. In order to actually display this component we need to attach the portal to the host.

+
+

Ok, but where does the overlay get rendered?

+
+

Good question. There’s an OverlayContainer service which creates a container div under the hood that gets appended to the body of the HTML Document. There are a few more wrapper elements created but our component eventually ends up in a div with a class of cdk-overlay-pane. Here’s what the DOM structure looks like:

+
<div class="cdk-overlay-container">
+  <div id="cdk-overlay-0" class="cdk-overlay-pane" dir="ltr">
+    <!-- Component goes here -->
+  </div>
+</div>
+

Done. That’s all we need to create our very first custom overlay using the CDK. Let’s try it out and see what we got so far:

+ +

Our service only exposes one public method open() that will take care of creating a custom overlay. For now, the service is quite simple but it gets more complicated as we implement a more sophisticated and complete (functional-wise) overlay. Therefore it’s a good idea to extract the common logic into a service to stay DRY. Imagine we would have the same logic defined in each component we want to show an overlay. No good, right?

+

Now that we have layed the foundation for our custom overlay, let’s take it one step further and improve on what we have so far. Let’s add a backdrop and specify a scroll and position strategy. Don’t worry if it’s unclear what scroll and position strategy is all about. We’ll cover that in a second.

+

Configuring the overlay

+

When creating an overlay, we can pass an optional configuration object to create() to set the desired options, e.g. whether it has backdrop, the position or scroll strategy, width, height and many more. Here’s an example:

+
// Example configuration
+overlay.create({
+  width: '400px',
+  height: '600px'
+});
+

First of all, we allow the consumer of our API to override certain options. Therefore, we update the signature for open() to also take a configuration object. In addition, we define an interface that describes the shape of the configuration from a consumer perspective:

+
// Each property can be overridden by the consumer
+interface FilePreviewDialogConfig {
+  panelClass?: string;
+  hasBackdrop?: boolean;
+  backdropClass?: string;
+}
+
+@Injectable()
+export class FilePreviewOverlayService {
+  open(config: FilePreviewDialogConfig = {}) {
+    ...
+  }
+}
+

Next, we define some initial values for the config, so that, by default, every overlay has a backdrop alongside a backdropClass and panelClass:

+
const DEFAULT_CONFIG: FilePreviewDialogConfig = {
+  hasBackdrop: true,
+  backdropClass: 'dark-backdrop',
+  panelClass: 'tm-file-preview-dialog-panel'
+}
+
+@Injectable()
+export class FilePreviewOverlayService {
+  ...
+}
+

With that in place, we can define a new method getOverlayConfig() which takes care of creating a new OverlayConfig for the custom overlay. Remember, it’s better to break down the logic into smaller parts instead of implementing everything in one giant function. This ensures better maintainability but also readability of our code.

+
@Injectable()
+export class FilePreviewOverlayService {
+
+  ...
+
+  private getOverlayConfig(config: FilePreviewDialogConfig): OverlayConfig {
+    const positionStrategy = this.overlay.position()
+      .global()
+      .centerHorizontally()
+      .centerVertically();
+
+    const overlayConfig = new OverlayConfig({
+      hasBackdrop: config.hasBackdrop,
+      backdropClass: config.backdropClass,
+      panelClass: config.panelClass,
+      scrollStrategy: this.overlay.scrollStrategies.block(),
+      positionStrategy
+    });
+
+    return overlayConfig;
+  }
+}
+

Our method is quite simple. It takes a FilePreviewDialogConfig and creates a new OverlayConfig with the values from the given configuration. However, there are two important things to mention. One is the scrollStrategy and the other one is the positionStrategy.

+

Scroll strategy

+

The scroll strategy is a way of defining how our overlay should behave if the user scrolls while the overlay is open. There are several strategies available as part of the CDK, such as

+
    +
  • NoopScrollStrategy: does nothing
  • +
  • CloseScrollStrategy: automatically closes the overlay when scrolling
  • +
  • BlockScrollStrategy: blocks page scrolling
  • +
  • RepositionScrollStrategy: will reposition the overlay element on scroll
  • +
+

For our file preview overlay, we are going to use the BlockScrollStrategy because we don’t want the user to be scrolling in the background while the overlay is open.

+

The scrollStrategy takes a function that returns a scroll strategy. All strategies are provided by the Overlay service and can be accessed via the scrollStrategies property:

+
const overlayConfig = new OverlayConfig({
+  ...
+  // Other strategies are .noop(), .reposition(), or .close()
+  scrollStrategy: this.overlay.scrollStrategies.block()
+});
+

If we don’t specify a strategy explicitly, all overlays will use the NoopScrollStrategy.

+

Position strategy

+

The position strategy allows us to configure how our overlay is positioned on the screen. There are two position strategies available as part of the CDK:

+
    +
  • GlobalPositionStrategy: used for overlays that need to be positioned unrelated to other elements on the screen. This strategy is mostly used for modals or root-level notifications.
  • +
  • ConnectedPositionStrategy: used for overlays that are positioned relative to other elements. This is commonly used for menus or tooltips.
  • +
+

We’ll be using the GlobalPositionStrategy for our overlay because it’s supposed to be positioned globally on screen, unrelated to other elements.

+

Similar to the scrollStrategy we can access all position strategies through the Overlay service like so:

+
const positionStrategy = this.overlay.position()
+  .global()
+  .centerHorizontally()
+  .centerVertically();
+
+const overlayConfig = new OverlayConfig({
+  ...
+  positionStrategy
+});
+

With the configuration in place, we go ahead and define another method createOverlay() that hides the complexity of creating an overlay with a given configuration:

+
@Injectable()
+export class FilePreviewOverlayService {
+  ...
+  private createOverlay(config: FilePreviewDialogConfig) {
+    // Returns an OverlayConfig
+    const overlayConfig = this.getOverlayConfig(config);
+
+    // Returns an OverlayRef
+    return this.overlay.create(overlayConfig);
+  }
+}
+

We now refactor our open() method to generate a default config and utilize createOverlay():

+
export class FilePreviewOverlayService {
+  ...
+  open(config: FilePreviewDialogConfig = {}) {
+    // Override default configuration
+    const dialogConfig = { ...DEFAULT_CONFIG, ...config };
+
+    const overlayRef = this.createOverlay(dialogConfig);
+    ...
+  }
+}
+

Here’s what it looks like in action:

+ +

Our overlay looks much more like an overlay as we have imagined it in the beginning. The good thing is that most of the heavy lifting is taken care of by the CDK, such as dynamically creating a component, block page scrolling, or positioning.

+

So far, so good, but we are still missing some very fundamental functionality. We can open an overlay but what about closing it? This it not yet possible, so let’s go ahead and add this feature.

+

Closing overlays with a remote control

+

Just like we use remote controls to snap between television channels, we want a remote control to close our overlays. It will provide an API for modifying, closing, and listening to events on the overlay instance. Especially if we want to be able to close the dialog from within the overlay component, and optionally return a value to the consumer.

+

Our remote control will be a simple class that exposes only one public method - close(). For now we keep simple and extend it as we introduce more features. Here’s what it looks like:

+
import { OverlayRef } from '@angular/cdk/overlay';
+
+export class FilePreviewOverlayRef {
+
+  constructor(private overlayRef: OverlayRef) { }
+
+  close(): void {
+    this.overlayRef.dispose();
+  }
+}
+

When implementing the remote control, the only thing we have to make sure is that we need access to the OverlayRef. It’s a reference to the overlay (portal host) that allows us to detach the portal. Note that, there’s no @Injectable decorator attached to the class which means that we can’t leverage the DI system for this service. This, however, is no big deal because we will manually create an instance for every overlay and therefore we don’t need to register a provider either. Theoretically, we could open multiple overlays stacked on top of each other where each overlay has its own remote control. The DI system creates singletons by default. That’s not what we want in this case.

+

What’s left to do is to update our open() method to create a remote control and return it to the consumer of our API:

+
@Injectable()
+export class FilePreviewOverlayService {
+  ...
+  open(config: FilePreviewDialogConfig = {}) {
+    ...
+    const overlayRef = this.createOverlay(dialogConfig);
+
+    // Instantiate remote control
+    const dialogRef = new FilePreviewOverlayRef(overlayRef);
+    ...
+    // Return remote control
+    return dialogRef;
+  }
+

Notice how we pass in the overlayRef when creating a new FilePreviewOverlayRef? That’s how we get a hold of the PortalHost inside the remote. Instead of implementing a class that represents a reference to the open overlay, we could have returned the OverlayRef directly. However, it’s not a good idea to expose lower-level APIs because users could mess with the overlay and detach the backdrop for instance. Also, we need a little bit more logic later on when we introduce animations. A remote control is a good way of limiting the access to the underlying APIs and expose only those that we want to be publicly available.

+

From a consumer perspective we now get a handle to the overlay that allows us to programatically close it at some point. Let’s go ahead and update AppComponent accordingly:

+
@Component({...})
+export class AppComponent  {
+  ...
+  showPreview() {
+    // Returns a handle to the open overlay
+    let dialogRef: FilePreviewOverlayRef = this.previewDialog.open();
+
+    // Close overlay after 2 seconds
+    setTimeout(() => {
+      dialogRef.close();
+    }, 2000);
+  }
+}
+

Here’s our code in action. Remember, once we open an overlay it will automatically close after 2 seconds:

+ +

Awesome! We are making serious progress and it’s not far until we reach the top of the mountain.

+

Improving ergonomics

+

In the previous sections we have mainly improved the overlay under the hood and layed a foundation for upcoming features. In this section we want to focus on improving the overlay’s ergonomics. This means that we want to be able to close the dialog when we click on the backdrop.

+

Turns out that the backdrop logic is extremely easy with the CDK. All we have to do is to subscribe to a stream that emits a value when the backdrop was clicked:

+
@Injectable()
+export class FilePreviewOverlayService {
+  open(config: FilePreviewDialogConfig = {}) {
+    ...
+    // Subscribe to a stream that emits when the backdrop was clicked
+    overlayRef.backdropClick().subscribe(_ => dialogRef.close());
+
+    return dialogRef;
+  }
+}
+

That’s it! Imagine how much work this would be without the CDK.

+ +

From here we could take it one step further and also close the overlay when a user naviagtes back in the browser history. For our application, however, this doesn’t make much sense because we are not using the router and there’s only one page that we render out to the screen. But feel free to give it a shot! Hint: use the Location service and subscribe to the browser’s popState events.

+

Sharing data with the overlay component

+

The goal of this post was to implement a generic file preview dialog rather than a static one. At the moment the overlay is quite static and there’s no way we can share data with the overlay component. Sharing data means we want to be able to provide an image that will be available within the component. After all it’s supposed to be a file preview. Therefore, we need to think about how we can share data with the component that is dynamically created.

+

Luckily, Angular has a hierarchical dependency injection system (DI for short) that we can leverage for our purpose. For more information on Angular’s DI system, check out this post.

+

In a nutshell, the DI system is flexible enough that we can reconfigure the injectors at any level of the component tree. That said, there is no such thing as the injector. An application may have multiple injectors and each component instance has its own injector. You hear the bells ring? Right, we can create our own custom injector and provide it with a list of custom injection tokens. It sounds more complicated than it actually is.

+

Turns out, the CDK already has a class PortalInjector that that we can use to provide custom injection tokens to components inside a portal. This is exactly what we need. Let’s break ground and implement a function createInjector() that creates a new PortalInjector and defines a list of custom injection tokens.

+
@Injectable()
+export class FilePreviewOverlayService {
+  ...
+  private createInjector(config: FilePreviewDialogConfig, dialogRef: FilePreviewOverlayRef): PortalInjector {
+    // Instantiate new WeakMap for our custom injection tokens
+    const injectionTokens = new WeakMap();
+
+    // Set custom injection tokens
+    injectionTokens.set(FilePreviewOverlayRef, dialogRef);
+    injectionTokens.set(FILE_PREVIEW_DIALOG_DATA, config.data);
+
+    // Instantiate new PortalInjector
+    return new PortalInjector(this.injector, injectionTokens);
+  }
+

In the code above we create a new WeakMap, set our custom injection tokens that we want to be available (injectable) in the overlay component, and finally instantiate a new PortalInjector. The important part though is that we also specify a parent injector (first argument) which is mandatory. Also notice the second argument where we pass in our injection tokens.

+

There are two things that we are providing. The first token is the FilePreviewDialogRef. Having the remote control at hand, allows the overlay component to close itself. This is very useful because there will definitely be a close button somewhere. The second token is a custom InjectionToken that stores the data that we want to share with the component.

+

For the InjectionToken we create new file file-preview-overlay.tokens and instantiate a new InjectionToken:

+
import { InjectionToken } from '@angular/core';
+
+import { Image } from './file-preview-overlay.service';
+
+export const FILE_PREVIEW_DIALOG_DATA = new InjectionToken<Image>('FILE_PREVIEW_DIALOG_DATA');
+

Next, let’s update our FilePreviewDialogConfig so that the user can specify an image that will be used by the overlay component:

+
interface Image {
+  name: string;
+  url: string;
+}
+
+interface FilePreviewDialogConfig {
+  panelClass?: string;
+  hasBackdrop?: boolean;
+  backdropClass?: string;
+  data?: Image;
+}
+
+@Injectable()
+export class FilePreviewOverlayService {
+  ...
+}
+

For better readability we’ll also refactor our open() method and create a new attachDialogContainer() function that now takes care of creating the injector and component portal, as well as attaching the portal to the host.

+
@Injectable()
+export class FilePreviewOverlayService {
+  ...
+  private attachDialogContainer(overlayRef: OverlayRef, config: FilePreviewDialogConfig, dialogRef: FilePreviewOverlayRef) {
+    const injector = this.createInjector(config, dialogRef);
+
+    const containerPortal = new ComponentPortal(FilePreviewOverlayComponent, null, injector);
+    const containerRef: ComponentRef<FilePreviewOverlayComponent> = overlayRef.attach(containerPortal);
+
+    return containerRef.instance;
+  }
+}
+

With that in place, we can now update our FilePreviewOverlayComponent and inject the tokens that we have defined on a component level with the help of a custom injector.

+
export class FilePreviewOverlayComponent {
+  constructor(
+    public dialogRef: FilePreviewOverlayRef,
+    @Inject(FILE_PREVIEW_DIALOG_DATA) public image: any
+  ) { }
+}
+

We can now define data that will be passed to the overlay component and render an image onto the screen. Here’s an example of how we can pass in data:

+
@Component({...})
+export class AppComponent  {
+  ...
+  showPreview(file) {
+    let dialogRef: FilePreviewOverlayRef = this.previewDialog.open({
+      image: file
+    });
+  }
+}
+

Finally with a little bit of styling we come much closer to what we’re trying to achieve.

+ +

Where to go from here

+

This is it. Although creating custom overlays is something that’s more or less tricky to do, this task becomes rather easy with UI libraries like Angular Material that provide us with a common set of tools to build awesome and high-quality Angular components. More specifically, by extracting common behaviors and patterns into a so called Component Dev Kit, it becomes extremely easy to build a custom overlay.

+

Where to go from here? As mentioned in the beginning, this was only part one and we haven’t fully re-built the Google Drive-like file preview yet. In the next post we will build on top of this and implement keyboard support, image preloading and add animations in order to make our overlay more engaging.

Written by  Author

Dominic Elm

\ No newline at end of file diff --git a/angular/2017/11/27/custom-overlays-with-angulars-cdk-part-two.html b/angular/2017/11/27/custom-overlays-with-angulars-cdk-part-two.html new file mode 100644 index 000000000..8ec7feab5 --- /dev/null +++ b/angular/2017/11/27/custom-overlays-with-angulars-cdk-part-two.html @@ -0,0 +1,330 @@ +Custom Overlays with Angular's CDK - Part 2 | Articles by thoughtram

Angular

Custom Overlays with Angular's CDK - Part 2

In a previous post we have layed the foundation for our custom overlay. To recap, we wanted to build a Google Drive-like custom overlay that looks and feels much like the one built for MachineLabs. Let’s have a look at a preview:

+

overlay preview

+

In the first part of this series we learned how to use Angular’s CDK to create our very first custom overlay. On top of that we made it configurable, implemented a “handle” to control (e.g. close) an opened overlay and made it possible to share data with the overlay component.

+

In this post, we’ll pick up from where we left off and implement a few additional features that will take our overlay to the next level. More specifically, we’ll implement keyboard support, image preloading and add animations in order to make our overlay more engaging and provide better feedback. In the end, we’ll complete this post by adding a toolbar component to fully match Google Drive’s look and feel.

+

Let’s dive right into it!

+

Adding keyboard support

+

Adding keyboard support is easy. All we need to do is to use the @HostListener() decorator. This decorator is a function decorator that accepts an event name as an argument. Let’s use it inside our FilePreviewOverlayComponent to listen to keydown events on the HTML Document, so that we can close the overlay whenever the escape button was pressed.

+

Closing the dialog from within the overlay component is only possible because the FilePreviewOverlayRef is now available via the DI system. Remember that we created our own custom injector and defined custom injection tokens for the remote control and the data that we want to share with the component.

+

Let’s have a look at the code:

+
import { ..., HostListener } from '@angular/core';
+
+// Keycode for ESCAPE
+const ESCAPE = 27;
+
+@Component({...})
+export class FilePreviewOverlayComponent {
+...
+// Listen on keydown events on a document level
+@HostListener('document:keydown', ['$event']) private handleKeydown(event: KeyboardEvent) {
+  if (event.keyCode === ESCAPE) {
+    this.dialogRef.close();
+  }
+}
+
+constructor(public dialogRef: FilePreviewOverlayRef, ...) { }
+

Using the host listener we decorate a class method that is called on every keydown event. The function itself gets the KeyboardEvent that we can use to check whether it’s the escape key and only then call close() on the dialogRef.

+

That’s it already for adding keyboard support. Go ahead and try it out. Open a file preview and then press the escape button.

+ +

Preloading images

+

Instant app response is without any doubt the best, but there are cases when our apps won’t be able to deliver the content immediately, e.g. slow internet connection or even latency issues. In those cases it’s extremely important to provide users with feedback and indicate that progress is being made. It’s crucial to let the user know what is happening in contrast to keep them guessing. One of the most common forms of such feedback is a progress indicator. It reduces the user’s uncertainty, perception of time and offers a reason to wait.

+

Looking at our overlay, we are facing the exact same problems. When we click to preview an image, depending on the internet connection, the image is fetched by the browser and progressively rendered onto the screen. If the connection is really bad it may take a while. Also, it doesn’t really look that nice if we display image data as it is received, resulting in a top-down filling in of the image.

+

To solve this, we can use a progress indicator. The good thing is we don’t need to write one from scratch because Angular Material already provides a nice set of loading indicators, one of which is the <mat-spinner>. In order to use it, we need to add the MatProgressSpinnerModule from @angular/material to the imports of our application:

+
import { ..., MatProgressSpinnerModule } from '@angular/material';
+
+@NgModule({
+  imports: [
+    ...,
+    MatProgressSpinnerModule
+  ],
+  ...
+})
+export class AppModule { }
+

Note that the <mat-spinner> component is an alias for <mat-progress-spinner mode="indeterminate">. As we can see, the progress-spinner supports different modes, determinate and indeterminate.

+

The difference is that determinate progress indicators are used to indicate how long an operation will take, whereas indeterminate indicators request that the user needs to wait while something finishes. The latter is used when it’s not necessary to indicate how long it will take or to convey a discrete progress. This is perfect for preloading images because we have no idea how long it may take to fetch the image.

+

Ok, now that we have added the respective module to our imports we can go ahead and update the template of the FilePreviewOverlayComponent as well as the component class:

+
@Component({
+  template: `
+    <div class="overlay-content">
+      <div class="spinner-wrapper" *ngIf="loading">
+        <mat-spinner></mat-spinner>
+      </div>
+
+      <img (load)="onLoad($event)" [style.opacity]="loading ? 0 : 1" [src]="image.url">
+    </div>
+  `,
+  ...
+})
+export class FilePreviewOverlayComponent {
+
+  loading = false;
+
+  ...
+
+  onLoad(event: Event) {
+    this.loading = false;
+  }
+}
+

First off, we introduce a new property loading and initialize it with a meaningful value, e.g. false. This will show our spinner until the image is loaded. Also note that we are using a property binding to set the opacity of the <img> element to 0 when it’s loading and 1 when it’s finished. If we didn’t do this, we’d still see the image being rendered or filled in from top to bottom. This is just a temporary solution that we will replace with a proper solution using Angular’s Animation DSL in just a moment. Last but not least, we define a success callback as a method on our class that is called when the image is loaded. The callback is hooked up in the template via an event binding. In this particular case we are listening for the load event and when fired, we call the onLoad() method.

+

One more thing to mention is that we needed to wrap the spinner in another element. The reason for this is that we want the spinner to be vertically and horizontally centered. To achieve this we would leverage the CSS transform property to apply a transformation to the spinner element. The problem is that the <mat-spinner> component is animated with CSS transforms meaning every transformation we set on the element is overridden. Therefore we use a container that wraps the spinner, so that we can savely apply transformation and center it on the screen.

+

Here’s the image preloading in action. To better demonstrate the loading, you can throttle your connection in the “Network” tab of Chrome’s DevTools.

+ +

Animating the overlay

+

With animations we aim to guide users between views so they feel comfortable using the site, draw focused-attention to some parts of our application, increase spacial awareness, indicate if data is being loaded, and probably the most important point - smoothly transition users between states.

+

The problem with our overlay is that it still pops right into our faces. The backdrop is already animated for us, but having an animation only for the backdrop is not enough. We also want to add a little bit of motion to the overlay component, so that it’s less surprising for the user.

+

If you are completely new to animations in Angular, please check out our post on the Foundation Concepts or have a look at our Web Animations Deep Dive with Angular.

+

Let’s start off by importing the BrowserAnimationsModule into our application’s NgModule like this:

+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+@NgModule({
+  imports: [
+    ...,
+    BrowserAnimationsModule
+  ],
+  ...
+})
+export class AppModule { }
+

With this in place, we can go ahead and define our animations with Angular’s Animation DSL and add it to the component via the animations metadata property in the @Component() decorator:

+
// Reusable animation timings
+const ANIMATION_TIMINGS = '400ms cubic-bezier(0.25, 0.8, 0.25, 1)';
+
+@Component({
+  ...
+  animations: [
+    trigger('fade', [
+      state('fadeOut', style({ opacity: 0 })),
+      state('fadeIn', style({ opacity: 1 })),
+      transition('* => fadeIn', animate(ANIMATION_TIMINGS))
+    ]),
+    trigger('slideContent', [
+      state('void', style({ transform: 'translate3d(0, 25%, 0) scale(0.9)', opacity: 0 })),
+      state('enter', style({ transform: 'none', opacity: 1 })),
+      state('leave', style({ transform: 'translate3d(0, 25%, 0)', opacity: 0 })),
+      transition('* => *', animate(ANIMATION_TIMINGS)),
+    ])
+  ]
+})
+export class FilePreviewOverlayComponent {
+  ...
+}
+

As you can see we created two animations, one for fading in the image (fade) and the other one to slide up the content (slideContent). The fade animation will mostly be visible in combination with a spinner. Remember how we used the property binding to temporarily make the image invisible while loading? With the fade animation we can now replace our temporary solution with proper one that leverages the animation DSL.

+

Next, we define an animationState property that represents the current animation state, e.g. void, enter or leave. By default it’s set to enter that will cause the content of the file preview to always slide up when it’s opened.

+
@Component({...})
+export class FilePreviewOverlayComponent {
+  ...
+  animationState: 'void' | 'enter' | 'leave' = 'enter';
+}
+

Now we can connect the pieces and set it up in the template:

+
@Component({
+  template: `
+    <div class="overlay-content" [@slideContent]="animationState">
+      <div class="spinner-wrapper" *ngIf="loading">
+        <mat-spinner></mat-spinner>
+      </div>
+
+      <img [@fade]="loading ? 'fadeOut' : 'fadeIn'" (load)="onLoad($event)" [src]="image.url">
+    </div>
+  `,
+  ...
+})
+export class FilePreviewOverlayComponent {
+  ...
+}
+

We can see that the entire content, including the spinner as well as the image, is wrapped in a container div. That’s because we only want the content to slide up. Later we’ll introduce a toolbar component which comes with its own set of animations. This also implies that it wouldn’t make any sense to use a @HostBinding() and apply the animation to the host element.

+

Done! Here’s a live demo with all the code above:

+ +

Animations guide our users and the overlay smoothly animates in. This is already much more engaging compared to what we had before but we can do even better. What about closing it? That’s right, we also want it to animate out.

+

At the moment we simply call this.overlayRef.dispose() which will detach the portal from the host, eventually removing it from the DOM. With this straightforward approach there’s no way we can execute animations before it is disposed.

+

What we can do instead is to leverage animation callbacks and introduce event streams that we can subscribe to. Angular provides animation callbacks that are fired when an animation is started and also when it is done.

+

Let’s start with the animation callbacks and hook them in the template of our FilePreviewOverlayComponent:

+
@Component({
+  template: `
+    <div class="overlay-content"
+      [@slideContent]="animationState"
+      (@slideContent.start)="onAnimationStart($event)"
+      (@slideContent.done)="onAnimationDone($event)">
+      ...
+    </div>
+  `,
+  ...
+})
+export class FilePreviewOverlayComponent {
+  ...
+}
+

The animation callbacks alone are not very useful, at least not for animating the overlay out. What’s missing is an event bus that we can use to broadcast animation events. That’s needed because when we close an overlay, we have to wait until the leaving animation is done before we can dispose the overlay. We’ll see in just a second how to use this but first let’s define a new EventEmitter inside the overlay component class:

+
import { ..., EventEmitter } from '@angular/core';
+...
+@Component({...})
+export class FilePreviewOverlayComponent {
+  ...
+  animationStateChanged = new EventEmitter<AnimationEvent>();
+}
+

The EventEmitter is an abstraction around a the Subject type from RxJS.

+

Cool, now let’s wire up the animation callbacks. To do this, we’ll define the two missing methods onAnimationStart() and onAnimationDone(). Every time one of the animation callbacks is fired, we broadcast the animation event using animationStateChanged. Moreover, we need a way to start the leave animation once we close an overlay. Therefore we’ll also add a method called startExitAnimation() that sets the animationState to leave. This will then trigger the corresponding animation.

+
@Component({...})
+export class FilePreviewOverlayComponent {
+  ...
+  onAnimationStart(event: AnimationEvent) {
+    this.animationStateChanged.emit(event);
+  }
+
+  onAnimationDone(event: AnimationEvent) {
+    this.animationStateChanged.emit(event);
+  }
+
+  startExitAnimation() {
+    this.animationState = 'leave';
+  }
+}
+

We know that we programatically close an overlay using the remote control aka FilePreviewOverlayRef. So far we have no access to the overlay component from within the FilePreviewOverlayRef. To fix this we define a property componentInstance on the remote control.

+
export class FilePreviewOverlayRef {
+  ...
+  componentInstance: FilePreviewOverlayComponent;
+  ...
+}
+

We simply set the componentInstance when an overlay is opened.

+
@Injectable()
+export class FilePreviewOverlayService {
+  ...
+  open(config: FilePreviewDialogConfig = {}) {
+    ...
+    // Instantiate remote control
+    const dialogRef = new FilePreviewOverlayRef(overlayRef);
+
+    const overlayComponent = this.attachDialogContainer(overlayRef, dialogConfig, dialogRef);
+
+    // Pass the instance of the overlay component to the remote control
+    dialogRef.componentInstance = overlayComponent;
+    ...
+  }
+}
+

Now we can go ahead and introduce two event streams on the FilePreviewOverlayRef. The first one emits values before the overlay is closed and the other one after is was closed. For the streams we’ll use the Subject type from RxJS and also expose public methods for each of the event streams.

+
export class FilePreviewOverlayRef {
+
+  private _beforeClose = new Subject<void>();
+  private _afterClosed = new Subject<void>();
+
+  ...
+
+  afterClosed(): Observable<void> {
+    return this._afterClosed.asObservable();
+  }
+
+  beforeClose(): Observable<void> {
+    return this._beforeClose.asObservable();
+  }
+}
+

From there we can use the component instance and subscribe to the animation events and act accordingly. This means that when an animation was started we detach the backdrop and call next() on _beforeClose. When an animation is finished we broadcast on _afterClosed. This also means we can dispose the overlay and remove it from the DOM.

+
import { filter, take } from 'rxjs/operators';
+...
+export class FilePreviewOverlayRef {
+  ...
+  close(): void {
+    // Listen for animation 'start' events
+    this.componentInstance.animationStateChanged.pipe(
+      filter(event => event.phaseName === 'start'),
+      take(1)
+    ).subscribe(() => {
+      this._beforeClose.next();
+      this._beforeClose.complete();
+      this.overlayRef.detachBackdrop();
+    });
+
+    // Listen for animation 'done' events
+    this.componentInstance.animationStateChanged.pipe(
+      filter(event => event.phaseName === 'done' && event.toState === 'leave'),
+      take(1)
+    ).subscribe(() => {
+      this.overlayRef.dispose();
+      this._afterClosed.next();
+      this._afterClosed.complete();
+
+      // Make sure to also clear the reference to the
+      // component instance to avoid memory leaks
+      this.componentInstance = null!;
+    });
+
+    // Start exit animation
+    this.componentInstance.startExitAnimation();
+  }
+}
+

That’s it. Here’s a live demo:

+ +

Adding a toolbar component

+

To finish this up we will create a toolbar component, add animations and also make use of the events streams exposed by the remote control to animate the toolbar out before the overlay is closed.

+
@Component({
+  selector: 'tm-file-preview-overlay-toolbar',
+  templateUrl: './file-preview-overlay-toolbar.component.html',
+  styleUrls: ['./file-preview-overlay-toolbar.component.scss'],
+  animations: [
+    trigger('slideDown', [
+      state('void', style({ transform: 'translateY(-100%)' })),
+      state('enter', style({ transform: 'translateY(0)' })),
+      state('leave', style({ transform: 'translateY(-100%)' })),
+      transition('* => *', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)'))
+    ])
+  ]
+})
+export class FilePreviewOverlayToolbarComponent implements OnInit {
+
+  // Apply animation to the host element
+  @HostBinding('@slideDown') slideDown = 'enter';
+
+  // Inject remote control
+  constructor(private dialogRef: FilePreviewOverlayRef) { }
+
+  ngOnInit() {
+    // Animate toolbar out before overlay is closed
+    this.dialogRef.beforeClose().subscribe(() => this.slideDown = 'leave');
+  }
+}
+

The template is very straightforward and leverages content projection to project content into the template of the toolbar component.

+
<!-- file-preview-overlay-toolbar.component.html -->
+<div class="toolbar-wrapper">
+  <ng-content></ng-content>
+</div>
+

Finally we have to use the toolbar inside the template of the overlay component:

+
@Component({
+  template: `
+    <tm-file-preview-overlay-toolbar>
+      <mat-icon>description</mat-icon>
+      {{ image.name }}
+    </tm-file-preview-overlay-toolbar>
+
+    <div class="overlay-content"
+      ...
+    </div>
+  `,
+  ...
+})
+export class FilePreviewOverlayComponent {
+  ...
+}
+

That’s pretty much! Let’s have a look at our final solution:

+
Written by  Author

Dominic Elm

\ No newline at end of file diff --git a/angular/2018/03/05/advanced-caching-with-rxjs.html b/angular/2018/03/05/advanced-caching-with-rxjs.html new file mode 100644 index 000000000..36f2d3dee --- /dev/null +++ b/angular/2018/03/05/advanced-caching-with-rxjs.html @@ -0,0 +1,706 @@ +Advanced caching with RxJS | Articles by thoughtram

Angular

Advanced caching with RxJS

When building web applications, performance should always be a top priority. There are many things we can do to speed up our Angular applications like tree-shaking, AoT (ahead-of-time compilation), lazy loading modules or caching. To get an overview of practices that will help you boost the performance of your Angular applications, we highly recommend you to check out the Angular Performance Checklist by Minko Gechev. In this post we focus on caching.

+

In fact, caching is one of the most efficient ways to improve the experience of our site, especially when our users are on bandwidth restricted devices or slow networks.

+

There are several ways to cache data or assets. Static assets are most commenly cached with the standard browser cache or Service Workers. While Service Workers can also cache API requests, they are typically more useful for caching resources like images, HTML, JS or CSS files. To cache application data we usually use custom mechanisms.

+

No matter what mechanism we use, a cache generally improves the responsiveness of our application, decreases network costs and has the advantage that content becomes available during network interruptions. In other words, when the content is cached closer to the consumer, say on the client side, requests don’t cause additional network activity and cached data can be retrieved much faster because we save on an entire network round trip.

+

In this post we’ll develop an advanced caching mechanism with RxJS and the tools provided by Angular.

+

Motivation

+

Every now and then there’s this question popping up how to cache data in an Angular application that makes excessive use of Observables. Most people have a good understanding on how to cache data with Promises but feel overwhelmed when it comes to functional reactive programming, due to its complexity (large API), fundamental shift in mindset (from imperative to declarative) and the multitude of concepts. Hence, it can be hard to actually translate an existing caching mechanism based on Promises to Observables, especially if you want that mechanism to be a little bit more advanced.

+

In an Angular application, we typically perform HTTP requests through the HttpClient that comes with the HttpClientModule. All of its APIs are Observable-based meaning that methods like get, post, put or delete return an Observable. Because Observables are lazy by nature the request is only made when we call subscribe. However, calling subscribe multiple times on the same Observable will cause the source Observable to be re-created over and over again and, hence, perform a request on each subscription. We call this cold Observables.

+

If you are completely new to this, we have written an article on Cold vs Hot Observables.

+

This behavior can make it tricky to implement a caching mechanism with Observables. Simple approaches often require a fair amount of boilerplate and we probably end up bypassing RxJS, which works, but is not the recommended way if we want to harness the power of Observables. Literally speaking, we don’t wanna drive a Ferrari with a scooter engine, right?

+

The Requirements

+

Before we dive into code, let’s start to define the requirements for our advanced caching mechanism.

+

We want to build an application called World of Jokes. It’s a simple app that randomly shows jokes for a given category. To keep it simple and focused, there’s only one category.

+

This app has three components: AppComponent, DashboardComponent and JokeListComponent.

+

The AppComponent is our entry point and renders a toolbar as well as a <router-outlet> that is filled based on the current router state.

+

The DashboardComponent simply shows a list of categories. From here, we can navigate to the JokeListComponent which then renders a list of jokes onto the screen.

+

The jokes themselves are fetched from a server using Angular’s HttpClient service. To keep the component’s responsibility focused and separate the concerns, we want to create a JokeService that takes care of requesting the data. The component can then simply inject the service and access the data through its public APIs.

+

All of the above is just our application’s architecture and there’s no caching involved yet.

+

When navigating from the dashboard to the list view, we prefer to request the data from a cache rather than requesting it from the server every time. The underlying data of this cache would update itself every 10 seconds.

+

Of course, polling for new data every 10 seconds isn’t a solid strategy for a production app where we would rather use a more sophisticated approach to update the cache (e.g. web socket push updates). But we’ll try to keep things simple here to focus on the caching aspect.

+

In any case we’d receive some sort of update notification. For our application we want the data in the UI (JokeListComponent) to not automatically update when the cache updates but rather waits for the user to enforce the UI update. Why? Imagine a user may be reading one of the jokes and then all of a sudden it’s gone because the data is updated automatically. That would be super annoying and a bad user experience. Therefore, our users receive notifications whenever new data is available.

+

To make it even more fun, we want the user to be able to force the cache to update. This is different from solely updating the UI because forcing an update means to freshly request the data from the server, update the cache and then also update the UI accordingly.

+

Let’s summarize what we want to build:

+
    +
  • Our app has two components where navigating from component A to B should prefer requesting B’s data from a cache rather than requesting it from the server every time
  • +
  • Cache is updated every 10 seconds
  • +
  • Data in the UI is not automatically updated and requires the user to enforce an update
  • +
  • User can force an update that will cause a request to actually update the cache and the UI
  • +
+

Here’s a preview of what we are going to build:

+

app preview

+

Implementing a basic cache

+

Let’s start simple and work our way up to the final and fully-fledged solution.

+

The first step is to create a new service.

+

Next, we’ll add two interfaces, one that describes the shape of a Joke and the other is used to strongly type the response of our HTTP request. This makes TypeScript happy but most importantly more convenient and obvious to work with.

+
export interface Joke {
+  id: number;
+  joke: string;
+  categories: Array<string>;
+}
+
+export interface JokeResponse {
+  type: string;
+  value: Array<Joke>;
+}
+

Now let’s implement the JokeService. We don’t want to reveal the implementation detail of whether the data was served from cache or freshly requested from the server, hence we simply expose a property jokes returning an Observable that captures a list of jokes.

+

In order to perform HTTP requests, we need to make sure to inject the HttpClient service in the constructor of our service.

+

Here’s the shell for the JokeService:

+
import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable()
+export class JokeService {
+
+  constructor(private http: HttpClient) { }
+
+  get jokes() {
+    ...
+  }
+}
+

Next, we implement a private method requestJokes() which uses the HttpClient to perform a GET request to retrieve a list of jokes.

+
import { map } from 'rxjs/operators';
+
+@Injectable()
+export class JokeService {
+
+  constructor(private http: HttpClient) { }
+
+  get jokes() {
+    ...
+  }
+
+  private requestJokes() {
+    return this.http.get<JokeResponse>(API_ENDPOINT).pipe(
+      map(response => response.value)
+    );
+  }
+}
+

With that in place, we have everything we need to implement the jokes getter method.

+

One naive approach would be to simply return this.requestJokes(), but that doesn’t do the trick. We know from the beginning that all methods exposed by the HttpClient, for instance get, return cold Observables. This means that the whole data stream is re-emitted for each subscriber causing an overhead of HTTP requests. After all, the idea behind a cache is to speed up the load time of our application and limit the amount of network requests to a minimum.

+

Instead we want to make our stream hot. Not only that, but every new subscriber should receive the latest cached value. It turns out that there’s a very convenient operator called shareReplay. This operator returns an Observable that shares a single subscription to the underlying source, which is the Observable returned from this.requestJokes().

+

In addition, shareReplay accepts an optional parameter bufferSize that is really handy in our case. The bufferSize determines the maximum element count of the replay buffer, that is the number of elements that are cached and replayed for every subscriber. For our scenario we only want to replay the most recent value and, hence, set the bufferSize to one (1).

+

Let’s look at the code and use what we have just learned:

+
import { Observable } from 'rxjs/Observable';
+import { shareReplay, map } from 'rxjs/operators';
+
+const API_ENDPOINT = 'https://api.icndb.com/jokes/random/5?limitTo=[nerdy]';
+const CACHE_SIZE = 1;
+
+@Injectable()
+export class JokeService {
+  private cache$: Observable<Array<Joke>>;
+
+  constructor(private http: HttpClient) { }
+
+  get jokes() {
+    if (!this.cache$) {
+      this.cache$ = this.requestJokes().pipe(
+        shareReplay(CACHE_SIZE)
+      );
+    }
+
+    return this.cache$;
+  }
+
+  private requestJokes() {
+    return this.http.get<JokeResponse>(API_ENDPOINT).pipe(
+      map(response => response.value)
+    );
+  }
+}
+

Ok, we already talked about most of what we see above. But wait, what’s about the private cache$ property and if statement inside the getter? The answer to this is quite simple. If we returned this.requestJokes().pipe(shareReplay(CACHE_SIZE)) directly then every subscriber creates a new cache instance. However, we want to share a single instance across all subscribers. Therefore, we keep the instance in a private property cache$ and initialize it as soon as the getter was called the first time. All subsequent consumers will receive the shared instance without re-creating the cache every time.

+

Let’s look at a more visual representation of what we’ve just implemented:

+

+ + + cache sequence diagram + +

+

Above we can see a sequence diagram that depicts the objects involved in our scenario, that is requesting a list of jokes, and the sequences of messages exchanged between the objects. Let’s break it down to understand what’s going on here.

+

We start out on the dashboard from where we navigate to the list component.

+

After the component was initialized and Angular calls the ngOnInit life cycle hook, we request the list of jokes by calling the getter function jokes exposed by the JokeService. Since this is the first time we ask for the data, the cache itself is empty and not yet initialized, meaning JokeService.cache$ is undefined. Internally we call requestJokes(). This will give us an Observable that emits the data from the server. At the same time we apply the shareReplay operator to get the desired behavior.

+

The shareReplay operator automatically creates a ReplaySubject between the original source and all future subscribers. As soon as the number of subscribers goes from zero to one it will connect the Subject to the underlying source Observable and broadcast all its values. All future subscribers will be connected to that in-between Subject, so that effectively there’s just one subscription to the underlying cold Observable. This is called multicasting and defines the foundation for our simple cache.

+

Once the data comes back from the server it will be cached.

+

Note that the Cache is a standalone object in the sequence diagram and is supposed to illustrate the ReplaySubject that is created in between the consumer (subscribers) and the underlying source (HTTP request).

+

The next time we request the data for the list component, our cache will replay the most recent value and send that to the consumer. There’s no additional HTTP call involved.

+

Simple, right?

+

To rip this really apart, let’s take this one step further and look at how the cache works on an Observable level. For this we use a marble diagram to visualize how the stream actually works:

+

+ + + cache share replay + +

+

The marble diagram makes it really clear that there’s only one subscription to the underlying Observable and all consumers simply subscribe to the shared Observable, that is the ReplaySubject. We can also see that only the first subscriber triggers the HTTP call and all others get the latest value replayed.

+

Finally, let’s look at the JokeListComponent and how we can display the data. The first step is to inject the JokeService. After that, inside ngOnInit we initialize a property jokes$ with the value returned by the getter function that is exposed by our service. This will return an Observable of type Array<Joke> and this is exactly what we want.

+
@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  jokes$: Observable<Array<Joke>>;
+
+  constructor(private jokeService: JokeService) { }
+
+  ngOnInit() {
+    this.jokes$ = this.jokeService.jokes;
+  }
+
+  ...
+}
+

Note that we are not imperatively subscribing to jokes$. Instead we use the async pipe in the template because it turns out that this pipe is full of little wonders. Curious? Check out this article that unravels three things you didn’t know about the AsyncPipe.

+
<mat-card *ngFor="let joke of jokes$ | async">...</mat-card>
+

Cool! Here’s our simple cache in action. To verify if the request is only made once, open up Chrome’s DevTools, click on the Network tab and then select XHR. Start out on the dashboard, go to the list view and then navigate back and forth.

+ +

Automatic updates

+

So far we have built a simple caching mechanism in a few lines of code. In fact, most of the heavy lifting is done by the shareReplay operator which takes care of caching and replaying the most recent value(s).

+

This works perfectly fine but the data is never actually updated in the background. What if the data is likely to change every few minutes? We certainly don’t want to force the user to reload the entire page just to get the latest data from the server.

+

Wouldn’t it be cool if our cache is updated every 10 seconds in the background? Totally! As a user we don’t have to reload the page and if the data has changed the UI will update accordingly. Again, in a real-world application we would most probably not even use polling but instead have the server push notifications. For our little demo app a refresh interval of 10 seconds is just fine.

+

The implementation is fairly easy. In a nutshell, we want to create an Observable that emits a sequence of values spaced by a given time interval, or simply said, we want to produce a value every X milliseconds. For that we have several options.

+

The first option is to use interval. This operator takes an optional parameter period that defines the time between each emission. Consider the following example:

+
import { interval } from 'rxjs/observable/interval';
+
+interval(10000).subscribe(console.log);
+

Here we set up an Observable that emits an infinite sequence of integers where each value is emitted every 10 seconds. That also means that the first value is somewhat delayed by the given interval. To better demonstrate the behavior, let’s take a look at the marble diagram for interval.

+

+ + + interval operator + +

+

Yep, as expected. The first value is “delayed” and this is not what we want. Why? Because if we come from the dashboard and navigate to the list component to read some funny jokes, we’d have to wait for 10 seconds before the data is requested from the server and rendered onto the screen.

+

We could fix this by introducing another operator called startWith(value) which would emit the given value first, as an initial value. But we can do better!

+

What if I told you that there’s an operator that emits a sequence of values after a given duration (initial delay) and then after each period (regular interval)? Meet timer.

+

Visualization time!

+

+ + + timer operator + +

+

Cool, but does that really solve our problem? Yep it does. If we set the initial delay to zero (0) and set the period to 10 seconds, we end up with the same behavior as if we used interval(10000).pipe(startWith(0)) but only with a single operator.

+

Let’s take that and plug it into our exisiting caching mechanism.

+

We have to set up a timer and for every tick we want to make an HTTP request to fetch new data from the server. That is, for every tick we need to switchMap to an Observable that, on subscription, fetches a new list of jokes. Using switchMap has the positive side effect that we avoid race conditions. That’s due to the nature of this operator because it will unsubscribe from the previously projected Observable and only emit values from the most recently projected Observable.

+

The rest of our cache remains untouched, meaning that our stream is still multicasted and all subscribers share one underlying source.

+

Again, the nature of shareReplay will broadcast new values to exisiting subscribers and replay the most recent value to new subscribers.

+

+ + + timer cache + +

+

As we can see in the marble diagram, the timer emits a value every 10 seconds. For every value we switch to an inner Observable that fetches our data. Because we are using switchMap, we avoid race conditions and therefore the consumer only receives the value 1 and 3. The value from the second inner Observable is “skipped” because we are already unsubscribed when the value arrives.

+

Let’s apply our learnings and update the JokeService accordingly.

+
import { timer } from 'rxjs/observable/timer';
+import { switchMap, shareReplay } from 'rxjs/operators';
+
+const REFRESH_INTERVAL = 10000;
+
+@Injectable()
+export class JokeService {
+  private cache$: Observable<Array<Joke>>;
+
+  constructor(private http: HttpClient) { }
+
+  get jokes() {
+    if (!this.cache$) {
+      // Set up timer that ticks every X milliseconds
+      const timer$ = timer(0, REFRESH_INTERVAL);
+
+      // For each tick make an http request to fetch new data
+      this.cache$ = timer$.pipe(
+        switchMap(_ => this.requestJokes()),
+        shareReplay(CACHE_SIZE)
+      );
+    }
+
+    return this.cache$;
+  }
+
+  ...
+}
+

Awesome! Wanna try it out yourself? Go ahead and play with the following live demo. From the dashboard, go to the list component and then watch the magic happening. Give it a few seconds so that you can see the update in action. Remember, the cache is refreshed every 10 seconds, but feel free to fiddle with the REFRESH_INTERVAL.

+ +

Sending update notifications

+

Let’s recap for a moment what we have built so far.

+

When we request data from our JokeService we always prefer to request that data from a cache rather than requesting it from the server every time. The underlying data of this cache is refreshed every 10 seconds and when this happens, the data is propagated to the component causing the UI to update automatically.

+

That’s unfortunate. Imagine we’re a user that is reading one of the jokes and all of the sudden it’s gone because the UI is updated automatically. That’s super annoying and a bad user experience.

+

Therefore, our users should rather receive notifications when there’s new data available. In other words, we want the user to enforce the UI update.

+

It turns out that we don’t have to touch our service in order to implement this. The logic is quite simple. After all, our service should not worry about sending notifications and the view should be in charge when and how to update the data on the screen.

+

First, we have to get an initial value to show something to the user, because otherwise the screen will be blank until the cache was updated the first time. We’ll see why in just a moment. Setting up a stream for the inital value is as easy as calling the getter function. Additionally, since we are only interested in the very first value we can use the take operator.

+

To make this logic reusable we create a helper methode getDataOnce().

+
import { take } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  ...
+  ngOnInit() {
+    const initialJokes$ = this.getDataOnce();
+    ...
+  }
+
+  getDataOnce() {
+    return this.jokeService.jokes.pipe(take(1));
+  }
+  ...
+}
+

From our requirements we know that we only want to update the UI when the user really enforces an update rather than reflecting the change automatically. How does the user enforce an update you ask? This happens when we click on a button in the UI that says “Update”. This button is shown together with the notification. For now, let’s not worry about the notification and instead focus on the logic that updates the UI when we click that button.

+

To make this work, we need a way to create an Observable from DOM events, specifically from button clicks. There are several ways but a very common way is to use a Subject as a bridge between the template and the view logic that lives in the component class. In a nutshell, a Subject is a type that implements both Observer and Observable types. Observables define the data flow and produce the data while Observers can subscribe to Observables and receive the data.

+

The good thing about the Subject here is that we can simply use an event binding in the template and then call next when the event is triggered. This will cause the specified value to be broadcasted to all Observers that are listening for values. Note that we can also omit the value if the Subject is of type void. In fact, this is true for our case.

+

Let’s go ahead and instantiate a new Subject.

+
import { Subject } from 'rxjs/Subject';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  update$ = new Subject<void>();
+  ...
+}
+

Now we can go ahead and wire this up in the template.

+
<div class="notification">
+  <span>There's new data available. Click to reload the data.</span>
+  <button mat-raised-button color="accent" (click)="update$.next()">
+    <div class="flex-row">
+      <mat-icon>cached</mat-icon>
+      UPDATE
+    </div>
+  </button>
+</div>
+

See how we use the event binding syntax to capture the click event on the <button>? When we click on the button we simply propagate a ghost value causing all active Observers to be notified. We call it ghost value because we are not actually passing in any value, or at least a value of type void.

+

Another way would be to use the @ViewChild() decorator in combination with the fromEvent operator from RxJS. However, this requires us to “mess” with the DOM and query an HTML element from the view. With a Subject we are actually just bridging the two sides and don’t really touch the DOM at all except the event binding we are adding to the button.

+

Alright, with the view being setup we can now switch to the logic that takes care of updating the UI.

+

So what does it mean to update the UI? Well, the cache is updated in the background automatically and we want to render the most recent value from the cache when we click on that button, right? This means that our source stream in this case is the Subject. Every time a value is broadcasted on update$ we want to map this value to an Observable that gives us the latest cached value. In other words, we are dealing with a so-called Higher Order Observable, an Observable that emits Observables.

+

From before we should know that there’s switchMap that solves exactly this problem. This time we’ll use mergeMap instead. This operator behaves very similar to switchMap with the difference that it does not unsubscribe from the previously projected inner Observable and simply merges the inner emissions in the output Observable.

+

In fact, when requesting the most recent value from the cache, the HTTP request is already done and the cache was successfully updated. Therefore, we don’t really face the problem of race-conditions here. Though it seems to be asynchronous, it’s actually somewhat synchronous because the value will be emitted in the same tick.

+
import { Subject } from 'rxjs/Subject';
+import { mergeMap } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  update$ = new Subject<void>();
+  ...
+
+  ngOnInit() {
+    ...
+    const updates$ = this.update$.pipe(
+      mergeMap(() => this.getDataOnce())
+    );
+    ...
+  }
+  ...
+}
+

Sweet! For every “update” we request the latest value from the cache using our helper method we implemented earlier.

+

From here, it’s only a small step to come up with the stream for the jokes that are rendered onto the screen. All we have to do is to merge the initial list of jokes with our update$ stream.

+
import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+import { merge } from 'rxjs/observable/merge';
+import { mergeMap } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  jokes$: Observable<Array<Joke>>;
+  update$ = new Subject<void>();
+  ...
+
+  ngOnInit() {
+    const initialJokes$ = this.getDataOnce();
+
+    const updates$ = this.update$.pipe(
+      mergeMap(() => this.getDataOnce())
+    );
+
+    this.jokes$ = merge(initialJokes$, updates$);
+    ...
+  }
+  ...
+}
+

It’s important that we use our helper method getDataOnce() to map each update event to the latest cached value. If we recall, it uses take(1) internally which will take the first value and then complete the stream. This is crucial because otherwise we’d end up with an on-going stream or live connection to the cache. In this case we would basically break our logic of enforcing a UI update only by clicking the “Update” button.

+

Also, because the underlying cache is multicasted, it’s totally safe to always re-subscribe to the cache to get the latest value.

+

Before we continue with the notification stream, let’s stop for a moment and visualize what we just implemented as a marble diagram.

+

+ + + jokes + +

+

As we can see in the diagram above, initialJokes$ is crucial because otherwise we’d only see something on the screen when we click “Update”. While the data is already updated in the background every 10 seconds, there’s no way we can press this button. That’s because the button is part of the notification and we never really show it to the user.

+

Let’s fill this gap and implement the missing functionality to complete the puzzle.

+

For that, we have to create an Observable that is responsible for showing or hiding the notification. Essentially, we need a stream that either emits true or false. We want the value to be true when there’s an update, and false when the user clicks on the “Update” button.

+

In addition we want to skip the first (initial) value emitted by our cache because it’s not really a refresh.

+

If we think in terms of streams, we can break this up into multiple streams and merge them together to turn them into a single Observable. The final stream then has the desired behavior to show or hide notifications.

+

Enough theory! Here’s the code:

+
import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+import { skip, mapTo } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  showNotification$: Observable<boolean>;
+  update$ = new Subject<void>();
+  ...
+
+  ngOnInit() {
+    ...
+    const initialNotifications$ = this.jokeService.jokes.pipe(skip(1));
+    const show$ = initialNotifications$.pipe(mapTo(true));
+    const hide$ = this.update$.pipe(mapTo(false));
+    this.showNotification$ = merge(show$, hide$);
+  }
+  ...
+}
+

Here, we listen for all values emitted by our cache but skip the first because it’s not a refresh. For every new value on initialNotifications$ we map it to true to show the notification. Once we click the “Update” button in the notification, a value is produced on update$ and we can simply map it to false causing the notification to disappear.

+

Let’s use showNotification$ inside the template of the JokeListComponent to toggle a class that either shows or hides the notification.

+
<div class="notification" [class.visible]="showNotification$ | async">
+  ...
+</div>
+

Yay! We are getting really close to the final solution. But before we continue, let’s try it out and play around with the live demo. Take your time and go through the code step by step again.

+ +

Fetching new data on demand

+

Awesome! We have come a long way and already implemented a few very cool features for our cache. To finish up this article and take our cache to a whole new level, there’s one thing left for us to do. As a user we want to be able to force an update at any point in time.

+

It’s not really that complicated but we have to touch both the component and the service to make this work.

+

Let’s start with our service. What we need is a public facing API that will force the cache to reload the data. Technically speaking, we’ll complete the current cache and set it to null. This means that the next time we request the data from our service we will set up a new cache, fetch the data and store this for future subscribers. It’s not a big deal to create a new cache every time we enforce an update because it will be completed and eventually garbage collected. In fact, this has the positive side effect that we also reset the timer which is absolutely desired. Let’s say we have waited 9 seconds and now click “Fetch new Jokes”. We expect the data to be refreshed, but we don’t to see a notification popping up 1 second later. Instead we want to restart the timer so that when we enforce an update it another 10 seconds to trigger the automatic update.

+

Another reason for the destroying the cache is that it’s much less complex compared to a mechanism that keeps the cache running all the time. If that’s the case then the cache needs to be aware of whether a reload was enforced or not.

+

Let’s create a Subject that we use to tell the cache to complete. We’ll leverage takeUntil and pluck it into our cache$ stream. In addition, we implement a public facing API that, internally, sets the cache to null and also broadcasts an event on our Subject.

+
import { Subject } from 'rxjs/Subject';
+import { timer } from 'rxjs/observable/timer';
+import { switchMap, shareReplay, map, takeUntil } from 'rxjs/operators';
+
+const REFRESH_INTERVAL = 10000;
+
+@Injectable()
+export class JokeService {
+  private reload$ = new Subject<void>();
+  ...
+
+  get jokes() {
+    if (!this.cache$) {
+      const timer$ = timer(0, REFRESH_INTERVAL);
+
+      this.cache$ = timer$.pipe(
+        switchMap(() => this.requestJokes()),
+        takeUntil(this.reload$),
+        shareReplay(CACHE_SIZE)
+      );
+    }
+
+    return this.cache$;
+  }
+
+  forceReload() {
+    // Calling next will complete the current cache instance
+    this.reload$.next();
+
+    // Setting the cache to null will create a new cache the
+    // next time 'jokes' is called
+    this.cache$ = null;
+  }
+
+  ...
+}
+

This alone doesn’t do much, so let’s go ahead and use that in our JokeListComponent. For this we’ll implement a function forceReload() that is called whenever we click on the button that says “Fetch new Jokes”. Also, we need to create a Subject that is used as an event bus for updating the UI as well as showing the notifications. We’ll see in a moment where this comes into play.

+
import { Subject } from 'rxjs/Subject';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  forceReload$ = new Subject<void>();
+  ...
+
+  forceReload() {
+    this.jokeService.forceReload();
+    this.forceReload$.next();
+  }
+  ...
+}
+

With this in place we can wire up the button in the template of the JokeListComponent to force the cache to reload the data. All we have to do is listen for the click event using Angular’s event binding syntax and call forceReload().

+
<button class="reload-button" (click)="forceReload()" mat-raised-button color="accent">
+  <div class="flex-row">
+    <mat-icon>cached</mat-icon>
+    FETCH NEW JOKES
+  </div>
+</button>
+

This already works, but only if we go back to the dashboard and then again to the list view. This is of course not what we want. We want the UI to update immediately when we force the cache to reload the data.

+

Remeber that we have implemented a stream updates$ that, when we click on “Update”, requests the latest data from our cache? It turns out that we need exactly the same behavior, so we can go ahead and extend this stream. This means we have to merge both update$ and forceReload$, because those two streams are the sources for updating the UI.

+
import { Subject } from 'rxjs/Subject';
+import { merge } from 'rxjs/observable/merge';
+import { mergeMap } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  update$ = new Subject<void>();
+  forceReload$ = new Subject<void>();
+  ...
+
+  ngOnInit() {
+    ...
+    const updates$ = merge(this.update$, this.forceReload$).pipe(
+      mergeMap(() => this.getDataOnce())
+    );
+    ...
+  }
+  ...
+}
+

That was easy, wasn’t it? Yea but we are not done. In fact, we just “broke” our notifications. It all works just fine until we click “Fetch new Jokes”. The data is updated on the screen as well as in our cache, but when we wait 10 seconds there’s no notification popping up. The problem here is that forcing an update will complete the cache instance, meaning we no longer receive values in the component. The notification stream (initialNotifications$) is basically dead. That’s unfortunate, so how can we fix this?

+

Quite easy! We listen for events on forceReload$ and for every value we switch to a new notification stream. Important here is that we unsubscribe from the previous stream. Does that ring a bell? Sounds a lot like we need switchMap here, doesn’t it?

+

Let’s get our hands dirty and implement this!

+
import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+import { merge } from 'rxjs/observable/merge';
+import { take, switchMap, mergeMap, skip, mapTo } from 'rxjs/operators';
+
+@Component({
+  ...
+})
+export class JokeListComponent implements OnInit {
+  showNotification$: Observable<boolean>;
+  update$ = new Subject<void>();
+  forceReload$ = new Subject<void>();
+  ...
+
+  ngOnInit() {
+    ...
+    const reload$ = this.forceReload$.pipe(switchMap(() => this.getNotifications()));
+    const initialNotifications$ = this.getNotifications();
+    const show$ = merge(initialNotifications$, reload$).pipe(mapTo(true));
+    const hide$ = this.update$.pipe(mapTo(false));
+    this.showNotification$ = merge(show$, hide$);
+  }
+
+  getNotifications() {
+    return this.jokeService.jokes.pipe(skip(1));
+  }
+  ...
+}
+

That’s it. Whenever forceReload$ emits a value we unsubscribe from the previous Observable and switch to a new notification stream. Note that there’s a piece of code that we needed twice, that is this.jokeService.jokes.pipe(skip(1)). Instead of repeating ourselves, we created a function getNotifications() that simply returns a stream of jokes but skips the first value. Finally, we merge both initialNotifications$ and reload$ together into one stream called show$. This stream is responsible for showing the notification on the screen. There’s also no need to unsubscribe from initialNotifications$ because this stream completes before the cache is re-created on the next subscription. The rest stays the same.

+

Puh, we did it. Let’s take a moment and look at a more visual representation of what we just implemented.

+

+ + + notification cache + +

+

As we can see in the marble diagrams, initialNotifications$ is very important for showing notifications. If we were missing this particular stream then we would only see a notification when we force the cache to update. That said, when we request new data on demand, we have to constantly switch to a new notification stream because the previous (old) Observable will complete and no longer emit values.

+

That’s it! We’ve made it and implemented a sophisticated caching mechanism using RxJS and the tools provided by Angular. To recap, our service exposes a stream that gives us a list of jokes. The underlying HTTP request is periodically triggered every 10 seconds to update the cache. To improve the user experience, we show a notification so that the user has to enforce an update of the UI. On top of that, we have implemented a way for the user to request new data on demand.

+

Awesome! Here’s the final solution. Take a few minutes to review the code once again. Try out different scenarios to see if everything works as expected.

+ +

Outlook

+

If you want some homework or brain food for later, here are some thoughts for improvements:

+
    +
  • Add error handling
  • +
  • Refactor the logic from the component into a service to make it reusable
  • +
+

Special Thanks

+

Special thanks to Kwinten Pisman for helping me with the code. Also, I’d like to thank Ben Lesh and Brian Troncone for giving me valuable feedback and pointing out a few improvements. In addition, big thanks to Christoph Burgdorf for reviewing my article as well as the code.

Written by  Author

Dominic Elm

\ No newline at end of file diff --git a/angular/2019/03/11/using-dynamic-angular-components-inside-your-custom-widgets.html b/angular/2019/03/11/using-dynamic-angular-components-inside-your-custom-widgets.html new file mode 100644 index 000000000..7fd6003b3 --- /dev/null +++ b/angular/2019/03/11/using-dynamic-angular-components-inside-your-custom-widgets.html @@ -0,0 +1,161 @@ +Dynamic Angular components inside custom widgets | Articles by thoughtram

Angular

Dynamic Angular components inside custom widgets

In my previous article I showed how you can integrate a third party widget with Angular using as an example a datagrid widget by ag-Grid. Most widgets you’ll find on the web are customizable and ag-Grid is not an exception. In fact, here at ag-Grid we strongly believe that developers should be able to easily extend the default functionality to meet their business requirements. For example, you can provide your custom components to implement a custom cell renderer, cell editor or custom filters.

+

Pure JavaScript version of ag-Grid is extended by implementing a JavaScript component - a class that implements methods for communication between ag-Grid and the component. For example, all components that implement some kind of UI element must implement getGui method that returns a DOM hierarchy that our JavaScript datagrid will render on the screen.

+

However, when ag-Grid is used inside Angular, we don’t directly work with DOM. In Angular, we define UI for a component through a template and delegate DOM manipulation to Angular. And this is exactly the possibility we want to provide for someone who wants to customize ag-Grid that is used as an Angular component. We want to allow our users to customize parts of our Angular datagrid by implementing Angular components.

+

Let me give you an example. A developer uses ag-Grid as an Angular component and wants to implement a requirement to format all numbers in cells according to a user’s locale (EUR). To implement this formatting logic in the pure JavaScript grid version, the developer needs to wrap this logic into a JavaScript class and implement the getGui method that returns the DOM with formatted values. This will be a component that ag-Grid will use for cell rendering, hence the type of a component is defined in the docs as a cell renderer. Here is how it could look:

+
class NumberCellFormatter {
+    init(params) {
+        const text = params.value.toLocaleString(undefined, {style: 'currency', currency: 'EUR'});
+        
+        this.eGui = document.createElement('span');
+        this.eGui.innerHTML = text;
+    }
+
+    getGui() {
+        return this.eGui;
+    }
+}
+

But when ag-Grid is used as an Angular datagrid, we want developers to define a cell render in Angular way like this:

+
@Component({
+    selector: 'app-number-formatter-cell',
+    template: `
+        <span>{{params.value | currency:'EUR'}}</span>
+    `
+})
+export class NumberFormatterComponent {
+    params: any;
+
+    agInit(params: any): void {
+        this.params = params;
+    }
+}
+

Also, as you can see, if a customization component is defined as an Angular component, it can take advantage of built-in Angular mechanisms, like the currency pipe to format values.

+

To make it possible for developers to use customization components implemented as Angular components, we need to use the dynamic components mechanism provided by the framework. Since the DOM of ag-Grid is not controlled by Angular, we need the possibility to retrieve the DOM rendered by Angular for a customization component and render that DOM in an arbitrary place inside the grid. There are many other architectural pieces required to enable developers to customize the grid through Angular components and we’ll now take a look briefly at the most important ones. Here’s the explanation of how we implemented this mechanism in ag-Grid.

+

Implementation

+

We represent each customization component using a generic wrapper component DynamicAgNg2Component. This component keeps a reference to the original customization Angular component implemented by a developer. When ag-Grid needs to instantiate an original component, it creates an instance of the generic DynamicAgNg2Component that’s responsible for using dynamic components mechanism to instantiate an Angular component. Once it obtains the reference to the instantiated dynamic component, it retrieves the DOM created by Angular and assigns it to the _eGui property of the wrapper component. The DynamicAgNg2Component component also implements the getGui method that ag-Grid uses to obtain the DOM from a customization component. Here we simply returned the DOM retrieved from a dynamic Angular component.

+

Here’s how it all looks in code. The DynamicAgNg2Component component extends the BaseGuiComponent it delegates all work to the init method of the class:

+
class DynamicAgNg2Component extends BaseGuiComponent {
+    init(params) {
+           _super.prototype.init.call(this, params);
+           this._componentRef.changeDetectorRef.detectChanges();
+    };
+    ...
+}
+

Inside the init method of the BaseGuiComponent is where a dynamic component is initialized and the DOM is retrieved. Once everything is setup, we run change detection manually once and forget about it.

+

The BaseGuiComponent implements a few required methods for the communication with ag-Grid. Particularly, it implements the getGui method that ag-Grid uses to obtain the DOM that needs to be rendered inside the grid:

+
class BaseGuiComponent {
+    protected init(params: P): void { ... }
+
+    public getGui(): HTMLElement {
+        return this._eGui;
+    }
+}
+

As you can see, the implementation of the getGui is very trivial. We simply return the value of the _eGui property. This property holds the DOM created for a dynamic component by Angular. When we dynamically instantiate a component, we obtain its DOM and assign it to the _eGui property. This happens in the init method.

+

Before we take a look at the implementation of the method, let’s remember that to dynamically instantiate components in Angular we need to get a factory for Angular components. The factory can be obtained using a ComponentFactoryResolver. That’s why we inject it to the main AgGridNg2 when the component is initialized:

+
@Component({
+    selector: 'ag-grid-angular',
+    ...
+})
+export class AgGridNg2 implements AfterViewInit {
+    ...
+    constructor(private viewContainerRef: ViewContainerRef,
+                private frameworkComponentWrapper: Ng2FrameworkComponentWrapper,
+                private _componentFactoryResolver: ComponentFactoryResolver, ...) {
+        ...
+        this.frameworkComponentWrapper.setViewContainerRef(this.viewContainerRef);
+        this.frameworkComponentWrapper.setComponentFactoryResolver(this._componentFactoryResolver);
+    }
+}
+

We also inject ViewContainerRef and Ng2FrameworkComponentWrapper services. The latter is used to wrap an original customization component provided by a developer into the DynamicAgNg2Component. The view container is used to render DOM and make change detection automatic. We run change detection manually only once in the init method of the DynamicAgNg2Component once the component is rendered. By injecting ViewContainerRef into the AgGridNg2 we turn this top level component a container and all dynamic customization components are attached to this container. When Angular checks the top-level AgGridNg2 component, all customization components are checked automatically as part of change detection process.

+

Let’s now take a closer look at the init method:

+
class BaseGuiComponent {
+    protected init(params: P): void {
+        this._params = params;
+
+        this._componentRef = this.createComponent();
+        this._agAwareComponent = this._componentRef.instance;
+        this._frameworkComponentInstance = this._componentRef.instance;
+        this._eGui = this._componentRef.location.nativeElement;
+
+        this._agAwareComponent.agInit(this._params);
+    }
+
+    ...
+}
+

Basically, inside the createComponent we delegate the call to the createComponent method of the Ng2FrameworkComponentWrapper. As you remember, this service keeps the references to the ViewContainerRef and componentFactoryResolver that were attached to it during the instantiation of AgGridNg2. In the createComponent method it uses them to resolve a factory for the customization component and instantiate the component:

+
export class Ng2FrameworkComponentWrapper extends ... {
+    ...
+    public createComponent<T>(componentType: { new(...args: any[]): T; }): ComponentRef<T> {
+        let factory = this.componentFactoryResolver.resolveComponentFactory(componentType);
+        return this.viewContainerRef.createComponent(factory);
+    }
+}
+

Then using the component reference we get the DOM and attach it to the eGui private property:

+
this._componentRef = this.createComponent();
+this._agAwareComponent = this._componentRef.instance;
+this._frameworkComponentInstance = this._componentRef.instance;
+this._eGui = this._componentRef.location.nativeElement;
+

And that’s it. If you’re interested to learn how we implemented the component resolution process, continue reading.

+

Component resolution process

+

ag-Grid is a very complex piece of software. To simplify things internally we’ve designed and implemented our own Dependency Injection (IoC) system that’s modeled after Spring’s IoC container and beans. The component resolution process requires a bunch of services that are registered in this DI system. The most important ones are ComponentResolver and ComponentProvider. Also, we need the Ng2FrameworkComponentWrapper service that is specific for ag-Grid used as Angular wrapper. It’s registered in the DI system using frameworkComponentWrapper token.

+

Resolution is performed through the ComponentResolver service. When the resolver is instantiated, the frameworkComponentWrapper and componentProvider services are attached to the resolver through the DI system and are available on the class instance:

+
@Bean('componentResolver')
+export class ComponentResolver {
+    @Autowired("gridOptions")
+    private gridOptions: GridOptions;
+
+    @Autowired("componentProvider")
+    private componentProvider: ComponentProvider;
+
+    @Optional("frameworkComponentWrapper")
+    private frameworkComponentWrapper: FrameworkComponentWrapper;
+
+    ...
+}
+

When the grid needs to instantiate a particular type of a component, e.g. a cell renderer, it calls ComponentResolver.createAgGridComponent method. The method uses a descriptor of a column to obtain the name of a component that needs to be created. For the cell renderer component the property that contains the name of a component is cellRenderer:

+
let columnDefs = [
+    {
+        headerName: 'Price',
+        field: 'price',
+        editable: true,
+        cellRenderer: 'numberFormatterComponent'
+    },
+    ...
+]
+

Once the name is obtained, it is used to retrieve the component class and metadata from the componentProvider:

+
export class ComponentResolver {
+    private resolveByName(propertyName, ...) {
+        const componentName = componentNameOpt != null ? componentNameOpt : propertyName;
+        const registeredComponent = this.componentProvider.retrieve(componentName);
+        ...
+    }
+}
+

The retrieve method returns the following descriptor of a component:

+
{
+    component: NumberFormatterComponent
+    dynamicParams: null
+    source: 1
+    type: Component_Type.Framework
+}
+

The type of a component denotes that it’s a framework specific component. All framework components are wrapped into the DynamicAgNg2Component as explained the first section of the article. Once the component is wrapped, it contains the getGui method common to all customization components and ag-Grid can work with it as if it’s a plain JavaScript component.

Written by  Author

Maxim Koretskyi

\ No newline at end of file diff --git a/angular2/2015/11/23/multi-providers-in-angular-2.html b/angular2/2015/11/23/multi-providers-in-angular-2.html new file mode 100644 index 000000000..4ae5a18e4 --- /dev/null +++ b/angular2/2015/11/23/multi-providers-in-angular-2.html @@ -0,0 +1,111 @@ +Multi Providers in Angular | Articles by thoughtram

Angular2

Multi Providers in Angular

The new dependency injection system in Angular comes with a feature called “Multi Providers” that basically enable us, the consumer of the platform, to hook into certain operations and plug in custom functionality we might need in our application use case. We’re going to discuss what they look like, how they work and how Angular itself takes advantage of them to keep the platform flexible and extendible.

+

Recap: What is a provider?

+

If you’ve read our article on Dependency Injection in Angular you can probably skip this section, as you’re familiar with the provider terminology, how they work, and how they relate to actual dependencies being injected. If you haven’t read about providers yet, here’s a quick recap.

+

A provider is an instruction that describes how an object for a certain token is created.

+

Quick example: In an Angular component we might have a DataService dependency, which we can ask for like this:

+
import { DataService } from './data.service';
+
+@Component(...)
+class AppComponent {
+
+  constructor(dataService: DataService) {
+    // dataService instanceof DataService === true
+  }
+}
+

We import the type of the dependency we’re asking for, and annotate our dependency argument with it in our component’s constructor. Angular knows how to create and inject an object of type DataService, if we configure a provider for it. This can happen either on the application module, that bootstrap our app, or in the component itself (both ways have different implications on the dependency’s life cycle and availability).

+
// application module
+@NgModule({
+  ...
+  providers: [
+    { provide: DataService, useClass: DataService }
+  ]
+})
+...
+
+// or in component
+@Component({
+  ...
+  providers: [
+    { provide: DataService, useClass: DataService }
+  ]
+})
+class AppComponent { }
+

In fact, there’s a shorthand syntax we can use if the instruction is useClass and the value of it the same as the token, which is the case in this particular provider:

+
@NgModule({
+  ...
+  providers: [DataService]
+})
+...
+
+// or in component
+@Component({
+  ...
+  providers: [DataService]
+})
+class AppComponent { }
+

Now, whenever we ask for a dependency of type DataService, Angular knows how to create an object for it.

+

Understanding Multi Providers

+

With multi providers, we can basically provide multiple dependencies for a single token. Let’s see what that looks like. The following code manually creates an injector with multi providers:

+
const SOME_TOKEN: OpaqueToken = new OpaqueToken('SomeToken');
+
+var injector = Injector.create([
+  { provide: SOME_TOKEN, useValue: 'dependency one', multi: true },
+  { provide: SOME_TOKEN, useValue: 'dependency two', multi: true }
+]);
+
+var dependencies = injector.get(SOME_TOKEN);
+// dependencies == ['dependency one', 'dependency two']
+

Note: We usually don’t create injectors manually when building Angular applications since the platform takes care of that for us. This is really just for demonstration purposes.

+

A token can be either a string or a type. We use a string, because we don’t want to create classes to represent a string value in DI. However, to provide better error messages in case something goes wrong, we can create our string token using OpaqueToken. We don’t have to worry about this too much now. The interesting part is where we’re registering our providers using the multi: true option.

+

Using multi: true tells Angular that the provider is a multi provider. As mentioned earlier, with multi providers, we can provide multiple values for a single token in DI. That’s exactly what we’re doing. We have two providers, both have the same token but they provide different values. If we ask for a dependency for that token, what we get is a list of all registered and provided values.

+

Okay understood, but why?

+

Alright, fine. We can provide multiple values for a single token. But why in hell would we do this? Where is this useful? Good question!

+

Usually, when we register multiple providers with the same token, the last one wins. For example, if we take a look at the following code, only TurboEngine gets injected because it’s provider has been registered at last:

+
class Engine { }
+class TurboEngine { }
+
+var injector = Injector.create([
+  { provide: Engine, deps: []},
+  { provide: Engine, useClass: TurboEngine, deps: [] }
+]);
+
+var engine = injector.get(Engine);
+// engine instanceof TurboEngine
+

This means, with multi providers we can basically extend the thing that is being injected for a particular token. Angular uses this mechanism to provide pluggable hooks.

+

One of these hooks for example are validators. When creating a validator, we need to add it to the NG_VALIDATORS multi provider, so Angular picks it up when needed

+
@Directive({
+  selector: '[customValidator][ngModel]',
+  providers: [
+    provide: NG_VALIDATORS,
+    useValue: (formControl) => {
+      // validation happens here
+    },
+    multi: true
+  ]
+})
+class CustomValidator {}
+

Multi providers also can’t be mixed with normal providers. This makes sense since we either extend or override a provider for a token.

+

Other Multi Providers

+

The Angular platform comes with a couple more multi providers that we can extend with our custom code. At the time of writing these were

+
    +
  • NG_VALIDATORS - Interface that can be implemented by classes that can act as validators
  • +
  • NG_ASYNC_VALIDATORS - Token that can be implemented by classes that can act as async validators
  • +
+

Conclusion

+

Multi providers are a very nice feature to implement pluggable interface that can be extended from the outside world. The only “downside” I can see is that multi providers only as powerful as what the platform provides. NG_VALIDATORS and NG_ASYNC_VALIDATORS are implemented right into the platform, which is the only reason we can take advantage of those particular multi providers. There’s no way we can introduce our own custom multi providers (with a specific token) that influences what the platform does, but maybe this is also not needed.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html b/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html new file mode 100644 index 000000000..ee6cee11b --- /dev/null +++ b/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html @@ -0,0 +1,55 @@ +One-time bindings in Angular 1.3 | Articles by thoughtram

Angularjs

One-time bindings in Angular 1.3

The time has come. Angular 1.3 is finally out and it comes with tons of new features, bug fixes, improvements but also breaking changes. And because of all this new stuff happening there, we thought it would make sense to help making the adaption of this release easier for all of us, by exploring its main features and improvements and make a blog series out of it. This is the first post of “Exploring Angular 1.3” and it covers one of the most important features ever: one-time binding.

+

We’ve written a few other articles on 1.3 already. Here’s a list:

+ +

Wait! Isn’t this Angular thing about databinding that automatically keeps the UI in sync? Well, yes it is and that’s great. However, Angulars implementation of databinding requires the framework to keep an eye on all values that are bound. This can lead to performance issues and one-time bindings are here to help. But before we explore one-time bindings, let’s understand Angulars concepts of databinding and watchers first.

+

Understanding data-binding and watchers

+

In order to make databinding possible, Angular uses $watch APIs to observe model mutations on the scope. What the scope actually is and where it comes from, depends on your application code. If you don’t create a child scope by, for example, using the ngController directive to create an association between your DOM and your actual controller code, you’re probably dealing with the $rootScope, which is (as the name says) the scope that acts as root scope for your application and created by Angular itself through the ngApp directive, unless you bootstrap your app manually.

+

However, at some point you always deal with a scope and that one is used to observe changes on it with the use of so called watchers. Watchers are registered through directives that are used in the DOM. So let’s say we use the interpolation directive to reflect scope model values in the DOM:

+
<p>Hello {{name}}!</p>
+

This interpolation directive registers a watch for a property name on the corresponding scope (which in our case is $rootScope) in order to interpolate against it to display the value in the DOM.

+

Defining a property with exactly that identifier on our scope and assigning a value to it, makes it magically displaying it in the DOM without further actions:

+
angular.module('myApp', [])
+.run(function ($rootScope) {
+  $rootScope.name = "Pascal";
+});
+

Great! We just bound a model value to the view with an interpolation directive. If now something changes the value, the view gets updated automatically. Let’s add a button that updates the value of name once it’s clicked:

+
<button ng-click="name = 'Christoph'">Click me!</button>
+

Clicking the button assigns the string Christoph to name which triggers a $digest cycle that automatically updates the DOM accordingly. In this particular case we’re just updating the value one-way (top → down). However, when for example dealing with an input element that has an ngModel directive applied, and a user changes its value property by typing something into it, the change is reflected back to the actual model.

+

This happens because when a $digest cycle is triggered, Angular processes all registered watchers on the current scope and its children and checks for model mutations and calls dedicated watch listeners until the model is stabilized and no more listeners are fired. Once the $digest loop finishes the execution, the browser re-renders the DOM and reflects the changes.

+

The problem with too many watchers

+

Now that we have a picture of how the databinding mechanism in Angular actually works, we might wonder why there is a feature for one-time binding.

+

Due to Angulars nature of using watchers for databinding, we might get some problems in terms of performance when having too many of them. As we learned, watch expressions are registered on the scope together with their callback listeners so Angular can process them during $digest cycles in order to update the view accordingly. That simply means, the more watchers are registered, the more Angular has to process.

+

Now imagine you have a lot of dynamic values in your view that have to be evaluated by Angular. Internationalization for example, is a very common use case where developers use Angulars databinding to localize their apps, even if the language isn’t changeable during runtime, but set on initial page load. In that case every single string that is localized in the view and written to the scope, sets up a watch in order to get updated once something triggers the next $digest. This is a lot of overhead especially when your language actually doesn’t change at runtime.

+

One-time bindings to the rescue!

+

This is where one-time bindings come in. So what are one-time bindings? Let’s just read what the official docs say:

+
+

One-time expressions will stop recalculating once they are stable, which happens after the first digest…

+
+

And this is exactly what the Angular world needs to tackle the problems mentioned above. So what does it look like when we want to use one-time binding? Angular 1.3 comes with a new syntax for interpolation directives and expressions in order to tell Angular that this particular interpolated value should be bound one-time.

+

Using this new syntax is as easy as starting an expression with ::. So if we apply the one-time expression to our example above, we change this:

+
<p>Hello {{name}}!</p>
+

To this:

+
<p>Hello {{::name}}!</p>
+

This works for all kind of typical Angular expressions you’re used to use throughout your app. Which means you can use them in ng-repeat expressions or even for directives that expose attributes that set up a two-way binding from the inside out. From the outside you’re able to just feed them with a one-time expression:

+
<custom-directive two-way-attribute="::oneWayExpression"></custom-directive>
+

Okay, let’s see it in action. We already updated the name to ::name to ensure the one-time binding. The rest of the code just stays as it is to demonstrate that our one-time binding works. Remember the button we added to update the name to Christoph? Well, try it again.

+

Perfect. name won’t ever change again. Pascal is a much better name anyway, right?

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/10/19/exploring-angular-1.3-ng-model-options.html b/angularjs/2014/10/19/exploring-angular-1.3-ng-model-options.html new file mode 100644 index 000000000..0f0aa9c2b --- /dev/null +++ b/angularjs/2014/10/19/exploring-angular-1.3-ng-model-options.html @@ -0,0 +1,68 @@ +ng-model-options in Angular 1.3 | Articles by thoughtram

Angularjs

ng-model-options in Angular 1.3

Hi again. This is the second article of “Exploring Angular 1.3”. If you haven’t read the first one you might want to check out that too. In this article, we cover another feature that turns out to be very useful in our daily development of Angular applications. Introducing the ngModelOptions directive. +We’ve written a few other articles on 1.3 already. Here’s a list:

+ +

ngModelOptions allows us to control how ngModel updates are done. This includes things like updating the model only after certain events are triggered or a debouncing delay, so that the view value is reflected back to the model only after a timer expires. To get an idea of what that actually means, let’s start with the probably simplest use case that sets up a two-way binding using an input element that has a ngModel directive applied:

+
<input type="text" ng-model="name">
+<p>Hello {{name}}!</p>
+

Now, when typing something into the input element, the model gets updated accordingly and then reflected back to the view, which displays the value in our p element.

+

Magic. If you’re not familiar with what’s going on here, I recommend heading over to the official docs and reading the chapter about the concepts of Angular.

+

The reason why the view is updated immediately, is that every time the input element fires an input event, Angulars $digest loop is executed until the model stabilizes. And that’s nice because we don’t have set up any event listeners and update the DOM manually to reflect model values in the view; Angular takes care of that.

+

However, that also means that, because of the $digest that happens to be triggered on every single keystroke, Angular has to process all registered watchers on the scope whenever you type something into the input element. Depending on how many watchers are registered and of course how efficient the watcher callbacks are, this can be very expensive. So wouldn’t it be great if we could somehow manage to trigger a $digest only after the user stopped typing for, let’s say, 300 milliseconds? Or only when the user removes the focus of the input element?

+

Yes, and we can do so thanks to Angular 1.3 and the ngModelOptions directive.

+

Updating ngModel with updateOn

+

ngModelOptions comes with a couple of options to control how ngModel updates are done. With the updateOn parameter, we can define which events our input should be bound to. For example, if we want our model to be updated only after the user removed the focus of our input element, we can simply do so by applying the ngModelOptions with the following configuration:

+
<input
+  type="text"
+  ng-model="name"
+  ng-model-options="{ updateOn: 'blur' }">
+<p>Hello {{name}}!</p>
+

This tells Angular that instead of updating the model immediately after each keystroke, it should only update when the input fires an onBlur event.

+

If we do want to update the model with the default events that belong to that control and add other events on top of that, we can use a special event called default. Adding more then just one event can be done with a space delimited list. The following code updates the model whenever a user types into the input, or removes the focus of it.

+
<input
+  type="text"
+  ng-model="name"
+  ng-model-options="{ updateOn: 'default blur' }">
+<p>Hello {{name}}!</p>
+

Alright, now that we know how that works, let’s take a look at how we can update the model after a timer expires.

+

Delaying the model update with debounce

+

We can delay the model update with ngModelOptions in order to reduce the amount of $digest cycles that are going to be triggered when a user interacts with our model. But not only that this ensures fewer $digest cycles, it’s also a powerful feature that can be used to implement a nice user experience when dealing with asynchronous code execution.

+

Just imagine an input[type="search"] element, where every time a user types into the field, the model gets updated and an asynchronous request is made to a server to get a response with search results depending on the given query. This works. However, we probably don’t want to update the model on every keystroke but rather once the user has finished typing a meaningful search term. We can do exactly that with ngModelOptionsdebounce parameter.

+

debounce defines an integer value which represents a model update delay in milliseconds. Which means, if we take the example mentioned above, that we want to update our model 300 milliseconds after the user stopped typing, we can do so by defining a debounce value of 300 like this:

+
<input
+  type="search"
+  ng-model="searchQuery"
+  ng-model-options="{ debounce: 300 }">
+<p>Search results for: {{searchQuery}}</p>
+

Now, when typing into the input field, there’s a slight delay until the model updates.

+

We can go even further and configure how the update delay should be done for certain events. Controlling the debounce delay for specific events can be done by defining an object literal instead of a primitive integer value, where keys represent the event name and values the debounce delay. A delay of 0 triggers an immediate model update.

+

The following code generates a model update delay of 300 milliseconds when the user types into our input, but an immediate update when removing the focus:

+
<input
+  type="search"
+  ng-model="searchQuery"
+  ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 300, 'blur': 0 } }">
+<p>Search results for: {{searchQuery}}</p>
+

Super powerful right? There are a few other options that are worth to checkout out. You can read about them in the official docs.

+

Synchronizing model and view with $rollbackViewValue

+

Due to the fact that we are able to control with ngModelOptions how and when model updates are done, the model and the view can get out of sync. For example, when we configure our input element to update the model only when it loses its focus, the moment when the user types into the field, the input value differs from the actual value in the model.

+

There might be situations, where you want to roll the view value back to what it was, before the change has been made. For such cases, Angular introduces a so called $rollbackViewValue method that can be invoked to synchronize the model and view. Basically what this method does is, it takes the value that is currently in the model and reflects it back to the view. In addition, it cancels all debounced changes.

+

To demonstrate this use case, we can setup a form that has an input element that updates the model when the user removes the focus. As long as the user didn’t remove the focus of the input element, he can hit the Esc key to discard his changes and get the value of the model back.

+

So it turns out that ngModelOptions is a super powerful directive that helps us making our apps more intuitive. Go and check out the docs about the allowInvalid and getterSetter options, to see what else is possible!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/11/06/exploring-angular-1.3-angular-hint.html b/angularjs/2014/11/06/exploring-angular-1.3-angular-hint.html new file mode 100644 index 000000000..235d1bfe9 --- /dev/null +++ b/angularjs/2014/11/06/exploring-angular-1.3-angular-hint.html @@ -0,0 +1,86 @@ +Angular-hint in Angular 1.3 | Articles by thoughtram

Angularjs

Angular-hint in Angular 1.3

With the release of version 1.3, the Angular project comes with a new module called angular-hint, that makes debugging and finding mistakes in the code base easier. It also gives us hints about best practices, so that our code is more maintainable and easier to read. In this article we’ll take a brief look at this module and explore how we can actually use it and what great features it provides. As a side note, this is the third article of our ”Exploring Angular 1.3” series, so you might want to check out our articles about ngModelOptions and one-time bindings too. Okay, let’s dive into the actual topic.

+

As already mentioned, angular-hint helps us writing better Angular code and makes finding very common mistakes in our code base easier. For example, did it ever happen to you, that you developed your Angular app, you grabbed a module from somewhere, then you started using the directives that the module comes with, and no matter how much you followed the usage instructions, it simply didn’t work. And after one hour of debugging you found out that you forgot to add the module dependency to your application. Yikes!

+

But let me tell you something. With angular-hint, these times are over. And that’s just one use case where angular-hint helps out. In fact, angular-hint comes with a couple of other sub modules for particular use cases.

+

These modules are:

+ +

Let’s start right away and see what the usage of angular-hint looks like.

+

Install and using angular-hint

+

Using angular-hint is super easy, since all we have to do is to install it via npm, embed the source in our application and use the ng-hint directive that takes care of the rest. Alright, so let’s install the module via npm:

+
$ npm install angular-hint
+

The angular-hint module declares all the sub modules (angular-hint-directives, angular-hint-controllers, …) as dependency, so you don’t have to care about installing them manually. The command above does the job for you. Also, the package comes with a pre-compiled hint.js file that contains the source of all mentioned angular-hint-* modules, so you can use it right away.

+

Once it’s installed, we can embed the source in our application right after Angular itself like this:

+
<script type="path/to/angular/angular.js"></script>
+<script type="path/to/angular-hint/hint.js"></script>
+

Next, we apply the ng-hint directive in order to actually use the angular-hint module:

+
<body ng-app="myApp" ng-hint>
+</body>
+

That’s it. We’re done. It’s that easy.

+

Applying the ng-hint directive to our document takes care of injecting all needed hint modules in your apps bootstrap phase. But how does ng-hint know, which hint modules we actually want to activate? By default, ng-hint injects all the mentioned hint modules. However, if we don’t want to get controller related hints, but are interested in DOM related hints, we can restrict the use of hint modules by using the ng-hint-include directive instead. The following code only injects angular-hint-dom:

+
<body ng-app="myApp" ng-hint-include="dom">
+</body>
+

We can even define more than just one hint module if needed:

+
<body ng-app="myApp" ng-hint-include="dom directives">
+</body>
+

As you can see, the names used as value for ng-hint-include map to the suffixes of the actual hint module names. E.g. dom and directives map to angular-hint-dom and angular-hint-directives respectively.

+

Module hints

+

Okay, so now that angular-hint is installed, let’s try to reproduce the “I forgot to add module dependency” scenario we were talking about. To do that, we declare an additional Angular module that act as our app dependency. For simplicity’s sake, we don’t add any ground breaking functionality here.

+
angular.module('myAppDependency', []);
+

Next we take a look at our actual app module definition. As you can see, we declare the module without any further dependencies a.k.a we simply forgot it.

+
angular.module('myApp', []);
+

Now, instead of fiddling around for an hour to find out why myAppDependency’s directives aren’t picked up, angular-hint is telling us that we might missed something. Simply open your browsers console and you should see something like this:

+
Angular Hint: Modules
+  Module "myAppDependency" was created but never loaded.
+

This log occurs whenever an Angular module is present but not declared as dependency anywhere (You might see another message says that ngHintModules was also created but never loaded. This is a probably a bug and filed here).

+

There are a couple more things that this module tries to warn you about and you can read about them here.

+

Controller hints

+

If there’s one thing you should embrace when working on a bigger Angular app and especially when working in a bigger team, are best practices. The Angular team published their style guide for apps built with Angular internally at Google, that covers best practices and conventions.

+

One of these best practices is, when naming controllers, to suffix them with Controller instead of using short names like Ctrl. angular-hint helps with that too. Let’s take a look what happens when we define a controller with a name that doesn’t have this suffix:

+
angular.module('myApp', []).controller('AppCtrl', function () {
+
+});
+

Having a controller registered like this, angular-hint gives us the following warning:

+
Angular Hint: Controllers
+  The best practice is to name controllers ending with 'Controller'.
+  Check the name of 'AppCtrl'
+

I think this makes pretty much clear what can be achieved with such a tool. Having a style guide with conventions and best practices that everybody agrees on, makes a projects structure easier to understand and debug. With angular-hint we actually have a tool to embrace and encourage these best practices and conventions and this is just the start!

+

Directive hints

+

When dealing with directives, there are a couple of things that can go wrong and stop us from being productive because we have to debug (once again) why a given directive doesn’t seem to work. Similar to the example where we forgot to add our module dependency, it also happens quiet often, that we misspell directive names. When a directive name is misspelled, for Angular, this is just an attribute (or element) that it doesn’t know about, so it gets completely ignored.

+

Just take a look at this small snippet:

+
<ul>
+  <li ng-repaet="i in [1,2,3,4]">
+    <!-- more dom goes here -->
+  </li>
+</ul>
+

As you can see, there’s a typo in the directive name. We actually wanted to type ng-repeat, but we typed ng-repaet. I can easily remember the last time, when I was debugging for an hour because I just misspelled a directive name. Because literally nothing happens.

+

However, when angular-hint is activated, we get the following very useful warning:

+
Angular Hint: Directives
+  There was an AngularJS error in LI element.
+  Found incorrect attribute "ng-repaet" try "ng-repeat"
+

How cool is that? Not only that angular-hint warns me about an incorrect directive name and on what kind of element it is applied, it also suggests me a directive that is actually registered and to use! And now think about how much time you can save with such a helper.

+

Conclusion

+

Even if we took a look at just a few of all provided angular-hint modules and features, I’m pretty sure you get an idea how useful it is. Of course, it’s still in early development and has one or the other bug here and there, but the initial idea of having such a module that you can easily apply and tells you about all your possible mistakes, is just awesome.

+

And again, this is just the start. I can’t even think of all the possibilities we have when extending the hint modules with additional functionality and hints. I could also imagine that this module could help out migrating from 1.x to 2.x in the future once the APIs are stable.

+

However, you should go and try it out in your apps today. If you find any bugs, make sure to file them at the dedicated repositories on GitHub, or fix them directly and send a pull request. Also, don’t hesitate to come up with new ideas on how the module could be extended. It’ll make all our lives easier!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/11/19/exploring-angular-1.3-stateful-filters.html b/angularjs/2014/11/19/exploring-angular-1.3-stateful-filters.html new file mode 100644 index 000000000..19798df44 --- /dev/null +++ b/angularjs/2014/11/19/exploring-angular-1.3-stateful-filters.html @@ -0,0 +1,70 @@ +Stateful filters in Angular 1.3 | Articles by thoughtram

Angularjs

Stateful filters in Angular 1.3

Angular 1.3 comes with a lot of cool features and improvements. We already covered a couple of them. You can for example read about one-time bindings, ngModelOptions or the newly introduced Angular-hint module that helps you out writing better Angular code.

+

However, it turns out that, even if the 1.3 release looks like a feature release, it comes with a change that might break your existing code. It handles all filters stateless by default and in this article we’re going to take a look at what this means and how we can deal with that.

+

The filter behaviour you know

+

I think I don’t have to go into much detail when it comes to how to use filters in general. We apply them with a | symbol in our interpolation expressions and are able to pass additional parameters by chaining them with a : symbol.

+

For instance, in the following example, we use the json filter to convert a JavaScript object into a JSON string:

+
{{ jsonExpression | json }}
+

Expecting jsonExpression to look something like: {'name':'value'}, the filter would return a string that looks something like this:

+
{
+  "name": "value"
+}
+

If we use a filter that can be configured with additional parameters, we can pass them to the filter by chaining them with a : symbol right in the expression. The following example uses the currency filter to format the given expression accordingly and uses an optional symbol parameter to change the default currency symbol for this particular use case.

+
{{ amount | currency:'€' }}
+

Okay, so this is pretty straight forward right? A filter takes an expression and uses it as input to manipulate the expressions value and returns (ideally) a string, so it can be used in our HTML right away.

+

We can go even further and build a custom filter that depends on another service to manipulate the given input. Let’s assume we have a filter like this:

+
angular.module('myApp', [])
+
+.filter('customFilter', ['someService', function (someService) {
+  return function customFilter(input) {
+    // manipulate input with someService
+    input += someService.getData();
+    return input;
+  };
+}]);
+

In this example we have a filter that depends on someService and this particular service apparently comes with a method getData(), which is used to change the value of input that gets returned.

+

Again, this is pretty straight forward. Filters in Angular follow the same rules as other component types like services, factories etc, when it comes to dependency injection. So basically it’s totally fine and valid to have dependencies in filter components. However, filters that have dependencies are usually stateful. And this is where the code might break.

+

But what does that mean? And why is it a problem at all? Well, let’s take a look at what changed in Angular 1.3, so we get a better idea of what causes problems.

+

Filters in 1.3

+

In order to make Angular faster, a lot of changes landed in the 1.3 release that come with performance improvements. One of them is how filters behave by default. We talked about what filters do and how we can use them, but we didn’t talk about the fact, that they always came with a relatively big drawback. Each filter creates a new watcher. Having to many watchers registered can slow down our app, since the more watchers are registered, the more work has to be done during the $digest cycle. That’s why we usually should avoid using too many filters.

+

However, there’s a reason why Igor Minar said that:

+
+

“Angular 1.3 is the best Angular yet!”

+
+

In version 1.3, filters are much smarter. By default, they cache the evaluated value so they don’t have to be re-evaluated all the time. Getting back to our simple {% raw %}{{ jsonExpression | json}}{% endraw %} example, the expression only gets re-evaluated when jsonExpression changes, which makes our code execution much faster.

+

To make it work like this, Angular assumes that, as long as the passed expression doesn’t change, the result of the expression doesn’t change either. It’s stateless. And this is where our code might break. Think about what that means in cases where your filter depends on other services, like our customFilter.

+

But to get a better picture, let’s take a look at the translate filter that comes with the angular-translate module. It consumes translation ids to look them up in a registered translation table, using the $translate service and returns the dedicated translation. It is stateful.

+

Here’s what it looks like:

+
{{ 'TRANSLATIONID' | translate }}
+

As we already discussed, Angular caches the value of this expression and won’t re-evaluate it unless 'TRANSLATIONID' changes. This is actually a problem, because 'TRANSLATIONID' never changes. When the user changes the language, the given translation id again, is looked up by the filter in a translation table, but the expression stays the exact same.

+

So how do we tell Angular, that expressions that are stateful have to be re-evaluated? It’s easy. All we have to do is to add a $stateful property to our filter that flags it as stateful. Here we see our customFilter being flagged accordingly:

+
angular.module('myApp', [])
+
+.filter('customFilter', ['someService', function (someService) {
+  function customFilter(input) {
+    // manipulate input with someService
+    input += someService.getData();
+    return input;
+  }
+
+  customFilter.$stateful = true;
+
+  return customFilter;
+}]);
+

That’s it. Setting the $stateful property to true does the trick (angular-translate’s filter comes with that flag already). Keep in mind that it’s in general recommended to avoid building stateful filters, because the execution of those can’t be optimized by Angular. Better build stateless filters that get all needed information as parameters.

+

To sum it up, make sure to flag your stateful filters as stateful in order to make them work with Angular 1.3. Hopefully this article made clear why these changes are a requirement.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/12/18/exploring-angular-1.3-es6-style-promises.html b/angularjs/2014/12/18/exploring-angular-1.3-es6-style-promises.html new file mode 100644 index 000000000..60311dd7a --- /dev/null +++ b/angularjs/2014/12/18/exploring-angular-1.3-es6-style-promises.html @@ -0,0 +1,123 @@ +ES6 Style Promises in Angular 1.3 | Articles by thoughtram

Angularjs

ES6 Style Promises in Angular 1.3

We mainly took a look at completely new features that come with the Angular 1.3 release until now. Things like ngModelOptions, Angular-hint or One-time Bindings are not just minor improvements, but rather real extensions to the framework. However, there have not only been significant new features added to the release, but also a ton of bug fixes and nice little additions that we might have overlooked. One of them is the ES6 streamlined promise API and today we gonna take a look what it brings to the table.

+

Asynchronous worlds with Promises

+

In order to understand what the new streamlined addition to the existing promise API means, we first have to make sure we’re all on the same page and know what promises are and how they’ve been implemented in Angular before. We don’t want to go in too much detail here though, since there are a ton of resources in the interwebs, but let’s take a very quick look at promises and move on then.

+

In just one sentence, a promise is an object that is used for deferred and asynchronous computations. So what does that mean? Well, in JavaScript we can have asynchronous code execution with, for example, callbacks. And with these things we’re able to execute some code once another execution that ran before is done without blocking the actual code execution context. We call this asynchronous.

+

Here’s an example:

+
onceDone(function () {
+  // do something once `onceDone()` calls you
+});
+

Here we have a function onceDone() that expects a function that is executed, once the onceDone() function is done with its work and it doesn’t block the rest of the code that might be there.

+

Okay, that’s clear. But where and how come promises into play? There’s a scenario that JavaScript developers love to call ”callback hell”. Callback hell is something that we have, when we nest a lot of functions in JavaScript that are asynchronous and have callbacks to be executed. Just take a look at the following code snippet.

+
onceDone(function (files) {
+  files.forEach(function (filename, fileIndex) {
+    filename.size(function (err, values) {
+      values.width.forEach(function (value) {
+        // ... and so on
+      });
+    });
+  });
+});
+

You get the idea right? While it is very common to have function calls that get a function callback, it can lead to very hard to get right code when we have to nest a lot of these. I recommend to head over to callbackhell.com to get a better picture, if things are still unclear.

+

We can get around this issue by defining named functions first, and pass just these as callbacks instead of using anonymous functions all the time, but it’s still not a very handy way to handle asynchronous JavaScript. And this is where promises come in.

+

Promises are a software abstraction or proxies, that make working with asynchronous operations much more pleasant. Coming back to our onceDone() function example, here’s what the code would look like if onceDone() used promises.

+
var promise = onceDone();
+
+promise.then(function () {
+  // do something once `onceDone` calls you
+});
+

Looks very similar right? But there’s a huge difference. As you can see, onceDone() returns something that is a promise which we can treat as first-class objects. This promise holds the actual state of the asynchronous code that has been called, which can be fulfilled, rejected or pending. We can pass promises around and even aggregating them. That’s a whole different way of handling our asynchronous code.

+

What we also see, is that the promise has a method .then(). This method expects two parameters which are functions of which one gets called when the asynchronous execution was fulfilled and the other one when it was rejected.

+
var promise = functionThatReturnsAPromise();
+
+promise.then(fulfilledHandler, rejectedHandler);
+

As I mentioned, we can aggregate them. .then() also returns a promise that resolves with the return value of the executed handler (fulfilled or rejected). That enables us to chain promises like this:

+
var promise = functionThatReturnsAPromise();
+
+promise
+  .then(fulfilledHandler, rejectedHandler)
+  .then(doSomethingElse)
+  .then(doEvenMore)
+  .then(doThis);
+

Compare this with our callback hell code snippet and you know why promises are so powerful. In fact, there’s a lot more about promises to tell, but that’s out of the scope of this article. If you want to read more about promises in general, I recommend reading Domenic’s article and the MDN docs on promises. But let’s get back to promises in Angular.

+

Promises in Angular and $q

+

Angular comes with a promise implementation by default. It has a $q service that we can of course inject and use though-out our application. Angular’s implementation is highly inspired by Kris Kowal’s Q library which is an implementation of the Promises/A spec.

+

It comes with a Deferred API which lets you get instances of deferred objects that hold a promise object. We can use the API of a deferred to either resolve or reject a promise depending on what our code should do. Here’s a quick example:

+
// `$q` is injected before
+
+var deferred = $q.defer();
+
+anAsyncFunction(function (success) {
+  deferred.resolve(success);
+}, function (error) {
+  deferred.reject(error);
+});
+
+var promise = deferred.promise;
+
+// and later
+
+promise.then(function () {
+  // do something when `anAsyncFunction` fulfilled
+});
+

We have a function anAsyncFunction() which is asynchronous and we use the deferred API to get a promise out of it. One thing to notice here is that our function doesn’t know anything about promises but we use the deferred API to get a promise back. The deferred API comes with a few more features that I don’t want to detail here, but you can read about them in the official docs.

+

Have you ever used Angular’s $http service? Sure you did. And as we know, XMLHttpRequests are asynchronous too. Guess what $http service uses to expose its .success() and .error() APIs? Right. Promises. When making XHR calls with $http we’re also using $q implicitly. It just adds some sugar APIs to the promise it returns:

+
$http.get('some/restful/endpoint')
+  .success(function (data) {
+    // do something with `data`
+  })
+  .error(function (reason) {
+    // oups, something went wrong
+  });
+

In fact, we can use the promise native APIs to achieve the same:

+
$http.get('some/restful/endpoint')
+  .then(function (data) {
+    // do something with `data`
+  }, function (reason) {
+    // oups, something went wrong
+  });
+

Okay, so we now got a picture of promises in Angular. There’s also a nice talk by the awesome Dave on promises at ngEurope, I recommend checking that one out too. But what is it with the ES6 style promises that we’ve mentioned in the blog title?

+

ES6 style Promises in Angular 1.3

+

Although it’s nice to have the deferred API in Angular to deal with promises, it turns out that the ECMAScript standard defines a slight different API. Taking a look at the MDN docs on Promises, we see that Promise is a constructor in ES6 that takes an executor function that has access to a resolve and a reject function to resolve and reject promises respectively.

+

Angular 1.3 streamlined its promise APIs partly with the ES6 standard. I say partly here, because not all methods are supported yet. However, what the team has streamlined is that $q can also be used as a constructor now.

+

So instead of doing creating a deferred like this:

+
function myFunctionThatReturnsAPromise() {
+  var deferred = $q.defer();
+
+  anAsyncFunction(function (success) {
+    deferred.resolve(success);
+  }, function (error) {
+    deferred.reject(error);
+  });
+
+  return deferred.promise;
+}
+
+myFunctionThatReturnsAPromise().then(yay, nay);
+

We can now use the promise constructor API and return it directly without creating a deferred object first:

+
function myFunctionThatReturnsAPromise() {
+  return $q(function (resolve, reject) {
+    anAsyncFunction(function (success) {
+      resolve(success);
+    }, function (error) {
+      reject(error);
+    });
+  });
+}
+

Even if this is just an optical difference at a first glance, it’s nice to know that we can safe the lines of code to create a deferred first. Also, the fact that the $q API is now closer to the actual spec makes the code more reusable in the future.

+

Now we might wonder, if we have to change all of our code where we’ve used $q.defer() to work with promises. The answer is no. As mentioned at the beginning of the article, this is a nice small addition (rather than a new feature or replacement) in the 1.3 release that doesn’t break the code.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2014/12/22/exploring-angular-1.3-disabling-debug-info.html b/angularjs/2014/12/22/exploring-angular-1.3-disabling-debug-info.html new file mode 100644 index 000000000..0e34c48cb --- /dev/null +++ b/angularjs/2014/12/22/exploring-angular-1.3-disabling-debug-info.html @@ -0,0 +1,64 @@ +Disabling Debug Info in Angular 1.3 | Articles by thoughtram

Angularjs

Disabling Debug Info in Angular 1.3

Angular has some cool new features that improve our production code. And since these are a bit shadowed by all the other bigger features that come with 1.3, we’re going to take a look at one of them: Disabling Debug Info.

+

Sure, disabling debug info doesn’t really sound super special and in fact, it really isn’t. However, it turns out that it can have a huge impact on our applications performance, so it’s definitely worth a mention in our blog series on exploring Angular 1.3.

+

So, what is the debug info we’re talking about anyway?

+

Debug Info in Angular

+

When using certain directives, Angular attaches some additional debug information to the elements they are applied to. For example, when we use the interpolation directive to evaluate an expression in our template, Angular adds an additional ng-binding class to the directive’s, or directive’s parent element.

+

For example, if we have a scope property like this:

+
app.controller('AppController', ['$scope', function ($scope) {
+  $scope.name = 'Pascal';
+}]);
+

And an expression in our HTML code like this:

+
<p>Hello {{name}}!</p>
+

What we get, once compiled, is this:

+
<p class="ng-binding">Hello Pascal!</p>
+

The same happens when using ng-bind or ng-bind-html directives. The former is an equivalent to the interpolation directive, in form of an attribute to prevent flash of uncompiled content flickering. The latter lets us evaluate expressions that have HTML code as value while the HTML itself is interpreted by the browser (use of $sce.trustAsHtml() required here).

+

To make things a bit more clear, here’s our example as ng-bind version:

+
<p>Hello <span ng-bind="name"></span>!</p>
+

Which would end up in a DOM that looks like this:

+
<p>Hello <span class="ng-binding" ng-bind="name">Pascal</span>!</p>
+

Angular has some more cases where additional debug information is attached to an element. When Angular’s compiler creates a new scope, it adds either ng-scope or ng-isolated-scope classes to the element, depending on what kind of scope is created. So for example, having a directive like this:

+
app.directive('myComponent', function () {
+  return {
+    scope: {},
+    template: 'This is a component with isolated scope.'
+  };
+});
+

Which is used like this:

+
<my-component></my-component>
+

Creates this compiled DOM:

+
<my-component class="ng-isolated-scope">
+  This is a component with isolated scope.
+</my-component>
+

As we can see, the compiler adds an ng-isolated-scope class to that element, because it has an isolated scope. But that’s not all. What also happens is, that the actual scope is added to the DOM element as well. Wait… the scope object itself? Hey that means we can access it imperatively in JavaScript, right? Yes!

+

Depending on what type of scope is created, the corresponding element gets either a .scope() or .isolateScope() method, that we can call to access the scope object for debugging purposes. Just notice that with element we refer to angular.element().

+

Running the following code in a browser console would display the components scope object:

+
angular
+  .element(document.querySelector('my-component'))
+  .isolateScope();
+

Now we may wonder why these classes and element properties are added by the compiler. It turns out that tools like Protractor and the Angular Batarang need all this information to actually run. Batarang for example displays scope data in the developer tools.

+

Disabling debug info for production

+

Having Angular providing all this information in our application is super useful when it comes to debugging. Tools like Protractor and Batarang can rely on that data and make debugging even easier. However, additional properties and classes that are added to the DOM also come with a performance cost depending on how much is stored on the scope and DOM operations are expensive anyway.

+

What we need is a way to actually turn off this behaviour when an application is deployed to production, because we usually don’t need this information there. Luckily Angular has exactly that switch since version 1.3. In order to turn off this default behaviour, all we have to do is to call the .debugInfoEnabled() method on the $compileProvider during our application’s configuration phase (since this is the only place where we have access to providers).

+
app.config(['$compileProvider', function ($compileProvider) {
+  // disable debug info
+  $compileProvider.debugInfoEnabled(false);
+}]);
+

Yay, just one line of code and our production application runs faster! But what if we do want to have this debug information in our application because something’s wrong in our production environment and we need to debug?

+

Angular got us covered. The global angular object comes with a new .reloadWithDebugInfo() method, which does exactly what it says. It reloads the browser with debug information to make your life easier again. And since the angular object is global, we can just call it directly from the browsers console. Neat ha?

+

Note! When you disable debugInfo, you will no longer be able to do $element.scope() or $element.isolateScope(). If your code is relying on these methods, you will need to refactor.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html b/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html new file mode 100644 index 000000000..c4c91c09a --- /dev/null +++ b/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html @@ -0,0 +1,200 @@ +Binding to Directive Controllers in Angular 1.3 | Articles by thoughtram

Angularjs

Binding to Directive Controllers in Angular 1.3

In version 1.2, Angular introduced a new controllerAs syntax that made scoping clearer and controllers smarter. In fact, it’s a best practice to use controllerAs throughout our Angular apps in order to prevent some common problems that developers run into fairly often.

+

Even if it’s nice that we are able to use that syntax in pretty much every case where a controller comes into play, it turned out that in version 1.2, there’s a little quirk when using it with directives that have an isolated scope. But before we get to the actual problem, let’s recap controllerAs in general first, to get an idea of what problems it solves and how to use it, so that we are all on the same page.

+

controllerAs as Namespace

+

Who of us did not run into the problem that, when having nested scopes created by nested controllers, scope properties that have the same name as properties on the parent scope, shadow that value of the parent scope property due to JavaScript’s prototypical inheritance model?

+

Or, when speaking in code, having two controllers like this:

+
function ControllerOne($scope) {
+  $scope.foo = 'Pascal';
+}
+
+function ControllerTwo($scope) {
+  $scope.foo = 'Christoph';
+}
+
+app.controller('ControllerOne', ControllerOne);
+app.controller('ControllerTwo', ControllerTwo);
+

And a DOM structure like this:

+
<div ng-controller="ControllerOne">
+  {{foo}}
+  <div ng-controller="ControllerTwo">
+    {{foo}}
+  </div>
+</div>
+

The {% raw %} {{foo}} {% endraw %} expression in ControllerTwo scope will shadow the {% raw %} {{foo}} {% endraw %} expression in ControllerOne scope, which results in string Christoph being displayed in the inner scope and Pascal being displayed in the outer scope.

+

We could always get around this problem by using a scope’s $parent property to reference its parent scope when accessing scope properties like this:

+
<div ng-controller="ControllerOne">
+  {{foo}}
+  <div ng-controller="ControllerTwo">
+    {{$parent.foo}}
+  </div>
+</div>
+

However, it turns out that using $parent is actually a bad practice, since we start coupling our expression code to the underlying DOM structure, which makes our code less maintainable. Just imagine you have not only two nested scopes, but four or five. That would bring you into $parent.$parent.$parent.$parent hell, right?

+

That’s one of the reasons why you might have heard that we should always have a dot in our expressions that access scope properties. In other words, this could easily be fixed with doing the following:

+
function ControllerOne($scope) {
+  $scope.modelOne = {
+    foo: 'Pascal'
+  };
+}
+
+function ControllerTwo($scope) {
+  $scope.modelTwo = {
+    foo: 'Christoph'
+  };
+}
+

And in our template, we update our expressions accordingly:

+
<div ng-controller="ControllerOne">
+  {{modelOne.foo}}
+  <div ng-controller="ControllerTwo">
+    {{modelOne.foo}}
+  </div>
+</div>
+

And here comes controllerAs into play. This syntax allows us to introduce a new namespace bound to our controller without the need to put scope properties in an additional object literal. In fact, we don’t even need to request $scope in our controller anymore, since the scope is bound the controller’s this reference when using controllerAs.

+

Let’s see what that looks like in code. First we remove the $scope service and assign our values to this:

+
function ControllerOne() {
+  this.foo = 'Pascal';
+}
+
+function ControllerTwo() {
+  this.foo = 'Christoph';
+}
+

Next, we update ngController directive expression with the controllerAs syntax, and use the new namespaces in our scopes:

+
<div ng-controller="ControllerOne as ctrl1">
+  {{ctrl1.foo}}
+  <div ng-controller="ControllerTwo as ctrl2">
+    {{ctrl2.foo}}
+  </div>
+</div>
+

It gets even better. We are able to use that syntax whenever a controller is used. For example if we configure an application state with Angular’s $routeProvider we can use controllerAs there too, in order to make our template code more readable.

+
$routeProvider.when('/', {
+  templateUrl: 'stateTemplate.html',
+  controllerAs: 'ctrl',
+  controller: 'StateController'
+});
+

And as you probably know, directives can also have controllers and yes, we can use controllerAs there too.

+
app.controller('SomeController', function () {
+  this.foo = 'bar';
+});
+
+app.directive('someDirective', function () {
+  return {
+    restrict: 'A',
+    controller: 'SomeController',
+    controllerAs: 'ctrl',
+    template: '{{ctrl.foo}}'
+  };
+});
+

Great. Now we know what the controllerAs syntax it is all about, but we haven’t talked about the little drawback that it comes with in 1.2. Let’s move on with that one.

+

The problem with controllerAs in Directives

+

We said that, when using controllerAs, the controllers’ scope is bound to the controllers’ this object, so in other words - this represents our scope. But how does that work when building a directive with isolated scope?

+

We know we can create an isolated scope by adding an object literal to our directive definition object that defines how each scope property is bound to our directive. To refresh our memory, here’s what we can do:

+
app.directive('someDirective', function () {
+  return {
+    scope: {
+      oneWay: '@',
+      twoWay: '=',
+      expr: '&'
+    }
+  };
+});
+

This is a directive with an isolated scope that defines how its scope properties are bound. Alright, let’s say we have directive with an isolated scope, a controller and a template that uses the controller properties accordingly:

+
app.directive('someDirective', function () {
+  return {
+    scope: {},
+    controller: function () {
+      this.name = 'Pascal'
+    },
+    controllerAs: 'ctrl',
+    template: '<div>{{ctrl.name}}</div>'
+  };
+});
+

Easy. That works and we knew that already. Now to the tricky part: what if name should be two-way bound?

+
app.directive('someDirective', function () {
+  return {
+    scope: {
+      name: '='
+    },
+    // ...
+  };
+});
+

Changes to isolated scope properties from the outside world are not reflected back to the controllers’ this object. What we need to do to make this work in 1.2, is to use the $scope service to re-assign our scope values explicitly, whenever a change happens on a particular property. And of course, we mustn’t forget to bind our watch callback to the controllers’ this:

+
app.directive('someDirective', function () {
+  return {
+    scope: {
+      name: '='
+    },
+    controller: function ($scope) {
+      this.name = 'Pascal';
+
+      $scope.$watch('name', function (newValue) {
+        this.name = newValue;
+      }.bind(this));
+    },
+    // ...
+  };
+});
+

Here we go… the $scope service we initially got rid off is now back. If you now think this is crazy, especially when considering that this is just one scope property and in a real world directive you usually have more than one, then my friend, I agree with you.

+

Luckily, this is no longer a problem in Angular 1.3!

+

Binding to controllers with bindToController

+

Angular 1.3 introduces a new property to the directive definition object called bindToController, which does exactly what it says. When set to true in a directive with isolated scope that uses controllerAs, the component’s properties are bound to the controller rather than to the scope.

+

That means, Angular makes sure that, when the controller is instantiated, the initial values of the isolated scope bindings are available on this, and future changes are also automatically available.

+

Let’s apply bindToController to our directive and see how the code becomes cleaner.

+
app.directive('someDirective', function () {
+  return {
+    scope: {
+      name: '='
+    },
+    controller: function () {
+      this.name = 'Pascal';
+    },
+    controllerAs: 'ctrl',
+    bindToController: true,
+    template: '<div>{{ctrl.name}}</div>'
+  };
+});
+

As we can see, we don’t need $scope anymore (yay!) and there’s also no link nor compile function in this directive definition. At the same time we keep taking advantage of controllerAs.

+

Improvements in 1.4

+

In version 1.4, bindToController gets even more powerful. When having an isolated scope with properties to be bound to a controller, we always define those properties on the scope definition and bindToController is set to true. In 1.4 however, we can move all our property binding definitions to bindToController and make it an object literal.

+

Here’s an example with a component directive that uses bindToController. Instead of defining the scope properties on scope, we declaratively define what properties are bound to the component’s controller:

+
app.directive('someDirective', function () {
+  return {
+    scope: {},
+    bindToController: {
+      someObject: '=',
+      someString: '@',
+      someExpr: '&'
+    }
+    controller: function () {
+      this.name = 'Pascal';
+    },
+    controllerAs: 'ctrl',
+    template: '<div>{{ctrl.name}}</div>'
+  };
+});
+

In addition to that, bindToController is no longer exclusive to isolated scope directives! Whenever we build a directive that introduces a new scope, we can take advantage of bindToController. So the following code also works:

+
app.directive('someDirective', function () {
+  return {
+    scope: true
+    bindToController: {
+      someObject: '=',
+      someString: '@',
+      someExpr: '&'
+    },
+    ...
+  };
+});
Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/01/11/exploring-angular-1.3-validators-pipeline.html b/angularjs/2015/01/11/exploring-angular-1.3-validators-pipeline.html new file mode 100644 index 000000000..8bf4ba63a --- /dev/null +++ b/angularjs/2015/01/11/exploring-angular-1.3-validators-pipeline.html @@ -0,0 +1,147 @@ +Validators Pipeline in Angular 1.3 | Articles by thoughtram

Angularjs

Validators Pipeline in Angular 1.3

We know that working with forms in Angular is just great. Due to its scope model nature, we always have a reference to the actual form state in its corresponding scope, which makes it easy to access particular field values or represent the form state in our views.

+

If there’s one thing that takes probably most of the work when building forms, it’s their validation. We know that validation on the server-side is always required in order to process given user data that could break our app. But we also want to provide a great user experience, which is where validation on the client-side comes into play. We already learned about ngModelOptions. In this article we are going to discuss the ways we’ve been able to validate data in our Angular forms in 1.2 and detail how version 1.3 makes it even easier with the validators pipeline.

+

Built-in form validation

+

Before we start looking at what the latest bigger Angular release brings to the table when it comes to form validation, let’s take a look at what capabilities we had anyway and also especially, why there was a need for an improvement at all.

+

HTML5 provides some validation attributes we can use to let the browser validate our form controls. For example, if we want to have native validation support for email input fields, all we have to do is to apply the required attribute to that element.

+
<input type="email" required>
+

And as you probably know, there are a couple of other validation attributes like minlength, maxlength and pattern. However, it turns out that the API is inconsistent and not even supported in all browsers and platforms today. That’s why Angular provides basic implementation for HTML5 validation with its ngModel directive and controller and makes it consistent between browsers.

+

Here’s a list of supported validation attributes:

+
    +
  • ng-required
  • +
  • ng-minlength
  • +
  • ng-maxlength
  • +
  • ng-min
  • +
  • ng-max
  • +
  • ng-pattern
  • +
+

In addition to that, Angular validates certain input types automatically without us doing anything. The following code displays a simple form that has just one field of type email. Applying an ng-model to it makes Angular aware of it. Also notice the name attribute of the form which publishes the FormController instance of the form into the scope.

+
<form name="myForm">
+  <input type="email" name="emailField">
+  <p ng-if="myForm.emailField.$error.email">Email address is not valid!</p>
+</form>
+

Running this code in the browser, you can see the validation happens automatically. Errors are even exposed on the FormController’s $error object, which makes displaying error messages a breeze.

+

Of course, email is not the only type where automatic validation happens. It’s also triggered when type url or number is used. In 1.3 there’s additional support for date and time inputs like date, time, datetime-local, week and month as well.

+

Custom Validations - The Old Way

+

Built-in validations are nice, but in some cases we need validations that go far beyond the basic functionality we get out of the box. And this is where custom validations come in.

+

The key part of validation in Angular is the ngModelController since it controls the logic of passing values back and forth between the DOM and the scope. In versions before 1.3, we were able to implement custom validations by using ngModelController and it’s $formatters and $parsers pipeline.

+

Let’s say we want to implement a custom validation that checks if the value that is passed by the user is an actual integer. In order to do that, we would first create a new directive that accesses ngModelController like so:

+
app.directive('validateInteger', function () {
+  return {
+    require: 'ngModel',
+    link: function (scope, element, attrs, ctrl) {
+      // add validation to ctrl
+    }
+  };
+});
+

In a directive’s link function, we can ask for other directives controllers with the require property of the directive definition object. ctrl is now a reference to an ngModelController instance which has a $formatters and $parsers property. These two properties are arrays, that act as pipelines that get called when certain things happen.

+

These certain things are:

+
    +
  • Model to view update - Whenever the bound model changes, all functions in $formatters are called one by one, in order to format the value and changes it’s validity state.
  • +
  • View to model update - Whenever the user interacts with a form control, it calls the ngModelController’s $setViewValue method, which in turn calls all functions of the $parsers array in order to convert the value and also change it’s validity state accordingly.
  • +
+

Okay, so we have one pipeline that pipes the value from model to view and another one that pipes it from view to model. Since we want to check if the given value in our control is an actual integer, we need to use the pipeline that is executed when the view updates the model, which is the $parsers array.

+

All we have to do now, is to add a new function to $parsers that performs the needed checks and sets the validity state with $setValidity() accordingly. In order to make sure that our validation function is called first in the pipe, we use Array.prototype.unshift.

+
app.directive('validateInteger', function () {
+
+  var REGEX = /^\-?\d+$/;
+
+  return {
+    require: 'ngModel',
+    link: function (scope, element, attrs, ctrl) {
+
+      ctrl.$parsers.unshift(function (viewValue) {
+
+        if (REGEX.test(viewValue)) {
+          ctrl.$setValidity('integer', true);
+          return viewValue;
+        } else {
+          ctrl.$setValidity('integer', false);
+          // if invalid, return undefined
+          // (no model update happens)
+          return undefined;
+        }
+
+      });
+    }
+  };
+});
+

We check if the new values matches against our regular expression. If it matches we set the validity of integer to true and return viewValue, so it can be passed to further parser functions.

+

In case it doesn’t match, we set it’s validity to false, which also exposes an integer member on the FormController’s $error object, so we can display error messages accordingly. We also return undefined explicitly, in order to stop processing of the pipe.

+

We can then use it like every other directive:

+
<form name="myForm">
+  <input type="text" validate-integer>
+  <p ng-if="myForm.$error.integer">Oups error.</p>
+</form>
+

As you can see, there’s a lot to take care of when writing custom validations. We need to know about the $parsers and $formatters pipeline. We also need to set a value’s validity state explicitly with $setValidity().

+

In addition to that, it turns out that due to the nature of HTML5 form validation, some input types may not expose the input value until the valid value is entered.

+

So how does Angular 1.3 a better job?

+

Meet the $validators pipeline

+

Angular 1.3 introduces yet another pipeline, the $validators pipeline, which is rather used than $parsers + $formatters. Unlike parsers and formatters, the validators pipeline has access to both, viewValue and modelValue, since it’s called once $parsers and $formatters has been successfully run.

+

Another API difference is that $validators is not an array, but an object with each member describing a validator. Let’s implement our integer custom validation as part of the $validators pipeline.

+
app.directive('validateInteger', function () {
+
+  var REGEX = /^\-?\d+$/;
+
+  return {
+    require: 'ngModel',
+    link: function (scope, element, attrs, ctrl) {
+
+      ctrl.$validators.integer = function (modelValue, viewValue) {
+
+        if (REGEX.test(viewValue)) {
+          return true
+        }
+        return false;
+      };
+    }
+  };
+});
+

As you can see, we no longer have to take care of calling $setValidity(). Angular calls $setValidity() internally, with the value that a validator returns, which is either true or false.

+

And of course, if a value is invalid, an $error is exposed on the FormController accordingly.

+

Async validators

+

With 1.3, Angular goes even a step futher and makes asynchronous validations possible. Just imagine the case you have an input field for a user name and whenever a user types in a name, you need to perform some validity checks on your server. The application needs to wait until the server responses.

+

That’s why there’s next to $validators another validators object called $asyncValidators. Asynchronous validators work pretty much like synchronous validators except that they are asynchronous and therefore promise based. Instead of returning true or false, we return a promise that holds the state of an asynchronous code execution.

+

Here’s what it could look like:

+
app.directive('validateUsername', function ($q, userService) {
+
+  return {
+    require: 'ngModel',
+    link: function (scope, element, attrs, ctrl) {
+
+      ctrl.$asyncValidators.username = function (modelValue, viewValue) {
+        return $q(function (resolve, reject) {
+          userService.checkValidity(viewValue).then(function () {
+              resolve();
+            }, function () {
+              reject();
+            });
+        });
+      };
+    }
+  };
+});
+

Asynchronous validators are called, after synchronous validators have been successfully executed. While an asynchronous validator is running, a $pending object will be exposed on the field’s ngModelController. Flags like $valid and $invalid are set to undefined at this point.

+

We could display a loading message like this (notice the name attribute applied to the field to expose it’s controller):

+
<form name="myForm">
+  <input type="text" name="username" validate-username>
+  <p ng-if="myForm.username.$pending">Validating user name...</p>
+</form>
+

Okay, we now learned about $validators and $asynchValidators, but does that mean our existing $parsers and $formatters won’t work anymore?

+

The answer is no. The validation pipeline has been added to the existing pipelines. It is basically there, so developers can explicitly distinguish between validations and parsing/formatting related functionality.

+

Also, as we learned, the validators pipeline has a slight simpler API. We don’t have to take care of setting $setValidity() anymore. And we can finally do proper asynchronous validations.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html b/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html new file mode 100644 index 000000000..0b63c010d --- /dev/null +++ b/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html @@ -0,0 +1,57 @@ +Go fast with $applyAsync in Angular 1.3 | Articles by thoughtram

Angularjs

Go fast with $applyAsync in Angular 1.3

As already mentioned in our articles on one-time bindings and disabling debug info, one of the biggest goals of the 1.3 release was to improve Angular’s overall performance.

+

This article details yet another nice feature that makes your Angular applications in particular cases potentially faster: It lets you resolve multiple $http responses, that are received around the same time, in one $digest cycle with a new API added to the $rootScope called $applyAsync.

+

Let’s talk about what that actually means and why you want to do that.

+

Why and when we need the $digest cycle

+

We know that two-way data binding is one big selling point of Angular. Changes to our model in the imperative world of JavaScript seem to sync magically with the model values in the declarative world of HTML and vice versa, without us setting up any event listener and other things that are required to achieve that functionality.

+

In fact, two-way binding is just one kind of binding that Angular supports. We also have one-way bindings and even one-time bindings (since version 1.3) as mentioned earlier. In order to make data binding possible, Angular comes with this sort of event loop (the $digest) to update our application model and DOM, whenever it is needed.

+

But how does Angular know, when it has to trigger another $digest cycle? We don’t want to go in too much detail here, since there are ton of resources out there that cover this topic very well, but let’s clarify at least the most important facts. Some people think initially, that Angular has a kind of poll mechanism that checks every few milliseconds if something on the model changed so it can update the view accordingly. This is not true.

+

There are basically three possible cases when the state of an application can change and these are the only moments where $digest cycles are needed. The case are:

+
    +
  • User Interaction through events - The user clicks UI controls like buttons and in turn triggers something in our application that changes state.
  • +
  • XMLHttpRequests - Also known as AJAX. Something in our app requests some data from a server and update model data accordingly.
  • +
  • Timeouts - Asynchronous operations cause through timers that can possibly change the state of our application
  • +
+

Whenever one of the above things happens, Angular knows it needs to trigger a $digest. You might still wonder how that works, since you don’t have to inform Angular about these interactions explicitly. This is because Angular intercepts all of these interactions already for you.

+

That’s why we have all these predefined directives like ng-click or even directives that override existing tags like <input>. The $http service that Angular brings to the table also makes sure that a $digest is triggered once a request returns.

+

In addtion, this explains why you need to call $scope.$apply(), which in turn triggers a $digest internally, when you have third-party code that changes your application’s state from the outside world.

+

Okay, so now we have a general picture of what the $digest is about and when it’s needed and triggered and also how we can trigger it explicitly with $scope.$apply. But we haven’t talked about when $applyAsync comes into play.

+

Batching multiple $http responses into one $digest

+

As the name already says, $applyAsync has something to do with executing a $scope.$apply through an asynchronous operation. But what does that mean and when makes it actually sense?

+

We mentioned that one of the cases where a $digest is triggered, is when an XHR call using $http service returns from it’s execution. This is nice because we don’t have to worry about updating our model in the DOM once the model is updated. Here’s a small snippet that details that scenario (note that we don’t use $scope here since we assume that controllerAs syntax is used:

+
app.controller('Ctrl', function ($http) {
+
+  // Make XHR and update model accordingly
+  $http.get('fetch/some/json/').then(function (response) {
+    this.myModel = response.data;
+  }.bind(this));
+});
+

We have a controller that asks for $http service and uses it to make an XHR to some url and once the call resolves, we update myModel on our controller with the new data that we got from the server. There’s nothing we need to do to update myModel in our DOM, since this call, once it resolves, triggers a $digest that takes care of the rest.

+

Now imagine we build an application where it’s required to make three XHRs at bootstrap time. That means, three independent requests that all resolve independently after different periods of time, which in turn causes three $digest cycles that get triggered once each of the calls return. This can slow down our application. Wouldn’t it be nice if we could collect the promises that return from the XHR calls that are made around the same time and resolve them at the next $digest cycle that happens? Yes! And this is exactly where $applyAsync comes into play.

+

Since Angular 1.3, $rootScope comes with a new method $applyAsync that lets us basically collect expressions. These expressions get immediately evaluated but resolved with the next tick ($digest). In order to make this work nice with requests that happen through $http calls, $httpProvider comes with a corresponding API that tells Angular that we actually want to use that feature.

+

All we need to do is to call the provider’s useApplyAsync method and Angular takes care of deferring the resolution of your XHR calls to the next tick. Here’s what it looks like:

+
app.config(function ($httpProvider) {
+  $httpProvider.useApplyAsync(true);
+});
+

That’s it! If the application now receives multiple $http responses at around the same time, this is what happens (a bit simplified though):

+
    +
  • The call’s promise is pushed into a queue
  • +
  • An asynchronous $apply is scheduled in case there’s no one scheduled yet, by telling the browser to execute setTimeout()
  • +
  • Once timed out, the queue is flushed and the actual $apply is triggered
  • +
+

The setTimeout() is called with a 0 delay which causes an actual delay of around 10 milliseconds depending on the browser. That means, if our three asynchronous calls return at around the same time (somewhere inside that particular timeout delay), they get resolve with a single $digest cycle instead of three which speeds up our application.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/01/23/exploring-angular-1.3-ngMessages.html b/angularjs/2015/01/23/exploring-angular-1.3-ngMessages.html new file mode 100644 index 000000000..e4cd3fc3c --- /dev/null +++ b/angularjs/2015/01/23/exploring-angular-1.3-ngMessages.html @@ -0,0 +1,162 @@ +ngMessages in Angular 1.3 | Articles by thoughtram

Angularjs

ngMessages in Angular 1.3

In one of our articles of our blog series on exploring Angular 1.3, we’ve covered a very nice feature that makes validating forms in Angular a breeze. Right, I’m talking about the validators pipeline.

+

While the validators pipeline seems to make our life a lot easier and we as developers think it can’t get any better, it turns out there’s another bigger feature that adds even more awesomeness to the world of forms when building Angular applications: ngMessages.

+

ngMessages is an entire new module that comes with a couple of directives to enhance the support for displaying messages within templates. Which means, even if in this article we’re using it just for forms, we’re not restricted to do so. But let’s start right away and take a look at a scenario that ngMessages tries to solve.

+

Displaying messages in forms - The old way

+

Providing a good user experience is always important. When building forms, it’s pretty common to display messages to the user depending on the data that the user entered into the form fields. This, for example, could be a message that tells the user that a specific field is required to be filled out, or a message that says that the given data doesn’t match a certain pattern.

+

We’ve already learned about the validators pipeline that lets us easily determine if the value of a form field is valid or not. Each state of an input element is exposed on the associated scope of a form (as long as a name attribute is applied), which makes it super easy to conditionally display DOM elements that have (validation) messages.

+

Let’s say we want to build a common login form where the user needs to enter an email address and a password, like this:

+
<form name="loginForm">
+  <label>Email:</label>
+  <input type="email" ng-model="email" name="email">
+
+  <label>Password:</label>
+  <input type="password" ng-model="password" name="password">
+</form>
+

Notice the name attributes on the <form> and <input> elements. These make sure the form’s FormController instance, that holds the state of the form, is exposed on the scope. Giving the <input> elements names exposes their state on the FormController. In other words, loginForm is now an actual expressions on the scope that we can use to evaluate data in our template.

+

We also specify the type of each <input> which adds some default validations to the fields behind the scenes. In fact, we can visualize the form’s state by adding the following expression to our document:

+
{{ loginForm | json }}
+

While entering an email address, Angular automatically validates the data given to the field and exposes the state to loginForm. That means, as long as we’re entering data which isn’t valid, the $error property of loginForm gets extended with a new object email that has all information about the state of the field.

+
{
+  "$error": {
+    "email": [
+      {
+        "$viewValue": "invalid value",
+        "$validators": {},
+        "$asyncValidators": {},
+        "$parsers": [],
+        "$formatters": [
+          null
+        ],
+        "$viewChangeListeners": [],
+        "$untouched": false,
+        "$touched": true,
+        "$pristine": false,
+        "$dirty": true,
+        "$valid": false,
+        "$invalid": true,
+        "$error": {
+          "email": true
+        },
+        "$name": "email",
+        "$options": null
+      }
+    ]
+  },
+  ...
+}
+

In addition to that, due to adding name attributes to the field itself, there’s also an email and password property on loginForm which have an $error object themselves. The $error object on form fields is a simple key/value store that represents the error state for each applied validator on a field.

+
{
+  ...
+  "email": {
+    ...
+    "$error": {
+      "email": true
+    },
+    ...
+  }
+}
+

So, in order to display a message when there’s an error with the default email validation (which we get automatically by specifying the type), all we need to do is to conditionally add a DOM node to the document like this:

+
<p ng-if="loginForm.email.$error.email">
+  Please enter a valid email
+</p>
+

Again, loginForm.email is the field reference, $error.email is the result of the email validation. To make it more clear, we can extend the example by adding a required attribute to the field, which also adds a validator to the field behind the scenes.

+
<input type="email" ng-model="email" name="email" required>
+

Displaying an error message accordingly could look like this:

+
<p ng-if="loginForm.email.$error.required">
+  Please enter your email
+</p>
+

I think we get the idea. Now imagine instead of just two different validations, we have five validations for just one field and we want to display a message for each. The markup for our form gets out of control very quickly. Here’s how our password field could be extended with conditional messages.

+
<label>Password:</label>
+<input
+  name="password"
+  ng-model="password"
+  type="password"
+  required
+  minlength="8"
+  pattern="..."
+  validator4
+  validator5
+>
+<p ng-if="loginForm.email.$error.required">...
+<p ng-if="loginForm.email.$error.minlength">...
+<p ng-if="loginForm.email.$error.pattern">...
+<p ng-if="loginForm.email.$error.validator4">...
+<p ng-if="loginForm.email.$error.validator5">...
+

We probably also want to control what message shows up when, especially when multiple messages occur at the same time. Try to build that with just ngIf directives all over the place. And this is where ngMessages comes into play.

+

Displaying messages in forms with ngMessages

+

ngMessages comes as a separate module. In order to use it, we first need to install it. One way to do so is to use npm:

+
$ npm install angular-messages
+

Then, we need to embed the actual script in our document:

+
<script src="path/to/angular-messages.js"></script>
+

Once done, we declare ngMessages as module dependency of our app and we are ready to go.

+
angular.module('myApp', ['ngMessages']);
+

Alright. The module is now installed and ready to be used. Let’s take a look at what our login form would look when ngMessages is used. The module comes with two directives - ngMessages and ngMessage.

+

Whereas ngMessages directive gets an expression that evaluates to an object where each member can control if a certain message is displayed or not, ngMessage directive is in charge of displaying that particular message.

+

Things are a bit easier to understand when actual code is shown, so here’s what our DOM looks like when ngMessages is used:

+
<div ng-messages="loginForm.password.$error">
+  <p ng-message="required">...</p>
+  <p ng-message="minlength">...</p>
+  <p ng-message="pattern">...</p>
+  <p ng-message="validator4">...</p>
+  <p ng-message="validator5">...</p>
+</div>
+

We have an element where ngMessages directive is applied. As mentioned earlier, ngMessages gets an expression that evaluates to an object where each member is either true or false. This fits perfectly to what FormController exposes on the scope, when fields have validations errors and a name attribute applied.

+

With ngMessage directive, we can just conditionally display messages by providing it with a name of a validator that is applied to the corresponding form field. Now, whenever a validator declares the value of the password field invalid, it displays the message that belongs to it.

+

In case we don’t want to pollute our DOM with additional elements, just to apply the directives, ngMessages and ngMessage are not restricted to attributes. We can also use them as elements. In that case, for and when attributes are needed to pass expressions accordingly.

+
<ng-messages for="loginForm.password.$error">
+  <ng-message when="required">...</ng-message>
+  <ng-message when="minlength">...</ng-message>
+  <ng-message when="pattern">...</ng-message>
+  <ng-message when="validator4">...</ng-message>
+  <ng-message when="validator5">...</ng-message>
+</ng-messages>
+

Taking a closer look at this code snippet, you might think this is a very familiar construct. Right. It looks pretty much like using ngSwitch and ngSwitchWhen directives. In fact, it is almost the same.

+

So what is the difference and why do we want to use one version over the other? Well, it turns out ngMessages is much more powerful.

+

Prioritization and multiple messages

+

Only one message is displayed at a time by default when using ngMessages. However, there might be cases where we want to display multiple message for a single field at a time. This we cannot do with ngSwitch, since it only renders a single match of the given construct. In order to display multiple messages, we can apply the ng-messages-multiple attribute to our ngMessages directive. This causes all messages to be displayed where the corresponding validations fail.

+
<ng-messages ng-messages-multiple for="loginForm.password.$error">
+  ...
+</ng-messages>
+

Or, if we want to display a single one, we at least want to prioritize which message shows up when. For example, before we want to display a message that says the given value doesn’t match a particular pattern, we first want to make sure we have at least six characters (minlength) and therefore displaying a message for that first.

+

We can do so by simply assembling our DOM accordingly. Messages that appear first in the DOM are also displayed first. The following construct displays a message for entering a value that is too short, before it informs the user that the given value doesn’t match certain pattern:

+
<ng-messages for="loginForm.password.$error">
+  <ng-message when="minlength">...</ng-message>
+  <ng-message when="pattern">...</ng-message>
+</ng-messages>
+

Reusing existing messages

+

It gets even better. It’s pretty common to have the same messages for the same validations across many forms. Instead of redefining the same message over and over again to have it available in each and every form, ngMessages directive expects yet another optional attribute called ng-messages-include that lets us include predefined messages at different places in our application.

+

All we need to do is to define a template that contains the messages we want to reuse and give it an id so we can reference it via ng-messages-include.

+
<script type="script/ng-template" id="required-message">
+  <ng-message when="required">
+    This field is required!
+  </ng-messages>
+</script>
+
+<ng-messages ng-messages-include="required-message" for="loginForm.password.$error">
+  ...
+</ng-messages>
+
+<!-- somewhere else -->
+<ng-messages ng-messages-include="required-message" for="otherForm.field.$error">
+  ...
+</ng-messages>
+

Breaking change in Angular 1.4:
ngMessagesInclude is no longer an attribute in Angular 1.4. If you're using an Angular version >= 1.4, we recommend reading this article on breaking changes introduced in ngMessages.

+

If the template is not present in the document, Angular performs a $templateRequest to fetch the template first.

+

Now we’ve learned that how we can define templates in order to reuse messages at different places in our application. You might think that this is a scenario where HTML Templates would be a better fit, instead of doing script overloading. I agree on that, since this is what the <template> element has been designed for. Unfortunately, at the time of writing this article, this was not supported, which is why I’ve created a corresponding issue here.

+

There’s a lot more to cover and I recommend heading over to the official docs to learn everything you need to know. This module is not only a time-saver but also adds some very powerful features to our declarative world.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/02/19/futuristic-routing-in-angular.html b/angularjs/2015/02/19/futuristic-routing-in-angular.html new file mode 100644 index 000000000..79f0d5b33 --- /dev/null +++ b/angularjs/2015/02/19/futuristic-routing-in-angular.html @@ -0,0 +1,204 @@ +Futuristic Routing in Angular | Articles by thoughtram

Angularjs

Futuristic Routing in Angular

One of the latest announcements that counts as the most exciting ones, is that the Angular team implements a completely new router for the 2.0 release that is much more flexible and powerful than the original one. Especially, when it comes to more complex application structures. Rob Eisenberg, creator of the recently announced aurelia framework, gave an introduction talk on the new router at last years ngEurope conference and showed us the main concepts that make this router so much better.

+

It got even better, when the team announced that they plan to back port the new router to the 1.x branch of the Angular framework and having it ready in time for the 1.3 release. Unfortunately it turned out, that it took a bit more effort to make the new router available for both projects, especially considering that both should share as much code as possible. That’s why the back port has been postponed to the next bigger 1.4 release, which is targeted to be ready in March, this year (yay!) The router also didn’t make it into the 1.4 release.

+

That’s right, at the time of writing this article, the new router hasn’t been released yet. However, since we’re following the latest developments of all Angular version 2.0.0 related projects actively on GitHub, we can’t wait to share our thoughts on the new router with you. In this article we explore the new router and discuss it’s concepts and features that we’ve all been waiting for!

+

The Routing we know

+

Before we start off showing what the new router is going to look like, let’s recap what kind of routing Angular comes with. The Angular source tries to be as modular as possible. Code components, that aren’t necessary to get an Angular application running, but provide nice additional functionality that might be needed, are sliced into their own modules.

+

And so is the basic Angular router. It’s implemented in the ngRoute module and can easily be installed through package managers like npm. Once installed, we can add it as module dependency to our application like so:

+
var app = angular.module('myApp', ['ngRoute']);
+

In order to configure routes for our application, we use the $routeProvider that we can access in our app’s config phase. $routeProvider comes with methods like .when() and .otherwise() to define which route maps to which controller and template. Here’s a quick example:

+
app.config(function ($routeProvider) {
+  $routeProvider
+    .when('/welcome', {
+      template: 'welcome.html',
+      controller: 'WelcomeController'
+    })
+    .otherwise('/welcome');
+});
+

We can see very nicely that the route /welcome maps to the controller WelcomeController and the template welcome.html. In addition, if at runtime a route is given that is not covered by any .when() configuration, the app defaults to /welcome route, that’s what .otherwise() is for.

+

If we have some additional dependencies that should be resolved before a route’s controller is instantiated, we can do that with the resolve property, which is an object literal that maps each member to a promise that either resolves or rejects later at runtime. These resolved dependencies are available to be injected in the controller instance. In order to instantiate the route’s controller, all specified promises need to resolve. If only one promise gets rejected, the route change is cancelled since the corresponding controller can’t be instantiated.

+

Here’s an example where a promise needs to be resolved first, before a controller can be instantiated:

+
app.config(function ($routeProvider) {
+  $routeProvider
+    .when('/welcome', {
+      template: 'welcome.html',
+      controller: 'WelcomeController',
+      resolve: {
+        person: function (WelcomeService) {
+          return WelcomeService.getPerson(); // returns a promise
+        }
+      }
+    })
+    .otherwise('/welcome');
+});
+
+app.controller('WelcomeController', function (person) {
+  // do something with person
+});
+

If you’re not familiar with promises we’ve written an article that gives a brief introduction. Also, if this resolve property looks completely new to you we recommend reading the official docs.

+

Let’s take a quick look at our main template to see where our route templates are loaded and rendered.

+
<div ng-view></div>
+

That’s it. One single entry point where, depending on our route, a template is loaded and it’s corresponding controller is instantiated. And here we already see the first weak points of Angular’s built-in routing implementation. The basic routing we get so far is just a simple URL to controller/template mapping. There’s no way to have sibling or nested components.

+

Since ngRoute lacks such advanced routing features, the community has built their own router component, that solves all these problems - ui-router.

+

But of course, the Angular team is listening. And that’s why they implement a new more powerful router that is available for both, the 1.x as well as the 2.x branch.

+

Introducing the new router

+

You might have seen that Christoph and I gave a talk on ”The Best Angular Yet!” at Amsterdam’s Angular conference NG-NL. There we already gave a little sneak peak on what the new router will look like. At the time of writing this article and giving that presentation, the router was still in development, so things might have changed over time but we try to keep this article updated.

+

The new router will be quite different. One of it’s main goals is that it works for both Angular 2.x and Angular >=1.4. That means, both need to share as much code as possible, since Angular version 2.0.0 is written in AtScript TypeScript. There are a couple more differences but we are going to take a look at them step by step.

+

We can install the new router via npm by running the following shell command:

+
$ npm install angular-new-router
+

Once installed we can take a look at node_modules/angular-new-router/dist/ and see that there are two versions of the router source - router.es5.js and router.js. The former one is an “angularfied” version of the router. So it’s basically the router code in ECMAScript 5 plus some additional Angular 1 specific components like service provider and directives. The latter one is the compiled AtScript code as AMD module so it can be used in other applications as well.

+

Currently we are interested in the Angular 1 components, so what we need to do is to embed router.es5.js and add the new router module as dependency to our application like this:

+
var app = angular.module('myApp', ['ngComponentRouter']);
+

Great! Next up: configuration. Here we’re going to encouter the first big difference when using the new router. As we know, in Angular 1 we have this .config() phase where we have access to service providers in order to configure services that are used later at runtime. That’s why we can use the $routeProvider of ngRoute to configure our routes.

+

However, it turned out that there are a lot of problems with having a separation between configuration and run phases, which is why there won’t be such a thing in Angular >= 2.0.0. And so there isn’t in the new router. Now you might wonder how we are able to configure our routes with the new router, if there’s no provider that we can access during our application’s .config().

+

Well, let’s take a look at some code.

+
app.controller('AppController', function ($router) {
+  $router.config([
+    {
+      path: '/',
+      component: 'welcome'
+    }
+  ]);
+});
+

Oh what’s happening here? All we do is creating a new controller that asks for the $router service, which we use to configure itself. The configuration is pretty straight forward. We use $router.config() and pass it an array with configuration objects that each have a property path to set the route and a property component that sets the name of the component to be instantiated.

+

We’re going to talk about what a component actually is in a second, but let’s first take a look at our template. So let’s assume we have an HTML document, this is what our application could look like:

+
<body ng-app="myApp" ng-controller="AppController">
+  <ng-outlet></ng-outlet>
+</body>
+

Right, there’s no <ng-view> anymore. Instead the new router comes with a directive called <ng-outlet> which can also be used as an attribute in case it’s needed. An outlet basically is a “hole” where component templates are loaded into. So the above code in it’s current state is pretty much the same as using <ng-view> with the old routing system.

+

But what gets loaded into our outlet you ask? Good question! This is where components come into play. When we configured the router, we said that the route / loads and instantiates the welcome component. But we haven’t talked about what the welcome component actually is.

+

A component in Angular 1, when using the new router, is a controller with a template and an optional router for that component. So if we say we have a welcome component, we need to create a corresponding controller and template for it. In fact, the new router already comes with a default configuration to load and instantiate components. This configuration behaves as follows:

+
    +
  • Load the component template asynchronously from components/[COMPONENT_NAME]/[COMPONENT_NAME].html
  • +
  • Instantiate [COMPONENT_NAME]Controller
  • +
+

Applying this to our configuration, it means that the router automatically tries to load components/welcome/welcome.html and instantiate WelcomeController.

+

If we’re not okay with that configuration, we can simply override this default behaviour by using the $componentMapperProvider in our application configuration. It provides us with methods to configure the names of controllers to be instantiated as well as the paths from where to load component templates.

+

The following code forces the router to load [COMPONENT_NAME].html instead of components/[COMPONENT_NAME]/[COMPONENT_NAME].html:

+
app.config(function ($componentMapperProvider) {
+  $componentMapperProvider.setTemplateMapping(function (name) {
+    // name == component name
+    return name + '.html';
+  });
+});
+

Let’s create WelcomeController (for simplicity reasons I define the controller directly on app rather than introducing a new module for that component, but you can do that of course).

+
app.controller('WelcomeController', function () {
+
+});
+

And here the corresponding template welcome.html:

+
<h1>Welcome!</h1>
+

The component is now ready to be loaded and instantiated.

+

Adding behaviour to components

+

Now we have a component with a controller that doesn’t do anything. Adding behaviour to our component works the way we are used to it. We simply define methods and properties on a component’s controller.

+

Just keep in mind that the new router enforces controller as syntax, which means we have to define our methods and properties on the controller itself instead of $scope. The controller is then exposed with the component name on the scope, which leads us to use welcome as controller reference in our template.

+

As an example let’s add a property and method to our WelcomeController:

+
app.controller('WelcomeController', function () {
+  this.name = 'Pascal';
+
+  this.changeName = function () {
+    this.name = 'Christoph';
+  };
+});
+

In our component template, we can access the controller properties via the welcome identifier like this:

+
<h1>Welcome!</h1>
+<p>Hello {{welcome.name}}</p>
+<button ng-click="welcome.changeName()">Change Name!</button>
+

If you’re not familiar with the controller as syntax. you might want to check out our article on Binding to Directive Controllers.

+

Linking to other components

+

In order to have a very easy way to navigate from one component to another, the ngComponentRouter module comes with a routerLink directive that we can use to tell our application, where to navigate. Let’s say we have another component user, we’d extend our application with a new configuration for that component.

+
app.controller('AppController', function ($router) {
+  $router.config([
+    {
+      path: '/',
+      component: 'welcome'
+    },
+    {
+      path: '/user',
+      component: 'user'
+    }
+  ]);
+});
+

And as we’ve learned, we also need a corresponding controller and template to actually assemble our component. Here’s our UserController:

+
app.controller('UserController', function () {
+
+});
+

And here’s the template user.html.

+
<h1>User</h1>
+

Now, in order to get there from our welcome component, all we have to do is to add an anchor tag to our welcome component template and us the routerLink directive accordingly.

+
<h1>Welcome!</h1>
+<p>Hello {{welcome.name}}</p>
+<button ng-click="welcome.changeName()">Change Name!</button>
+<p><a ng-link="user">User View</a></p>
+

As you can see, we don’t have to set an href attribute, since routerLink takes care of that. The directive itself takes a component name to navigate to once the link is clicked, which in our case is `user.

+

Linking with dynamic parameters

+

Of course, just navigating to a component in some cases isn’t enough. We might have routes that take some additional parameters in order to post process that data accordingly. Our user component doesn’t do anything right now, but we might want to display some data of a certain user depending on a given user id.

+

Configuring a route that expects query parameters works pretty similar to what we already know when doing that with the original router. We can define placeholders in our route definition by using the : symbol followed by an identifer that is used to later expose the value of that placeholder in a dedicated $routeParams service.

+

To get a better idea, here’s our updated route configuration that takes an additional user id parameter:

+
app.controller('AppController', function ($router) {
+  $router.config([
+    {
+      path: '/',
+      component: 'welcome'
+    },
+    {
+      path: '/user/:userId',
+      component: 'user'
+    }
+  ]);
+});
+

Simple right? As already mentioned, query parameter are exposed on the $routeParams service as simple hash. Which means, in order to access a given user id from that route, all we have to do is to inject $routeParams into our component’s controller and ask for the paramters we’re looking for.

+
app.controller('UserController', function ($routeParams) {
+  this.userId = $routeParams.userId;
+});
+

Okay cool. But how do we link to a component that takes parameters? We’ve learned that ngComponentRouter comes with a routerLink directive that takes a component name. It turns out, we can set parameters, for a route we want to navigate to, with that directive too! All we need to do is to pass a hash literal to specify the values. Here’s our updated welcome component template:

+
<h1>Welcome!</h1>
+<p>Hello {{welcome.name}}</p>
+<button ng-click="welcome.changeName()">Change Name!</button>
+<p><a ng-link="user({ userId: 3 })">User View</a></p>
+

Sibling Components

+

What we’ve done so far is not a very complex scenario, nor does it show the advantage over Angular’s basic routing with ngRoute. There are other scenarios where the power of the new router really shines. One of them is being able to have sibling components per route. Exactly, we’re finally able to load more than one component at a time!

+

Imagine, when a user visits our app, we not only want to have a welcome component be loaded, we want to spin up another component for our navigation as well. So we end up with two components at the same time for one single route - navigation and welcome. We can configure our $router accordingly by specifying a components property which describes what components should be loaded for which outlet. Let’s see how that works.

+
app.controller('AppController', function ($router) {
+  $router.config([
+    {
+      path: '/',
+      components: {
+        navigation: 'navigation',
+        main: 'welcome'
+      }
+    }
+  ]);
+});
+

Here we see that we expect two outlets navigation and main and we say that we want to load the navigation and welcome component respectively. Of course, we now need a NavigationController and navigation.html to make this work. Here’s a simple controller with an even simpler template:

+
app.controller('NavigationController', function () {
+
+});
+
<h2>Navigation</h2>
+<p>Yay, navigation goes here.</p>
+

Now we need to decide, where our components are actually rendered. We’ve learned that a component has outlets, in fact, a component can have multiple outlets. In our router configuration we said we have an outlet navigation and main. All we need to do is to use the <ng-outlet> directive multiple times and give them the names accordingly.

+

Here’s our updated index.html that now introduces two outlets, one for each component specified in the router configuration:

+
<body ng-app="myApp" ng-controller="AppController">
+  <nav ng-outlet="navigation"></nav>
+
+  <main ng-outlet="main"></main>
+</body>
+

That’s it! Running this code in the browser shows that now two sibling components are loaded for one route.

+

So much more to talk about

+

Following the development on the new router and also actively contributing to it, there’ve been a couple of questions popping up that we haven’t covered in this article yet. In fact, most of them aren’t answered yet but you can follow them on GitHub since we’ve created issues accordingly. To give you an idea of what questions we are talking about, we’ve asked ourself for example if a component can have it’s own sub components and outlets or how to link to routes that have multiple outlets.

+

We also haven’t talked about nested routing and or if there’s a resolve equivalent in the new router, but once we have answers to all these questions, we either going to update this article or write separate ones that cover each topic isolated.

+

In the meantime check out the official repository or take a look at the online documentation. Don’t forget to contribute, we always need your help!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/2015/02/21/around-the-globe.html b/angularjs/2015/02/21/around-the-globe.html new file mode 100644 index 000000000..5c5aa21b0 --- /dev/null +++ b/angularjs/2015/02/21/around-the-globe.html @@ -0,0 +1,44 @@ +Around the globe | Articles by thoughtram

Angularjs

Around the globe

If you’ve been following us for a while, you’ve probably noticed by now that we <3 to travel. Especially to run workshops and speak at tech events around the globe.

+

Salt Lake City

+

Salt Lake City

+

Today we are excited to disclose that Pascal will fly over to the US to speak at ng-conf. It’s the world’s original Angular conference taking place on March 5th + 6th in Salt Lake City, Utah.

+

Pascal will co-present with Angular core team member Chirayu. They’ll be speaking about the new internationalization and localization support that is currently being worked on.

+

We are super happy about this and are very much looking forward to the event!

+

Bangkok

+

Bangkok

+

Roughly a week ago Pascal and I talked about “The best Angular yet” at the ng-nl conference in Amsterdam. We are happy to share with you that we will bring this talk to Bangkok, Thailand on April 17th. I will present “The best Angular yet” alone since Pascal will be running a workshop in Switzerland then. Did I mention that we love traveling?

+

The talk will be given at the local Angular meetup in Bangkok. We’re still busy to find the perfect location for the meetup so stay tuned for updates regarding the exact location and time.

+

The meetup will take place at the Launchpad Co-working space and start at 4pm.

+ +
+ +
+ +

Other events

+

If you’re interested in where else you can find us, head over to our events page to get an overview of upcoming events we’re speaking at.

+

— Christoph

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angularjs/2015/03/08/joining-betahaus-education.html b/angularjs/2015/03/08/joining-betahaus-education.html new file mode 100644 index 000000000..89bacee24 --- /dev/null +++ b/angularjs/2015/03/08/joining-betahaus-education.html @@ -0,0 +1,36 @@ +Joining betahaus education | Articles by thoughtram

Angularjs

Joining betahaus education

If you are following the startup scene chances are you’ve heard about the betahaus before. It’s a popular co-working space with branches in Berlin, Hamburg, Barcelona and Sofia.

+

We’re happy to announce to take part in the new betahaus education program that aims to deliver various formats including sessions, workshops and even series around topics such as tech, startup and everything digital.

+

On Friday 17th of July we’ll give a one day workshop on the popular Angular framework at the betahaus Hamburg.

+

While booking isn’t yet available we recommend to already mark the date in your calendar!

+ +
+ +
+ +

Other events

+

If you’re interested in where else you can find us, head over to our events page to get an overview of upcoming events we’re speaking at.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html b/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html new file mode 100644 index 000000000..2ed57f31d --- /dev/null +++ b/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html @@ -0,0 +1,102 @@ +Exploring Angular 1.5: Lifecycle Hooks | Articles by thoughtram

Angularjs

Exploring Angular 1.5: Lifecycle Hooks

Angular 1.5 has been finally released and it’s more powerful than ever before! Many new features have been added and tons of fixes landed in the latest bigger release. If you’re following our articles, you know that we love to give an overview of the latest and greatest in the Angular world. Last year we blogged about the 1.3 release in our article series Exploring Angular 1.3. With this article we’re going to start a new series called, guess what, “Exploring Angular 1.5”, and the first topic we’re going to explore is the new feature of Lifecycle Hooks. Let’s get started right away!

+

Lifecycle Hooks

+

Lifecycle hooks in Angular landed first in the Angular 2 alpha release and they are more or less inspired by the Custom Elements lifecycle callbacks. By inspired we mean that they are not exactly the same. They’re not only named differently and do different things, there are also more. An Angular 2 component comes with lifecycle hooks like ngOnInit(), ngOnDestroy(), ngOnChanges() and many more. We get a very detailed overview of these in the official docs.

+

However, this article is on Angular 1.5. Since Angular 1 is evolving in a way to keep the gap to Angular 2 as small as possible, some lifecycle callbacks have been backported to the current “best Angular yet”. Let’s take a look at them one by one.

+

Note: The Angular lifecycle-hooks were introduced in version 1.5.3.

+

$onInit()

+

This lifecycle hook will be executed when all controllers on an element have been constructed and after their bindings are initialized. This hook is meant to be used for any kind of initialization work of a controller. To get a better idea of how this behaves, let’s take a look at some code.

+
var mod = angular.module('app', []);
+
+function MyCmpController() {
+  this.name = 'Pascal';
+}
+
+mod.component('myCmp', {
+  template: '<h1>{{$ctrl.name}}</h1>',
+  controller: MyCmpController
+});
+

We start off with an Angular 1.5 component myCmp. A component has a controller and a template, and in our example, our component simply has a name property that we interpolate. $ctrl is the default controllerAs namespace, which is super nice because we don’t have to set it up.

+

We can now move the initialization work into the $onInit lifecycle hook, by defining the hook on the controller instance:

+
function MyCmpController() {
+  this.$onInit = function () {
+    this.name = 'My Component';
+  };
+}
+

Okay great. But uhm… what’s the big deal? Well, while the resulting output will be the same, we now have the nice side effect that this component doesn’t do any initialization work when its constructor is called. Imagine we’d need to do some http requests during initialization of this component or controller. We’d need to take care of mocking these requests whenever we construct such a component. Now we have a better place for these kind of things.

+

Intercomponent Communication

+

Another nice thing about $onInit, is that we can access controllers of parent components on our own component’s controller, as those are exposed to it for intercomponent communication. This means it’s not even necessary anymore to have a link() function to access other directive controllers.

+

For example, if we’d build a <tabs> component and a <tab> component, where the latter needs access to the TabsController to register itself on it, we can simply ask for it using the require property an call it directly via the controller instance.

+
mod.component('myTab', {
+  ...
+  require: {
+    tabsCtrl: '^myTabs'
+  },
+  controller: function () {
+    this.$onInit = function () {
+      this.tabsCtrl.addTab(this);
+    };
+  }
+});
+

So after all, this aligns perfectly with what we’ve predicted a long time ago in our article on binding to directive controllers.

+

$onChanges()

+

This hook allows us to react to changes of one-way bindings of a component. One-way bindings have also been introduced in Angular 1.5 and align a bit more with Angular 2’s uni-directional data flow. Let’s say we make the name property of our myCmp configurable from the outside world using a one-way binding:

+
mod.component('myCmp', {
+  template: '<h1>{{$ctrl.name}}</h1>',
+  bindings: {
+    name: '<'
+  },
+  controller: MyCmpController
+});
+

We can now bind an expression to the component’s name property like this:

+
<my-cmp name="someExpression"></my-cmp>
+

Let’s say we want to prepend the name with “Howdy” when the name is “Pascal” and otherwise simply greet with “Hello”. We can do that using the $onChanges() lifecycle hook. It gets called with an object that holds the changes of all one-way bindings with the currentValue and the previousValue.

+
function MyCmpController() {
+  this.$onChanges = function (changesObj) {
+    if (changesObj.name) {
+      var prefix;
+      (changesObj.name.currentValue === 'Pascal') ?
+        prefix = 'Howdy ' : prefix = 'Hello ';
+      this.name = prefix + this.name;
+    }
+  };
+}
+

Neat stuff!

+

$onDestroy()

+

$onDestroy() is a hook that is called when its containing scope is destroyed. We can use this hook to release external resources, watches and event handlers.

+

For example, if we’d manually set up a click handler (instead of using ng-click), we need unregister the event handler when the component is destroyed, otherwise it’ll keep hanging around and leaks memory.

+
function MyCmpController($element) {
+
+  var clickHandler = function () {
+    // do something
+  };
+
+  this.$onInit = function () {
+    $element.on('click', clickHandler);
+  };
+
+  this.$onDestroy = function () {
+    $element.off('click', clickHandler);
+  };
+}
+

$postLink()

+

There has been a lot of discussion around the fact that bindToController pushes us into the direction not to use compile() and link() anymore, and rather stick to simply controller() because it does the same job almost all the time, because it seemed unclear what to do when DOM manipulation needs to be done.

+

Well, it turns out there has always been the local injectable $element, which is basically a reference to the DOM element on which our directive is applied. This reference can and could be used to do DOM manipulation, even in the controller.

+

This still gave some Angular users a weird feeling because there’s this one rule to not do DOM manipulations in the controller. This rule still applies, unless we’re talking about directive controllers. And a component controller is a directive controller.

+

In Angular 1.5 it gets even better, because there’s a lifecycle hook called $postLink(), which not only can be the place where we do all of the DOM manipulation, but it’s also the hook where we know that all child directives have been compiled and linked.

+

We’ll see if there are going to be more lifecycle hooks that Angular 1.5 can take advantage of. We clearly can’t simply backport all of Angular 2’s lifecycle hooks, because the compilation process is not exactly the same, so some lifecycle hooks don’t really make sense in an Angular 1 world.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/angularjs/es6/2015/01/23/exploring-angular-1.3-using-es6.html b/angularjs/es6/2015/01/23/exploring-angular-1.3-using-es6.html new file mode 100644 index 000000000..b5ca075d4 --- /dev/null +++ b/angularjs/es6/2015/01/23/exploring-angular-1.3-using-es6.html @@ -0,0 +1,148 @@ +Using ES2015 with Angular today | Articles by thoughtram

Angularjs

Using ES2015 with Angular today

One of the most exciting upcoming changes for Angular 2.0 is that it’s entirely written in ES2015 - the next version of JavaScript. There’s no need to wait until Angular 2.0 though. You can start writing your Angular apps with ES2015 today - even with Angular 1.x.

+

In this post we are going to look at two very exciting ES2015 features and how they can play a role in the context of an Angular application: Modules and Inheritance.

+

Modules

+

Modules are one of the most exciting features of ES2015. They enable us to decouple our code easier than ever before. The concept isn’t all that new as most programming languages have some kind of module system. Using JavaScript, your best bet so far was to use one of the community projects such as requirejs. With ES2015 a proper module standard is coming right into your browser, natively. With modules code can be structured to explicitly import all dependencies that are used in a file. Before a module can imported it first needs to be exported as a module.

+
function MainController () {
+  
+}
+
+export { MainController }
+

Once a module has been exported it can easily be imported from another file.

+
import { MainController } from './path/to/MainController';
+

Inheritance

+

First things first: Inheritance is one of the most over abused software patterns of all times. If you aren’t family with the difference of Is-A and Has-A relationships, please take a moment and read it up. Similary, if you aren’t aware that you should favor composition over inheritance, please take a moment to read the linked article.

+

Most programmers that are coming from traditional languages such as Java or C# have been using inheritance for many years. In fact, it has been possible to use inheritance with JavaScript for years, too. Using it without further abstractions has been very clunky though. There is a huge number of libraries and frameworks that invent some kind of it’s own DSL to make inheritance more approachable with JavaScript.

+

With ES2015 we don’t need to rely on such non standard abstractions anymore. ES2015 defines a few new keywords and syntax additions that allow for easier inheritance. What’s important to know is that it’s really only sugar on top of the good old prototypal inheritance model that we’ve been using for years.

+

To use inheritance we need to make use of the new class keyword.

+
class Vehicle {
+
+    constructor (name) {
+        this._name = name;
+    }
+
+    get name () {
+        return this._name;
+    }
+}
+export { Vehicle }
+

In the example above we construct a simple Vehicle class which isn’t different from a simple constructor function in ES5. Where the new class syntax really shines is when you want to inherit from another constructor function. Let’s write a Car class that inherits from Vehicle.

+
import { Vehicle } from './Vehicle';
+
+class Car extends Vehicle {
+
+    move () {
+        console.log(this.name + ' is spinning wheels...')
+    }
+}
+export { Car }
+

Seen that? We import Vehicle as a module and extend it by using the new extends keyword. We could have done the same with ES5 but it’s much more boilerplate code. Let’s forget about the nice module seperation for a moment and put both Vehicle and Car into one file for the ES5 version.

+
//Vehicle
+function Vehicle (name) {
+    this._name = name;
+}
+
+Object.defineProperty(Vehicle.prototype, 'name', {
+    get: function () { return this._name; },
+    set: function (value) { this._name = value }
+});
+
+
+// Car
+function Car (name) {
+    Vehicle.call(this, name);
+}
+
+Car.prototype = Object.create(Vehicle.prototype);
+Car.prototype.constructor = Car;
+
+Car.prototype.move = function () {
+    console.log(this.name + ' is spinning wheels...');
+}
+

Oh wow, things really got a lot easier with ES2015, no?

+

Now that we know what modules and inheritance mean in the context of ES2015, let’s take a look at how we can actually use it with Angular today.

+

Angular and ES modules

+

Let’s first look at ES2015 modules in the context of Angular 1.x.

+
class MainController {
+
+    constructor(searchService) {
+        this.searchService = searchService;
+    }
+
+    search () {
+        this.searchService
+            .fetch(this.searchTerm)
+            .then(response => {
+                this.items = response.data.items;
+            });
+    }
+}
+export { MainController }
+

Notice something? There’s no Angular in our controller definition at all. It’s plain old JavaScript code that happens to be in control of something.

+

The spirit of Angular has always been to stay out of the way of the developer as much as possibles. It embraces simple POJOs instead of special Angular object types. With ES2015 modules it’s even easier to excell on that idea. You can write your controller as a simple constructor function and have it exported as an ES2015 module.

+

At some point though, we need to make Angular aware of the controller. Otherwise it just won’t play any role in our Angular application.

+
import { MainController } from './MainController';
+import { SearchService } from './SearchService';
+
+angular
+    .module('app', [])
+    .controller('mainController', MainController)
+    .service('searchService', SearchService);
+

We simply import the MainController in our app.js file that we use to bootstrap our application. In order to register it as a controller, we pass it on to Angular’s controller() method.

+

Angular and ES2015 inheritance

+

The good news is, you already know how to use it! We’ve already seen how easy inheritance becomes with ES2015 in our earlier example. Let’s create a PageController and a ProductPageController whereas the PageController simply defines a title() function that should be available in all controllers that derive from PageController. All it does is that it prepends the string Title: to the instance variable _title.

+
class PageController {
+
+    constructor(title) {
+        this._title = title;
+    }
+
+    title () {
+        return 'Title: ' + this._title;
+    }
+}
+export { PageController }
+

While it’s possible to just set _title to a string from within the constructor of our ProductPageController we are aiming for the cleaner way and instead pass it to the constructor of our PageController by calling super('ES2015 inheritance with Angular');

+
import { PageController } from './PageController';
+
+class ProductPageController extends PageController {
+
+    constructor() {
+        super('ES2015 inheritance with Angular');
+    }
+}
+
+export { ProductPageController }
+

That’s probably not the most exciting example in the world but it works! All there’s left to do is to angularize the ProductPageController.

+
import { ProductPageController } from './ProductPageController';
+
+angular
+    .module('app', [])
+    .controller('ProductPageController', ProductPageController);
+

Please note that we don’t have to do the same with the PageController as long as it’s not explicitly used as an Angular controller. In our case, it’s only used implicitly by the ProductPageController.

+

Edit: Evgeniy asked on G+:

+
+

“how can be ‘title’ argument in constructor of next controller be legit? It doesn’t look like the name of service”.

+
+

*The parameters in our constructor do work together with Angulars DI. In fact, the constructor is not different from a traditional constructor function. But how can title be legit then? The reason for that to work is that the title parameter is only used by the PageController which isn’t registered with myModule.controller(fn). The PageController is only used implicitly by the ProductPageController.*

+

Easy isn’t it? Can we use that for services, too? Yes, we can but there’s a small gotcha. It doesn’t work with services that are defined using the myModule.factory(fn) API but only for those that are defined using myModule.service(fn). That’s because services that are defined using the myModule.service(fn) API are instantiated with the new operator under the hood whereas the others are not. For inheritance to work it’s important that our constructor function is instantiated with new though.

+

There’s one more gotcha pointed out by Evgeniy: When we use ES2015 classes we lose the ability to use explicit dependency annotation with the inline array notation.

+

In order to preserve dependency annotations for minification, we need to use the $inject property notation now:

+

MainController.$inject = ['SearchService'];

+

Getting started with our boilerplate

+

There are plenty of different ways to get started with ES2015 today. The sheer amount of different ways to approach it can be very confusing. At thoughtram we created a boilerplate that makes it quite easy to get rolling. It uses the popular babel transpiler to convert ES2015 to ES5 code that works in all current major browsers. The boilerplate also uses browserify to concat and minify all ES2015 modules into a single file.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/annoucements/2015/06/17/anouncing-hanovers-second-rust-meetup.html b/annoucements/2015/06/17/anouncing-hanovers-second-rust-meetup.html new file mode 100644 index 000000000..783069fe4 --- /dev/null +++ b/annoucements/2015/06/17/anouncing-hanovers-second-rust-meetup.html @@ -0,0 +1,34 @@ +Anouncing Hanover's second Rust meetup | Articles by thoughtram

Annoucements

Anouncing Hanover's second Rust meetup

It’s been roughly a year ago that we organized the very first Rust meetup in Hanover. The meetup was quite a success and ever since then we’ve been asked a lot about a revival.

+

If you haven’t heard about Rust yet here comes a little spoiler.

+

From the Rust website:

+
+

Rust is a systems programming language that runs blazingly fast, prevents almost all crashes*, and eliminates data races

+
+

What makes this language different from any other language is that it aims to provide memory safety without sacrificing performance. It provides modern features like pattern matching, generics, traits etc. while at the same time it’s aimed to eventually replace C/C++.

+

Imagine a world where system programming becomes approachable for the rest of us!

+

Today we are happy to announce the second Rust meetup in Hanover.

+

Here are the facts:

+

When: June 23rd 2015, 19:00
Where: Schwarzer Bär 2, 30449 Hannover

+ +

Schedule:

+
    +
  • Christoph will give a talk about Rust
  • +
  • We will have an open discussion about all things Rust
  • +
+

Don’t worry if you don’t have any prior knowledge in system programming languages like C or C++. We are all beginners. That said, if you do have knowledge in system programming languages, we definitely want you to attend, too! Don’t be late, we might have Rust stickers for you :)

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2014/06/06/we-are-thoughtram.html b/announcements/2014/06/06/we-are-thoughtram.html new file mode 100644 index 000000000..7da8004a8 --- /dev/null +++ b/announcements/2014/06/06/we-are-thoughtram.html @@ -0,0 +1,65 @@ +We are thoughtram | Articles by thoughtram

Announcements

We are thoughtram

Hey there,

+

introducing Pascal Precht and Christoph Burgdorf. We are thoughtram.

+

Since our first mysterious announcement on twitter people +have been wondering what the heck thoughtram.io is.

+

+ + + more ram + +

+

Sorry, folks. No fancy dropbox competitor here. Our mission is something completely different.

+

We enjoy learning new stuff every day and we love teaching it to others even more. In fact, we love it so much that we started the HannoverJS meet-up years ago. From then on we began to discuss tech stuff regularly at meet-ups, conferences and in-company workshops.

+

The purpose of thoughtram is to create a package of training material including workshops (online and offline), webinars, screencasts and scripts with a very strong focus on quality. If you aren’t satisfied with our material, then we aren’t either. Learning new things with thoughtram should be both fun and efficient.

+

We will soon launch our website with a lot more information and the first workshop available for booking. But for now, if you’re interested in boosting your skills - and as someone working in tech you definitely should be - go and subscribe at thoughtram.io for notification of the launch.

+

— Pascal Precht & Christoph Burgdorf

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2014/06/23/announcing-our-first-workshop.html b/announcements/2014/06/23/announcing-our-first-workshop.html new file mode 100644 index 000000000..ef9fcfac6 --- /dev/null +++ b/announcements/2014/06/23/announcing-our-first-workshop.html @@ -0,0 +1,32 @@ +Announcing our first workshop | Articles by thoughtram

Announcements

Announcing our first workshop

As announced earlier this month, we were working on our website to launch it as soon as possible. The time has come!

+

We finally launched our brand new website at thoughtram.io and we are excited to announce our very first workshop:

+

Git Ninja Class!

+

Have you ever wondered what the heck the HEAD is? And do you know how to properly do an interactive rebase without screwing everything up? And if you screwed it up, do you know how to restore things? Don’t worry, you’ll learn all this in this course. This course will make your average workday more productive. Every. Single. Day.

+

For more information, head over to thoughtram.io and save the date, because the first bunch of tickets will be available very soon!

+

— Christoph & Pascal

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2014/07/04/tickets-are-on-sale-now.html b/announcements/2014/07/04/tickets-are-on-sale-now.html new file mode 100644 index 000000000..81058e528 --- /dev/null +++ b/announcements/2014/07/04/tickets-are-on-sale-now.html @@ -0,0 +1,34 @@ +Tickets are on sale now! | Articles by thoughtram

Announcements

Tickets are on sale now!

It’s on! You can now buy the first bunch of tickets for our Git Ninja Class workshop happening on the 6th + 7th of September in Hannover, Germany!

+

What is this workshop about?

+

Git is a free and open source distributed version control system. It’s invention was a blessing to the whole software world. However, it takes a bit of training to become really productive. Now is the best time to level up to become a real git Ninja!

+

What you will learn

+

We split the workshop in two days. The first day should get you started to get on an intermediate level.

+

The second day will teach you all the ins and outs of git to become really productive. For the second day to attend it is important to be on an intermediate level already. That doesn’t mean that you have to attend the first day as a precondition for the second day. If you feel that you have the skills already it will be fine to only attend the second day but you’ll get the most out of it if you attend both days.

+

Take a look at the detailed workshop description to get a feel for the covered topics.

+

Why take this training?

+
    +
  • This course will make you more productive
  • +
  • It will save you lots of time and money. Every day.
  • +
  • You’ll get a six month micro plan for github.com
  • +
  • You get the chance to meet our special guest Mike Adolphs from GitHub
  • +
+

Mike from github

+

Be quick!

+

The first eight day tickets will be sold as a special early bird offer for 350.00 € per day or 650.00 € for both days combined. Make sure to grab your ticket now.

+

Just head over to thoughtram.io

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2014/07/05/git-ninya-class-comes-to-istanbul.html b/announcements/2014/07/05/git-ninya-class-comes-to-istanbul.html new file mode 100644 index 000000000..b584ff180 --- /dev/null +++ b/announcements/2014/07/05/git-ninya-class-comes-to-istanbul.html @@ -0,0 +1,24 @@ +Git Ninja Class comes to Istanbul | Articles by thoughtram

Announcements

Git Ninja Class comes to Istanbul

Today we are happy to announce that our Git Ninja Class is coming to Istanbul!

+

At thoughtram we are commited to run workshops online and offline. Every once in a while we try to bring our offline workshops to a new city. Yes, we love chats and hangouts as well but we also value being physically in one room together.

+

Istanbul is the largest city in Turkey with a vibrant tech scene. We are happy to have a strong partner in Istanbul to support us with all local affairs.

+webbox.io logo +

The workshop will be held on 13th + 14th of September 2014.

+

If you like to attend this workshop please head over to the webbox.io website for more details and booking.

+

Like to see Git Ninja Class in your city? Drop us an email at hello@thoughtram.io

+

— Pascal & Christoph

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2014/11/02/git-ninja-class-in-amsterdam.html b/announcements/2014/11/02/git-ninja-class-in-amsterdam.html new file mode 100644 index 000000000..cf52f4d8f --- /dev/null +++ b/announcements/2014/11/02/git-ninja-class-in-amsterdam.html @@ -0,0 +1,37 @@ +Git Ninja Class in Amsterdam | Articles by thoughtram

Announcements

Git Ninja Class in Amsterdam

Today we are very happy to announce that we will bring our Git Ninja Class to Amsterdam in the Netherlands! Join us for another exciting two-day workshop experience about the true power of the Git version control system.

+

The workshop takes place at the 15th and 16th January 2015 in the wonderful rooms of De Voorhoede at the wonderful Razmataz restaurant. Unfortunately there was a fire in the building where the workshop was supposed to be held, therefore we had to change the location. Here’s an updated map:

+ +
+ +
+ +

Of course, this wouldn’t be possible without a strong helping hand. With De Voorhoede we not only have a first class sponsor, but also a new friend to make future events possible. We are very happy and thankful being able to partner with people that are excited as we are.

+

Tickets are already on sale and until the end of November we offer a special ticket price of 400,00 €; the regular price is 500,00 € so you better be fast sold out!

+

You can find more information about the workshop here.

+

See you in Amsterdam!

+

Pascal & Christoph

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/04/19/upcoming-events-in-2015.html b/announcements/2015/04/19/upcoming-events-in-2015.html new file mode 100644 index 000000000..ac11af5f1 --- /dev/null +++ b/announcements/2015/04/19/upcoming-events-in-2015.html @@ -0,0 +1,37 @@ +Upcoming events in 2015 | Articles by thoughtram

Announcements

Upcoming events in 2015

Just recently, we wrote about how much we love to travel and making friends around the globe. That’s why we’re making this part of our mission. In fact, I’m writing this post while I’m sitting in a hotel in Lugano, Switzerland. Today we’d like to give you information about the next upcoming events that we’ll be running or attending to.

+

Angular Master Class

+

We’ve been running our workshops in several cities including Vienna, Istanbul, Lugano and Amsterdam. However, we are always looking for new places and people that to help us out making our trainings possible.

+

Our Angular Master Class will come to the following cities:

+ +

For general information about our Angular Master Class, visit our dedicated website.

+

Conferences and Meetups

+

We love to attend to conferences, since it’s always a nice experience to get to know new people and make new friends. Every now and then, we also speak at some events. Here’s where you can find us at upcoming conferences.

+
    +
  • Webmontag Bielefeld, Germany, 5th May - More info
  • +
  • JSConf Budapest, Hungary, 14th May - More info
  • +
  • goto; Amsterdam, Netherlands, 17th June - More info
  • +
+

More to come!

+

Even if that is already lot of dates to set your alarms for, we’re not completely done with our planning for 2015 yet. There’s a lot of interest in trainings specifically for testing Angular apps. Also, people are interested in Angular 2.x. We are working on it and will announce some more exciting things very soon.

+

You can always stay up to date by following us on twitter, checking out our events website, or by subscribing to our events on facebook.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2015/05/08/speaking-at-code-talks.html b/announcements/2015/05/08/speaking-at-code-talks.html new file mode 100644 index 000000000..f53b37ab7 --- /dev/null +++ b/announcements/2015/05/08/speaking-at-code-talks.html @@ -0,0 +1,37 @@ +Speaking at code.talks | Articles by thoughtram

Announcements

Speaking at code.talks

Roughly three weeks ago we blogged about our upcoming events for workshops, conferences and meetups. Today we are happy to add two more conference talks to that list. Both of us are going to speak at the code.talks conference in September this year and we are very happy to join an already great lineup of speakers.

+
+ code talks logo +
+

Here are two little spoilers to give you an idea what our talks will be about.

+

Dependency Injection for future generations

+

We’ve spent the last month digging a lot through the Angular 2.x design documents and source code. In fact we already started to build an app with Angular and are contributing back to the project itself.

+

In this talk Pascal will share his learnings about the new dependency injection system in Angular 2.x. The talk will be held the 29th September from 16:00 to 16:45 on track 2 and that’s the abstract.

+
+

Because we think dependency injection is cool and that it makes life of all us developers a looot easier, we want to start off our talk by first understanding what dependency injection stands for and why it is such a blessing in disguise…and also because first things first. Once that is clear we can dive deeper into some existing implementations, to get a real feel of how it all works, and finish by looking at how these implementations are used inside the new Angular framework, by taking advantage of decorators using TypeScript.

+
+

Rust - Rethinking Systems Programming

+

Rust is a new exciting systems programming language by Mozilla and it quickly took Christoph’s heart early last year. Over the last couple of months he’s been busy creating libraries such as nickel for building web applications and clog for generating nice changelogs. In fact there’s grown quite a nice community with many active contributors around those libraries.

+

The talk is happening the 29th September from 13:00 to 13:45 on track 2 and that’s the abstract.

+
+

Rust is a new exciting system programming language by Mozilla, developed for fast and safe system applications. Even though the language is still in beta, you can start building stuff with it today! In fact, Christoph started the nickel.rs project, which aims to be a blazingly fast, easy to use web application framework for Rust to build fully featured web applications. In this session Christoph will show you why you want to learn Rust and how it can be used for real world web programming..

+
+

Make sure to find us at the conference to talk all things Angular and Rust!

+

Your chance to get 50% off!

+

We have a couple of coupon codes to give a way that will get you the conference ticket for half the price. But that’s not happening without a little fun so make sure to follow us on twitter and facebook to watch out for a little raffle.

+

Also make sure to checkout our events website for other interesting events to meet us.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/05/11/sponsoring-angularconnect.html b/announcements/2015/05/11/sponsoring-angularconnect.html new file mode 100644 index 000000000..47e12040a --- /dev/null +++ b/announcements/2015/05/11/sponsoring-angularconnect.html @@ -0,0 +1,47 @@ +Sponsoring AngularConnect | Articles by thoughtram

Announcements

Sponsoring AngularConnect

Today we are very happy to announce that we’re going to sponsor AngularConnect, the official Angular conference in Europe, taking place on the 20th and 21st October in London this year. Not only that you can find us during the conference, we’re also going to run a workshop the day before to help you getting started with Angular and being perfectly prepared for the event.

+

+ + + angularconnect + +

+

The workshop will be a single-day training on the 19th October and mostly cover the first part of our Angular Master Class. However, we’re very flexible in interchanging specific topics, so you as an attendee get to learn what you want to learn! If you’re interested in attending AngularConnect, head over to their website and make sure to get a ticket.

+

We are looking very forward to meeting you there at both, the workshop and the conference!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2015/08/31/going-full-time.html b/announcements/2015/08/31/going-full-time.html new file mode 100644 index 000000000..3fea9cf63 --- /dev/null +++ b/announcements/2015/08/31/going-full-time.html @@ -0,0 +1,56 @@ +Going full-time | Articles by thoughtram

Announcements

Going full-time

Time is a weird thing. When you are doing something you love, it just seems to rush by. Over one year has passed since we officially announced thoughtram, a project to create high quality training.

+

Back then Pascal and I were both employed as software engineers at CouchCommerce where we had a stable job, cool technical challenges, lovely and talented co-workers, a modern tech stack and very competitive salaries.

+

When we started thoughtram it was really just a project on the side for us to share our knowledge on another level. We had already spoken a lot at meetups and conferences so it was logical for us to expand on that and run workshops. We didn’t even bother to register a company when we started. We ran our first workshop on Git in September 2014 and the feedback was so overwhelmingly great that we founded thoughtram as a company right after that in October 2014.

+

Since then and especially after we announced our Angular Master Class course demand sky-rocketed and we had far more requests for workshops than we were able to deal with alongside our full-time jobs.

+

Over the last 12 months we have given workshops and talks in countries such as the US, Thailand, the Netherlands, Belgium, Turkey, Austria and Hungary. And actually we’re just getting started.

+

As of today we are proud and happy to announce that from the 1st of September on we are going to work full-time for our own company thoughtram.

+

+ + + Christoph & Pascal + +

+

We would like to thank our previous co-workers for the wonderful time spent together. It was great to help CouchCommerce to get where it is right now. It has a bright future ahead and continues to be an awesome company to work for (hint: they are always looking for talents to join the team!).

+

On to something new! We are excited to dedicate all our time to our baby that is thoughtram. We are excited to travel to more places to spread our workshops across the globe. We are excited to be able to spend more time working on the open source projects that we love. We are excited to meet you!

+

It’s great to see so many people spreading the love about throughtram either on Twitter, their own blogs or Facebook. We would like to point out that what you’ve seen so far is the work of a two-man project that we did on the side. It will only get better now that we are devoting all our time to it.

+

We have lots of cool stuff in the pipeline that we are eager to share with you soon. Some of it we can’t share publicly yet and others we’ll announce with dedicated blog posts in the near future. Stay tuned!

+

Christoph & Pascal

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/09/14/angular-training-day-bangkok.html b/announcements/2015/09/14/angular-training-day-bangkok.html new file mode 100644 index 000000000..7b6a3e53b --- /dev/null +++ b/announcements/2015/09/14/angular-training-day-bangkok.html @@ -0,0 +1,30 @@ +Angular Training Day Bangkok | Articles by thoughtram

Announcements

Angular Training Day Bangkok

Today we are happy to announce that we will run a free Angular Training Day in Bangkok on the 29th of October.

+

When we’ve been to Bangkok in April this year to give talks about Angular and Rust we were overwhelmed by the kindness of the local tech community.

+

Angular Meetup BKK

+

We made a promise to come back later this year to spend an entire day teaching Angular. Here we are making our promise a reality with our free Angular Training Day.

+

Schedule

+

We don’t have a fixed schedule. We have material to teach a whole week about Angular 1 and 2. Including lots of slides, running examples and exercises. Unfortunately there really is just this single day available for this event. We will let you drive our schedule. No matter if you are a beginner or a proficient Angular developer, we’ll try to make this event both fun and beneficial for all of you.

+

When & Where

+

When: 29th of October 2015 from 10 am to 5 pm
+Where: Growth cafe & co., Siam Square Soi 2, Bangkok, Thailand

+ +

Booking

+

The event is 100% free but seats are limited. To register for the event head over to eventbrite or directly book from here.

+ +

Do you like to see a thoughtram event in your city? Drop us a line at hello@thoughtram.io and let us know!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/10/08/pascal-becomes-a-gde.html b/announcements/2015/10/08/pascal-becomes-a-gde.html new file mode 100644 index 000000000..b4e4f7e65 --- /dev/null +++ b/announcements/2015/10/08/pascal-becomes-a-gde.html @@ -0,0 +1,59 @@ +Pascal becomes a GDE | Articles by thoughtram

Announcements

Pascal becomes a GDE

As I’m writing this I’m sitting in the train from Berlin to Hanover after returning from the AngularJS Days. We’ve been at the event to run a workshop about Angular 2. The workshop had about roughly 80 attendees and we’ve been overwhelmed by all the positive feedback afterwards.

+

Reflecting about how we prepared the workshop it kind of makes me feel honored to run this company with my friend Pascal and be on this exciting journey with him.

+

+ + + Pascal at the Angular Days + +

+

Pascal is that kind of person who cares about all the little details. He is very passionate about all kind of web technologies and makes a huge effort to keep up with the rapid pace of innovation.

+

There’s rarely ever a day where he doesn’t engage in discussions on GitHub, Slack or Gitter. He’s traveling a lot to share his knowledge at conferences and meetups and when he’s not busy preparing his next conference talk he’s probably working on some article to share on this blog. It sometimes makes me wonder how he’s able to push things at this pace with just two arms and legs.

+

Today I’m happy to announce that this level of engagement was acknowledged by Google by officially making Pascal a member of the Google Developer Experts program.

+

In case you are wondering what it means to be a GDE here’s a spoiler from the official website.

+
+

Google Experts are experienced, recognized developers of Google technologies as well as outstanding professionals in product strategy, UX/UI, marketing, growth hacking and monetization. They distinguish themselves through frequently speaking at conferences, share their passion and experience by publishing videos and tutorials, writing code samples, mentoring developers and startups and much more. Thanks to their support, developers, high-potential startups and technical communities around the world build and launch highly innovative apps.

+
+

To become a GDE one has to be selected by another GDE and undergo a stringent evaluation process.

+

With Pascal being an GDE now, we are also kind of an interface between the community and the core Angular team at Google, which enables us to provide an even better experience for our workshops.

+

Pascal isn’t the type of person who’s much affected by titles so becoming a GDE alone probably won’t change much of his life but still I’m happy and proud of him and say: Well deserved my friend!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/10/26/angular-master-class-extended-ngupgrade.html b/announcements/2015/10/26/angular-master-class-extended-ngupgrade.html new file mode 100644 index 000000000..246a6edc3 --- /dev/null +++ b/announcements/2015/10/26/angular-master-class-extended-ngupgrade.html @@ -0,0 +1,22 @@ +Angular Master Class Extended: ngUpgrade | Articles by thoughtram

Announcements

Angular Master Class Extended: ngUpgrade

At thoughtram, we are constantly working on making our training experience better and better. Our primary goal is to have fun with our training participants and that everyone is happy and satisfied with what they get when we work together. We’re constantly receiving great feedback. Some participants even say that with us, they’ve got the best training they’ve ever experienced, which makes us very happy. And even though, this is already great, we think we can take it even further.

+

Earlier this year, we were very excited to announce that we took our Angular Master Class to the next level, by listening to you and extending the program with additional topics like Forms, Server Communication, Performance, or Advanced Routing. Guess what, we didn’t stop there! At this year’s AngularConnect event, we not only were thrilled to be an official sponsor of the conference, we also had the honour to run a single-day workshop on upgrading to Angular 2 with bleeding edge content and material.

+

+

Today we’re happy to announce that upgrading to Angular 2 is now an official module of our Angular Master Class.

+

We wrote about everything we learned here and we updated our dedicated website. The Angular Master Class material will get an update as well, so again, everyone who already attended to one of our classes gets the update for free. Don’t hesitate to reach out to us though, as we’re already receiving new requests for in-house trainings.

+

Happy upgrading!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2015/11/19/how-we-run-trainings.html b/announcements/2015/11/19/how-we-run-trainings.html new file mode 100644 index 000000000..0a6e097ab --- /dev/null +++ b/announcements/2015/11/19/how-we-run-trainings.html @@ -0,0 +1,42 @@ +How we run trainings | Articles by thoughtram

Announcements

How we run trainings

Since we started with thoughtram we have ran plenty of trainings in many different cities and countries. After each workshop we sat down to reflect and discuss the feedback we got. We fine tuned our setup and material again and again over the last couple of months.

+

It turned out that people really love the way we run our trainings. Many people told us that they attended lots of different workshops but never quite something close to what they experienced with us. We feel flattered by all the positive feedback

+

While we do have some information about our Angular Master Class and Git Master Class on our website we thought you may be interested to get a more in depth view about how we actually run our workshops.

+

It’s all modular

+

Just like good software our workshops are organized in a modular fashion. For Angular 1 we have material for up to five days but the modularity of our material enables us to orchestrate topics to bundles of 2 or 3 days length. For in-house workshops the customer is in total control about the planned topics. For public workshops we obviously have to follow the advertised schedule but even then we often have enough room to let the audience decide between a selection of extra topics.

+

The digest cycle

+

Learning new things can be really exhausting. We know it. In order to keep everyone alert and make learning fun we are iterating in a cycle that goes like this.

+
    +
  1. We explain things theoretically using our slide material.
  2. +
  3. We do live demos
  4. +
  5. We let the attendees solve exercises about what they just learned
  6. +
+

While one of us is taking the lead as an instructor the other one is observing the chat room to pick up questions or to share links. Since Pascal and I are running most workshops together we usually toggle our roles multiple times per day.

+

Fun fact: We play music during the exercises blocks and we haven’t had a single workshop where no one came to ask for the playlist!

+

classroom

+

Our slides are built up on reveal.js and while all attendees have access to our slide repositories building and serving the slides by yourself can be a bit cumbersome and simply is a distraction to the attendee. When we started to run workshops we gave out PDFs to our attendees so that they can lookup things in the slides while they try to solve the exercises.

+

From the feedback we got, we knew that would be something where we could definitely do better.

+

That’s why we built classroom. It’s our very own platform to serve our workshop material to the attendees. With classroom, attendees can easily view all slides right from their browser. One thing to highlight here is that attendees get access to all slide decks of the master class even to those that weren’t covered in that particular workshop. Also we constantly keep updating and adding new slide decks and attendees automatically have access to the improved material through classroom, too. For example, we just announced new material that teaches how to upgrade from Angular 1 to Angular 2.

+

+ + +

+

When we started to use classroom for our workshops we noticed that the exercise blocks went much smoother. People had an easier time looking up things in the slides and even started sharing links to specific slides in the chatroom (which isn’t part of classroom yet).

+

Authentication for classroom is done via GitHub OAuth and for now is only accessible for people who attended one of our workshops. The project is pretty much in an alpha state but we are rolling with the vibe of release early, release often. The frontend is written in Angular 1 using TypeScript and a component oriented approach. You may want to checkout the code on GitHub. The backend is written in Rust using nickel.rs but is not yet publicly available.

+

In the future we may plan to open up classroom for a broader audience. For now we see it more as a supportive tool for our workshops rather than a polished product to share with the masses though.

+

If you like us to run a workshop in your city just drop us a line at hello@thoughtram.io and get in touch!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2015/12/21/introducing-angular-2-master-class.html b/announcements/2015/12/21/introducing-angular-2-master-class.html new file mode 100644 index 000000000..9a5843e2f --- /dev/null +++ b/announcements/2015/12/21/introducing-angular-2-master-class.html @@ -0,0 +1,57 @@ +Angular 2 Master Class: Jump Start | Articles by thoughtram

Announcements

Angular 2 Master Class: Jump Start

With the beta release of Angular 2 and the end of the year right around the corner, we’re happy to share some very exciting news with you. As our Angular Master Class has been a huge success in 2015, we’re about to bring a new experience to the table in 2016:

+

Angular 2 Master Class!

+

Just like the previous edition, Angular 2 Master Class is an evolving experience provided as on-site training to level up your skills in Angular 2 as efficient as possible

+

With “Jump Start” we want to debut and roll out our new material for the very first time to give you a nice overall picture about the next version of the framework.

+

Join us at the 26th January in the beautiful city of Berlin (grab your ticket here)!

+

Here are the facts:

+

When?
+26th January 2016

+

Where?
+Betahaus
+Prinzessinnenstr. 19-20
+10969 Berlin
+Germany

+ +

What’s the format, what will we learn?
+Angular 2 Master Class: Jump Start is, as the name says, the start. It’s the first part of the entire Angular 2 Master Class experience and we will spend one full day building our first Angular 2 application, touching the most important parts of the framework.

+

We are going to learn…

+
    +
  • how to bootstrap an application and display data.
  • +
  • how to create and inject services using the new Dependency Injection.
  • +
  • how to perform http calls using the revamped http layer based on observables.
  • +
  • how to implement basic routing using the new component router.
  • +
  • how to create simple forms that take advantage of two-way data binding.
  • +
+

After this training we’ve built an Angular 2 application covering all the learnings we gained throughout the day.

+

Who is this training for?
+Angular 2 is in beta and most of the APIs are stable, however, things are still moving. This training is for absolute beginners as well as experienced developers coming from the AngularJS world, who can’t wait to get their feet wet with the next version of the framework.

+

What you will take home
+As an Angular 2 Master Class attendee you’ll get life-long access to all current and future slide decks via our exclusive learning platform classroom. Same goes for all our private repositories with tons of examples, exercises and solutions.

+

What do we need?
+To ensure we all and especially you have fun during the day, the following things are needed:

+
    +
  • At least a little JavaScript and HTML experience - we’ll do TypeScript though
  • +
  • A laptop with Node/npm and Git installed - unfortunately we can’t provide machines, but you can totally work with someone (or more) in a group
  • +
  • A GitHub account - This is used to distribute our material and give you access to classroom. Not an absolute requirement, but it gives you the best experience possible.
  • +
  • Good mood!
  • +
+

Get your tickets

+

The first batch of 10 tickets (5 Early Birds, 5 Regular) is on sale right now, so better be fast because seats are limited!

+ +

We’re going to roll out more tickets over the next few weeks, if you want to stay up-to-date, make sure to follow us on Twitter and/or Facebook.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2015/12/31/angular2-master-class-at-ng-nl.html b/announcements/2015/12/31/angular2-master-class-at-ng-nl.html new file mode 100644 index 000000000..d1060454a --- /dev/null +++ b/announcements/2015/12/31/angular2-master-class-at-ng-nl.html @@ -0,0 +1,50 @@ +Angular 2 Jump Start at NG-NL 2016 | Articles by thoughtram

Announcements

Angular 2 Jump Start at NG-NL 2016

This year, the very first Angular conference in Amsterdam took place and it was a huge success. The conference aims to be mainly driven by the community and focusses not only on technical, but also inspiring talks. Christoph and I have been part of it as speakers and we talked about the best Angular yet which was really fun!

+

+ + + ng nl 2016 + +

+

A couple of days ago, we announced the debut of the Angular 2 Master Class: Jump Start in Berlin and guess what, we don’t stop there. Today we’re excited to announce that we’re going to run our Angular 2 Master Class: Jump Start at NG-NL 2016!

+

One day before the conference, we’ll be building a complete application making you familiar with the most important concepts and APIs of the framework. Tickets are limited so if you want to join us, make sure to grab yours.

+

We’re looking very forward to meeting all of you there, let’s have a great time!

+

We also wish everyone a very happy new year.

+

Pascal & Christoph

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2016/02/10/sponsoring-angularconnect-again.html b/announcements/2016/02/10/sponsoring-angularconnect-again.html new file mode 100644 index 000000000..cfc360b5c --- /dev/null +++ b/announcements/2016/02/10/sponsoring-angularconnect-again.html @@ -0,0 +1,25 @@ +Sponsoring AngularConnect. Again. | Articles by thoughtram

Announcements

Sponsoring AngularConnect. Again.

AngularConnect has been an awesome conference with many people of the Angular community, coming from all over the world to get together, listen to excellent talks and make new friends. Last year we sponsored the conference as we believe in the spirit of the Angular world and we always try the best we can do to push and support the community.

+
+ +
+

Today we are very happy to announce that we’re going to sponsor AngularConnect again! It’s going to take place on the 27th and 28th September in London. And of course, we won’t just show up there without doing anything special.

+

We’ll be running our “Angular 2 Master Class: Jump Start” one day before the conference, in which you’ll have the chance to learn how to build your first Angular 2 application!

+

The training will be a single-day training covering the introductory part of our Angular 2 Master Class experience. However, we will have material for tons of topics, so if you want to dive into anything specific, don’t hesitate to ask!

+

Tickets will be on sale soon, so you should go to angularconnect.com and watch this space. If you can’t afford a ticket, even better. Go and submit a talk about your learnings, experiences and thoughts on anything Angular.

+

We are looking very forward to meeting you there at both, the Training and the conference!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2016/04/15/updates-and-announcements.html b/announcements/2016/04/15/updates-and-announcements.html new file mode 100644 index 000000000..1667b2496 --- /dev/null +++ b/announcements/2016/04/15/updates-and-announcements.html @@ -0,0 +1,91 @@ +Updates and announcements | Articles by thoughtram

Announcements

Updates and announcements

It became a little bit quite around us and you might be wondering what thoughtram is doing these days. In fact, we’re pushing hard on different projects, improving our material, working on open source and today we’d like to share some updates with you, as well as some very cool announcements.

+

Trainings at ng-conf

+

We’re super thrilled and excited to announce that we’re not only a sponsor of the one and only ng-conf this year, we’re also the official training partner and going to run two Angular 2 Master Class: Jump Start trainings the day before the conference!

+

As you might know, we usually run our trainings with two trainers. Since the classes at ng-conf will be run in parallel, we get support from the following two great community members:

+

Thomas Burleson

+

+ + + thomas burleson + +

+

Thomas is not only part of the Angular Material team and ensures that you can build crisp user interfaces in your Angular applications, he’s also an all-around nice guy that hanging around with is just pure fun.

+

Gerard Sans

+

+ + + gerard sans 1 + +

+

You might have seen Gerard at one or the other conference talking about Angular 2. He’s a Google Developer Expert for Angular, loves running workshops and he’s a super fun guy too!

+

We’re super happy to have these two great community members as partners to give you the best training experience we can. In addition, there’ll be a handful other Google Developer Experts that will assist during the trainings. Or, in other words: You’ll get in touch with many many great and experienced people.

+

Get weekly updates on 5 things Angular

+

Staying up to date with the latest and greatest in the Angular world is hard nowadays, as things are moving super fast and many things are happening around the framework development too. In order to create a channel where the community gets updates on just the important things that happened in the Angular world, we started a new project called 5thingsAngular.

+

For more information about the idea, please read issue #0.

+

Angular CLI now part of Angular 2 Master Class

+

The last couple of weeks we’ve spent a lot of time getting an idea of the new Angular CLI tool, trying out its commands and evaluated if it’s mature enough to become part of our Angular 2 Master Class experience.

+

Today we’re super happy to announce that Angular CLI is now baked into our Angular 2 Master Class experience! This means you’ll not only build a full application throughout our trainings, you’ll also get familiar with the CLI tool and learn how to generate components and services, and scaffold applications.

+

If you want to get more information on what’s part of our Angular 2 Master Class, head over to our official website.

+

Angular 2 Master Class Starter Kit

+

Another nice addition we’ve been working on is the Angular 2 Master Class Starter Kit. We experienced many issues with the setup of existing starter repositories, especially when it comes to getting the build environment working across platform. Angular 2 Master Class Starter Kit should solve these issues. It’s publicly available, so everyone who’s about to attend one of our trainings, can install and run it before the actual training starts.

+

Preparation Guide

+

To make the startup phase in our trainings even faster, we’ve written a small preparation guide to help you out setting up your machine for any of our trainings.

+

We believe that this guide and the Starter Kit and the CLI tool will make the experience of our trainings even better.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2016/05/19/thomas-joins-thoughtram.html b/announcements/2016/05/19/thomas-joins-thoughtram.html new file mode 100644 index 000000000..c3750b3da --- /dev/null +++ b/announcements/2016/05/19/thomas-joins-thoughtram.html @@ -0,0 +1,65 @@ +Thomas joins thoughtram | Articles by thoughtram

Announcements

Thomas joins thoughtram

Nearly two years passed since we announced thoughtram on this blog. It’s been an amazing journey for us. We ran a lot of trainings all over the globe, we contributed to exciting projects and we met many people. Some of them even turned into close friends.

+

Many of these people asked us if they could join our company to work with us. So far our answer has always been “no” (we were very polite though!). We just like the idea to keep things simple and small. It gives us lots of freedom and keeps things more manageable.

+

Meet Thomas

+

Things changed slowly but steady once we met Thomas.

+

+ + + thomas high res + +

+

You may know Thomas already from his work as the Team Lead developer on Angular Material, ngMaterial Workshops, and the Material-Adaptive solutions. He is very smart, organized and more importantly tons of fun! We had the chance to hang out, talk, laugh and work as a group of three over the last couple of months. We then explored ways of collaboration and most visibly ran one of the full-day thoughtram trainings at ng-conf together.

+

Today we are very happy to announce that Thomas officially joins our team to work with us. +He will continue to lead the Angular Material v1.x team. And he will also work with us to help build our content, business and deliver thoughtram trainings in the US and APAC regions.

+

More trainings, more articles, more fun

+

With Thomas on board you may be wondering what to expect from us in the future. Well, if you liked our work so far be sure you’ll like our future work even more. Thomas already gave us a bunch of great ideas and influenced our current training experience behind the scenes.

+

So far, it’s been only the two of us to run the trainings and we know many of you would like to see us coming to their company or city. This is especially true for the US where we seem to have a lot of fans.

+

With Thomas on board we are happy to announce that we’ll now run many more trainings in the US than ever before. You may also expect more articles and different topics to be covered on our blog as well as in our workshops.

+

If you like to give Thomas a warm welcome you might want to include @thomasburleson in a tweet.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2016/06/23/updates-and-announcements.html b/announcements/2016/06/23/updates-and-announcements.html new file mode 100644 index 000000000..b7bd759cd --- /dev/null +++ b/announcements/2016/06/23/updates-and-announcements.html @@ -0,0 +1,131 @@ +Updates and announcements | Articles by thoughtram

Announcements

Updates and announcements

Today we like to bring you up to date with all the exciting things that we were working on over the last couple of weeks.

+

The search bar

+

Two months ago we rolled out a new design for our blog to make discovering all our different articles a whole lot easier. The feedback we got is overwhelming and we are thrilled to see how many people read and love our blog. Just recently we hit 170k sessions per month.

+

+ + + 170k monthly sessions + +

+

With that in mind we thought it would be nice to make it simpler to search for articles on our blog. Say hello to the new search bar feature which makes finding articles on our blog easier than ever before.

+

+ + + search bar feature + +

+

Meet us at thoughtram TV

+

Many of you asked us to share more information about our process to learn and teach Angular, organize workshops, write articles and more. We are listening and are excited to share more of these kind of things with you in our new format thoughtram TV.

+

In the first episode of thoughtram TV you’ll get to see parts of an internal review call where we discuss how to improve the setup experience for the attendees of our workshops.

+

Don’t forget to subscribe to our channel on YouTube!

+ +

Angular 2 Master Class: Helsinki

+

We’re thrilled to announce yet another public Angular 2 Master Class. It’s in Helsinki and it’s gonna be epic. The venue for this event is actual an old fortress on an Island. It’s an UNESCO world heritage site and will give this event an unforgettable touch. Shout out to Panu from frantic who helps us organizing the event and pointed us to this beauty.

+

+ + + Suomenlinna + +

+

The event takes place 5th and 6th of September 2016 and you can get your ticket right now, right here. Better be quick, there’ll only be 5 early bird tickets for a limited time!

+ +

Checkout the event page for a more detailed description.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2016/08/30/announcing-angular-2-master-class-in-nyc.html b/announcements/2016/08/30/announcing-angular-2-master-class-in-nyc.html new file mode 100644 index 000000000..f302e0b8a --- /dev/null +++ b/announcements/2016/08/30/announcing-angular-2-master-class-in-nyc.html @@ -0,0 +1,54 @@ +Announcing Angular 2 Master Class in NYC | Articles by thoughtram

Announcements

Announcing Angular 2 Master Class in NYC

As recently posted on Facebook, we have some very exciting news to share with you today:

+

We are super happy and excited to announce, that we’ll bring our Angular 2 Master Class to the beautiful city of New York!

+

Event Banner

+

The event takes place on the 14th to 16th November and we’ll announce the exact location very soon. Wait, 3 days? Yes! With this Angular 2 Master Class in New York City, it’ll be the first time, we’ll offer an optional third training day, allowing us to cover even more topics on Angular 2! In just 3 days you will get more than 22 hours of hands-on, developer training for the Angular 2 platform. It’s up to you if you want to get an awesome 2-day training experience, or if you want to go one step further and gain even more knowledge with the third day.

+


+

“Young-Minds” Free Training

+

We have also decided to encourage and inspire the next generation of developers… we think of this as a pay-forward, investment for the future of Angular!

+

With our new A2MC Training Sponsorship, we will encourage teenagers to continue their exploration & skills development with Web app technologies and the Angular 2 platform. +There simply is no better way to quickly ramp up on Angular 2!

+

Do you know a young teenager who has impressed you with their interest and beginner-skills in Web app development? Do you want to inspire them to continue their amazing journey with software development?

+

youngmindstraining

+

For this training, we will give away three (3) FREE tickets to our Angular 2 Master Class. Just tweet why you think your nominee should get a ticket! Mention that person’s twitter handle or email in the tweet. Include hashtag #A2MC_YoungMinds.

+

The nominee must be between at least 13 years old and younger than 18 years of age: high school students. And all candidates must be nominated by the Angular community.

+

For example:

+
+
+

“I nominate @SomeOne because she’s the youngest Angular Material contributor #A2MC_YoungMinds

+
+
+

For each of our future public trainings, we promise to continue this sponsorship program… to promote young minds and the Angular platform.

+


+

Tickets on sale now

+

The first round of tickets is on sale now. First come, First serve! Seats are limited so you better be fast! +Come to our training… have a blast and get the best acceleration into Angular 2 possible.

+

You can find more information on our official event page.

+ +

See you in New York City!

+

Pascal, Thomas & Christoph

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2016/10/30/announcing-angular-2-master-class-in-sydney.html b/announcements/2016/10/30/announcing-angular-2-master-class-in-sydney.html new file mode 100644 index 000000000..cc8808671 --- /dev/null +++ b/announcements/2016/10/30/announcing-angular-2-master-class-in-sydney.html @@ -0,0 +1,53 @@ +Announcing Angular 2 Master Class in Sydney | Articles by thoughtram

Announcements

Announcing Angular 2 Master Class in Sydney

Just a couple of weeks ago we announced to bring our Angular 2 Master Class to New York City. And now in fact it’s happening in just about two weeks with only very few tickets left.

+

Today we are incredibly excited to announce that we’ll bring our Angular 2 Master Class to the land of kangaroos and…sharks! Yes, that’s right we are coming to Sydney!

+

The event takes place on the 9th to 11th January 2017 and we’ll announce the exact location very soon. Like in NYC we’ll offer an optional third training day, allowing us to cover even more topics on Angular 2! In just 3 days you will get more than 22 hours of hands-on, developer training for the Angular 2 platform. It’s up to you if you want to get an awesome 2-day training experience, or if you want to go one step further and gain even more knowledge with the third day.

+


+

“Young-Minds” Free Training

+

As recently announced, we are committed to encourage and inspire the next generation of developers… we think of this as a pay-forward, investment for the future of Angular!

+

With our new A2MC Training Sponsorship, we will encourage teenagers to continue their exploration & skills development with web app technologies and the Angular 2 platform. +There simply is no better way to quickly ramp up on Angular 2!

+

Do you know a young teenager who has impressed you with their interest and beginner-skills in web app development? Do you want to inspire them to continue their amazing journey with software development?

+

youngmindstraining

+

For this training, we will give away three (3) FREE tickets to our Angular 2 Master Class. Just tweet why you think your nominee should get a ticket! Mention that person’s twitter handle or email in the tweet. Include hashtag #A2MC_YoungMinds.

+

The nominee must be between at least 13 years old and younger than 18 years of age: high school students. And all candidates must be nominated by the Angular community.

+

For example:

+
+
+

“I nominate @SomeOne because she’s the youngest Angular Material contributor #A2MC_YoungMinds

+
+
+

For each of our future public trainings, we promise to continue this sponsorship program… to promote young minds and the Angular platform.

+


+

Tickets on sale now

+

The early bird round of tickets is on sale now. You can save up to $125.00 so you’ll better be fast. Seats are limited!

+

Come to our training… have a blast and get the best acceleration into Angular 2 possible.

+

You can find more information on our official event page.

+ +

See you in Sydney!

+

Pascal, Thomas & Christoph

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2017/02/01/announcing-angular-master-class-in-freiburg.html b/announcements/2017/02/01/announcing-angular-master-class-in-freiburg.html new file mode 100644 index 000000000..daee5fa05 --- /dev/null +++ b/announcements/2017/02/01/announcing-angular-master-class-in-freiburg.html @@ -0,0 +1,77 @@ +Announcing Angular Master Class in Freiburg | Articles by thoughtram

Announcements

Announcing Angular Master Class in Freiburg

We just came back from Sydney and are still adjusting to the Central European Timezone, but this doesn’t keep us from sharing the exciting news with you that we’ll be running our Angular Master Class in Freiburg!

+

+ + + humboldtsaal + +

+

The event takes place on the 13th to 15th March 2017 at the Humboldtsaal in Freiburg, where we will have three (3) days of fun and learning the ins and outs of Angular including things like

+
    +
  • Components and Modules
  • +
  • Fetching data using Http
  • +
  • Advanced Dependency Injection
  • +
  • Template-driven Forms
  • +
  • Reactive Forms
  • +
  • Observables deep-dive
  • +
  • Children routes and lazy-loading
  • +
  • Architectural patterns
  • +
  • ContentChildren and ViewChildren
  • +
+

…and many more!

+

Tickets on sale now

+

The early bird round of tickets is on sale now. You can save up to 200,00 € so you’ll better be fast. Seats are limited!

+

Come to our training… have a blast and get the best acceleration into Angular possible. You can find more information on our official event page, or grab your ticket right here!

+ +

See you in Freiburg!

+

Pascal, Christoph & Dominic

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2017/02/09/dominic-joins-thoughtram.html b/announcements/2017/02/09/dominic-joins-thoughtram.html new file mode 100644 index 000000000..57dabe455 --- /dev/null +++ b/announcements/2017/02/09/dominic-joins-thoughtram.html @@ -0,0 +1,67 @@ +Dominic joins thoughtram | Articles by thoughtram

Announcements

Dominic joins thoughtram

It’s been almost one year since we announced that Thomas joins thoughtram to help us cope with the growing demand for thoughtram trainings, code reviews and consulting.

+

Since then it’s been a fun ride and we’ve done lots of work together in the US, Europe, Australia and even New Zealand.

+

People who follow us closely will notice that we deliberately take it very slowly when it comes to growing the company. That said, the demand for our trainings keeps growing at a fast pace and we get requests from literally all around the world. Our trainings stand for high quality, up to date content spiced up with a very fun and casual style of presentation.

+

We see it as our mission to deliver the best experience we can. In order to keep the quality of our work very high we pick the people we want to work with very carefully.

+

Meet Dominic

+

We’ve first met Dominic almost two years ago as an attendee of one of our classes on Angular 1.x. Since then, we’ve seen him growing into a very passionate and skilled member of the Angular community. He’s also tons of fun and we love hanging out with him. 🎉

+

People keep asking if his last name “Elm” is real or if he’s just a big fan of the Elm programming language. We think the reality is that Elm took a lot of inspiration from Dominics last name!

+

+ + + dominic + +

+

Over the last couple of months we’ve had the chance to closely work with Dominic and co-run trainings with him all across Europe.

+

Today we are very happy to announce that Dominic officially joins our team to work with us.

+

If you’d like to see Dominic in action go and grab your ticket either for our upcoming Angular Master Class Freiburg or for and in-depth workshop on forms at ng-conf in Salt Lake City.

+

More courseware, trainings and articles

+

We are proud to have Dominic on our team and look forward to the things we will achieve together. We’ve already kickstarted working on new courseware with Dominic to cover more and more topics for all you hungry minds out there!

+

Dominic will also help us to publish more articles on our blog as well as run more trainings all around the world.

+

You may want to give a warm welcome to him and include @elmd_ in a tweet.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2017/05/05/announcing-angular-master-class-in-denmark.html b/announcements/2017/05/05/announcing-angular-master-class-in-denmark.html new file mode 100644 index 000000000..20077ddd4 --- /dev/null +++ b/announcements/2017/05/05/announcing-angular-master-class-in-denmark.html @@ -0,0 +1,77 @@ +Announcing Angular Master Class in Denmark | Articles by thoughtram

Announcements

Announcing Angular Master Class in Denmark

Today we are excited to announce our next public Angular Master Class way up north in Denmark! We couldn’t be happier to finally run our training in the country where Bjarne Stroustrup and Anders Hejlsberg were born!

+

+ + + foss analytics a s office + +

+

The event takes place on the 20th to 22th June 2017 at Foss in Hillerød about 35 km out of Copenhagen. We couldn’t be happier to have Foss as a strong partner to help us bring our famous training to Denmark.

+

We will have three (3) days of fun and learning the ins and outs of Angular including things like

+
    +
  • Components and Modules
  • +
  • Fetching data using Http
  • +
  • Advanced Dependency Injection
  • +
  • Basic Forms
  • +
  • Observables deep-dive
  • +
  • Architectural patterns
  • +
  • ContentChildren and ViewChildren
  • +
  • Redux and ngrx
  • +
+

…and many more!

+

Yes, that’s right: This will be the first public training where we debut our brand new courseware on Redux and ngrx.

+

Tickets on sale now

+

The early bird round of tickets is on sale now. You can save up to 125,00 € so you’ll better be fast. Seats are very limited this time with only 3 early bird tickets available!

+

Come to our training… have a blast and get the best acceleration into Angular possible. You can find more information on our official event page, or grab your ticket right here!

+ +

Free Goodies

+

In addition to our three day course there’ll be a free community event in the evening that we’ll announce soon. We’ll also have special deals with hotels in Hillerød as well as Copenhagen.

+

See you in Denmark!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2017/05/30/angular-master-class-community-meetup.html b/announcements/2017/05/30/angular-master-class-community-meetup.html new file mode 100644 index 000000000..10125dea2 --- /dev/null +++ b/announcements/2017/05/30/angular-master-class-community-meetup.html @@ -0,0 +1,49 @@ +Join our free meetup in Denmark | Articles by thoughtram

Announcements

Join our free meetup in Denmark

Just a couple of weeks ago, we announced to bring our Angular Master Class to Denmark.

+

This will be a very special class for us as we are debuting our brand new courseware on Redux and ngrx for the first time in a public training.

+

If you haven’t had a chance to grab one of the early bird tickets, you can still get one of the regular tickets.

+

Join our free community event in the evening

+

As mentioned in the initial announcement, we’ll run a free event in the evening of June 21st at 5:30 PM.

+

The event takes place at the Auditorium at Foss. If you attend the training, you can simply follow us over to the Auditorium right after we finished the second day of the course.

+

For everyone joining the community event only, you’ll find the Auditorium at the following address.

+

Foss Allé 1 +
3400 Hillerød

+ +

If you are not attending the Angular Master Class training but plan to attend the meetup, please make sure to reserve a seat at our meetup page to ease coordination

+

What’s the theme?

+

The event will be focussed on short talks (5 - 20 min) with no strict guideline regarding topics. Anything from Angular, Progressive Web Apps to Blockchains and Machine Learning is appreciated.

+

Psst…first time speakers, this is your perfect chance

+

Announced Talks

+

As of today, we can plan for the following short talks.

+
    +
  • Shahrzad (Sherry) Azimi : A migration story (AngularJS -> Angular)
  • +
  • Shmuela Jacobs - Angular is on Fire(base)!
  • +
  • Anders Aastrup Søborg : Why we replace a feature rich PC application, with an angular application
  • +
  • Dominic Elm - Animations in Angular 4.2
  • +
  • Christoph Burgdorf - Machine Learning for everyone
  • +
+

Please ping us with your talk idea and we’ll add it to the list

+

See you in Denmark!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2017/06/23/announcing-angular-master-class-in-berlin.html b/announcements/2017/06/23/announcing-angular-master-class-in-berlin.html new file mode 100644 index 000000000..774c56e1a --- /dev/null +++ b/announcements/2017/06/23/announcing-angular-master-class-in-berlin.html @@ -0,0 +1,97 @@ +Announcing Angular Master Class in Berlin | Articles by thoughtram

Announcements

Announcing Angular Master Class in Berlin

We just returned from Denmark after running yet another Angular Master Class with a very diverse group of attendees from a wide range of different countries.

+

If you haven’t had the chance to join us in Denmark you really missed out on a fun event! However, here are good news: We are bringing our Angular Master Class to Berlin soon!

+

We are happy to team up with Westernacher Solutions AG to run the training in a really cool atmosphere with lots of room to teach, learn and have fun!

+

+ + + amc berlin main room + + + + + + amc berlin billard + +

+

The event takes place on the 18th to 20th September 2017 in one of the most vibrant parts of the city.

+

We will have three (3) days of fun and learning the ins and outs of Angular including things like

+
    +
  • Components and Modules
  • +
  • Fetching data using Http
  • +
  • Advanced Dependency Injection
  • +
  • Basic Forms
  • +
  • Observables deep-dive
  • +
  • Architectural patterns
  • +
  • ContentChildren and ViewChildren
  • +
  • Redux and ngrx
  • +
+

…and many more!

+

Tickets on sale now

+

The early bird round of tickets is on sale now. You can save up to 125,00 € so you’ll better be fast. Seats are very limited with only 5 early bird tickets available!

+

Come to our training… have a blast and get the best acceleration into Angular possible. You can find more information on our official event page, or grab your ticket right here!

+ +

See you in Berlin!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2017/06/23/announcing-angular-master-class-in-houston.html b/announcements/2017/06/23/announcing-angular-master-class-in-houston.html new file mode 100644 index 000000000..af1fd3f90 --- /dev/null +++ b/announcements/2017/06/23/announcing-angular-master-class-in-houston.html @@ -0,0 +1,120 @@ +Announcing Angular Master Class in Houston | Articles by thoughtram

Announcements

Announcing Angular Master Class in Houston

Angular Master-Class Training

+

It’s been almost 7 months since our last AMC training in USA: +Brooklyn, NY.

+

With help from Bonnie Brennan Angular Meetup Houston, we are very pleased to announce that our +Angular Master Class is finally coming to Houston, Texas on August 22 - 25, 2017!

+

And we found the perfect, artsy venue: Bisong Art Gallery, Houston Texas.

+

+ + + amc houston hall2 + +

+

After all, fun places are sure to get everyone into the right state of mind to learn the +nuances and power of Angular!

+

About the Training

+

You or your team should attend our 4-day Angular Master Class training.

+
+

Developers should note that this is the first time we have offered a public 4-day training! +The reason we added another day is because we want to teach our brand new course material focused on Redux and ngrx.

+
+

Training Schedule

+
    +
  • Day 1: Jump Start with full, high-level introduction to Angular
  • +
  • Day 2: Observables & Component Architecture
  • +
  • Day 3: Routing & Forms
  • +
  • Day 4: Redux + ngrx with immutable, 1-way dataflows and reactive programming +Sample: ngRx Lab Exercise
  • +
+

These are intense, full 8-hour days… but don’t worry, we make the classroom fun. thoughtram teaches the class with two (2) trainers. And almost every 30 minutes, students work with hands-on, coding lab exercises.

+
+

We have also updated our course and lab exercises to use Angular Material and Angular Flex-Layout. You can read about that here: Revamped Angular Master Class.

+
+

We also have social time after class on Day 2 and Day 4. Leverage the social times for networking, offline questions, and simply relaxing with your fellow Angular-philes. +So come to our training, have a blast, and get the best acceleration into Angular possible.

+

On-Premise, Private Training?

+

+ + + sample private training + +

+

If you prefer private, on-site training for your development teams [10 or more developers], +please contact us for rates, details, and scheduling: hello@thoughtram.io

+

Tickets on sale now

+

You can find more information on our official event page, or grab your public training ticket right here!

+ +

See you in Houston!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2018/02/07/announcing-angular-master-class-at-shopware.html b/announcements/2018/02/07/announcing-angular-master-class-at-shopware.html new file mode 100644 index 000000000..4d3e0b169 --- /dev/null +++ b/announcements/2018/02/07/announcing-angular-master-class-at-shopware.html @@ -0,0 +1,108 @@ +Announcing Angular Master Class at Shopware | Articles by thoughtram

Announcements

Announcing Angular Master Class at Shopware

A few months have passed since our last Angular Master Class in Berlin, but now it’s finally time again: In June 2018 we are organizing a new Angular Master Class in Germany.

+

We are thrilled to have found in Shopware the ideal partner and a first-class venue for an unforgettable event. Shopware is a leading e-commere company with a passionate, skilled and fun team to work with.

+

The facilities of Shopware in Schöppingen are modern, spacious and fancy and will be more than ideal for our Angular Master Class.

+

The event will take place from 20th to 22nd June 2018 at Shopware in Schöppingen.

+

+ + + shopware classroom + + +Picture by Rainer Lonsing

+

We will have three (3) days of fun and learning the ins and outs of Angular including things like

+
    +
  • Components and Modules
  • +
  • Fetching data using Http
  • +
  • Advanced Dependency Injection
  • +
  • Basic Forms
  • +
  • Observables deep-dive
  • +
  • Architectural patterns
  • +
  • Component communication
  • +
  • ContentChildren and ViewChildren
  • +
  • ngrx
  • +
+

…and much more!

+

During the day we will deepen our Angular knowledge together and at the end of the day we can have a nice time playing volleyball, table soccer and enjoy some delicious food.

+

This will be so much fun!

+

+ + + shopware volleyball + +

+

+

Left picture by Rainer Lonsing

+

Who wouldn’t want to join in? So better be quick and get your ticket soon.

+

Tickets on sale now

+ +

The early bird round of tickets is on sale now. You can save up to 125,00 € so you’ll better be fast. Seats are very limited with only 5 early bird tickets available!

+

You can find more information on our official event page, or grab your public training ticket right here!

+

See you in Berlin!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2018/04/12/rxjs-master-class-and-courseware-updates.html b/announcements/2018/04/12/rxjs-master-class-and-courseware-updates.html new file mode 100644 index 000000000..3b140a370 --- /dev/null +++ b/announcements/2018/04/12/rxjs-master-class-and-courseware-updates.html @@ -0,0 +1,94 @@ +RxJS Master Class and courseware updates | Articles by thoughtram

Announcements

RxJS Master Class and courseware updates

When we introduced our first workshop a few years back, we knew this was just the first step to creating a sophisticated portfolio of courseware material, that enables us to provide high-quality software training.

+

Ever since, we’ve added the Angular Master Class to our products and we kept improving, fine-tuning and battle-testing it over the years. We’ve extended the courseware to cover topics like state management with Redux and ngrx as well, and obviously, there’s still so much more to come.

+

Progressive Web Apps and Server-side Rendering

+

In fact, today we’re happy to let you know that our Angular Master Class now officially covers Progressive Web Apps with Angular and Server-side rendering with Angular Universal as well!

+

+ + + amc pwa universal + +

+

These are the missing pieces to take your Angular apps to the next level and provide a richer user experience, by making them blazingly fast. Those topics might become part of our upcoming Angular Master Class in Germany and we have a couple of seats left!

+

Introducing RxJS Master Class

+

By the title of this post, you might have guessed that this is not everything we have to share today. Many of you might have noticed that Angular embraces functional reactive programming paradigms using RxJS. We see many people struggling with this topic, which is why we decided to create a dedicated class just about that!

+

+ + + rxjs master class + +

+

The fun thing about this class is that you’ll build the classic video game “Snake” from scratch, using Observables and powerful operators purely with RxJS. This also means that this class is a standalone one and doesn’t go necessarily hand-in-hand with our Angular Master Class.

+

Also, we might plan to extend our RxJS Master Class with more features for the game so we can explore even more sophisticated scenarios with RxJS! Head over to our official website for more information.

+

That’s it so far from our side, if you’re interested in public or corporate in-house class, as always, feel free to reach out to us!

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/2019/07/28/angular-master-class-coming-to-malaga.html b/announcements/2019/07/28/angular-master-class-coming-to-malaga.html new file mode 100644 index 000000000..c8078efef --- /dev/null +++ b/announcements/2019/07/28/angular-master-class-coming-to-malaga.html @@ -0,0 +1,86 @@ +Angular Master Class coming to Málaga | Articles by thoughtram

Announcements

Angular Master Class coming to Málaga

Angular Master-Class Training

+

You may have noticed that we recently asked the Angular community where to host our next public Angular Master Class.

+

Today we are happy to announce that the community has decided: We will come to MÁLAGA!

+

It’s been a while but now we are excited to finally hold another public Angular Master Class and bring it to the beautiful city of Málaga in Spain this October! We can’t wait for it!

+

We’ve also found a really great venue in Málaga that will set the right balance between work and fun: The Innovation Coworking Campus

+

+AMC Malaga Event Location +

+

We are sure this place will get everyone into the right state of mind to learn the nuances and power of Angular!

+

About the Training

+

The event takes place on the 28th to 30th October 2019 in the heart of the vibrant city Málaga.

+

We will have three (3) days of fun and learning the ins and outs of Angular including things like:

+
    +
  • Components and Modules
  • +
  • Fetching data using Http
  • +
  • Advanced Dependency Injection
  • +
  • Advanced Forms
  • +
  • Observables deep-dive
  • +
  • Architectural patterns
  • +
  • ContentChildren and ViewChildren
  • +
  • Statemanagement with ngrx
  • +
  • …and many more!
  • +
+

Tickets on sale now

+

You can find more information on our official event page, or grab your public training ticket right here!

+ +

See you in Málaga!

+

On-Premise, Private Training?

+

+ + + sample private training + +

+

If you prefer private, on-site training for your development teams, please contact us for rates, details, and scheduling: hello@thoughtram.io

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2019/09/02/more-GDE-power-at-thoughtram.html b/announcements/2019/09/02/more-GDE-power-at-thoughtram.html new file mode 100644 index 000000000..8c0f34623 --- /dev/null +++ b/announcements/2019/09/02/more-GDE-power-at-thoughtram.html @@ -0,0 +1,75 @@ +More GDE power at thoughtram | Articles by thoughtram

Announcements

More GDE power at thoughtram

More GDE power at thoughtram

+

Today we want to share some great news about two of our trainers.

+

We can see them in our mind’s eye as they teach a group of aspiring developers how to write great applications with the Angular framework. There’s a great vibe and energy in the room, people are laughing and having fun. Because with joy and fun in something, learning - as we all know - is much easier!

+

Who are we talking about you may ask? That’s Dominic Elm and Kwinten Pisman.

+

Our beautiful dream team of trainers, both of whom recently became Google Developer Experts (GDE’s) for Web Technologies, specializing in Angular.

+

+ + + kwinten brecht + +

+

Congrats to both of you, that is very well deserved! 🙏

+

What does it mean to be a GDE?

+

Being appointed as a GDE means that you’re an expert in a Google technology. For Kwinten and Dominic, that’s Angular. It is a recognition for all those who demonstrate their energy and attention again and again by pushing the technology, blogging about it, speaking at conferences as well as being helpful to the community.

+

However, one does not just become a GDE by applying for it, one has to get nominated by an existing GDE and go through an interview and approval process. Feel free to learn more about the GDE program through the official website.

+

Both have demonstrated outstanding work!

+

We’d like to highlight some of the great things that both of them have done for the Angular community.

+

Dominic recently came up with a great library ngx-template-streams that lets us supercharge our Angular templates to embrace reactivity through Observables. We highly recommend to check it out!

+

He has also authored ngx-drag-to-select, a lightweight component for fast drag to select. Check out the demo, it’s rad!

+

Kwinten has also proved his exceptional Angular skills time and time again! He’ll soon release an Angular Change Detection Profiler as a browser extension. It will supercharge everybody’s debugging skills for sure!

+

Kwinten and Dominic will also be part of the revo.js conference this year where they’ll give a talk together.

+

Come to Málaga to improve your Angular skills at our next public class

+

Both have absolutely earned their GDE status and we couldn’t be happier to have them among our thoughtram trainers!

+

If you like to upgrade your Angular skills to the next level, come to our upcoming public Angular class in Malaga, you’ll have a blast for sure!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/2024/02/13/farewell.html b/announcements/2024/02/13/farewell.html new file mode 100644 index 000000000..bb874dcf6 --- /dev/null +++ b/announcements/2024/02/13/farewell.html @@ -0,0 +1,34 @@ +Farewell | Articles by thoughtram

Announcements

Farewell

To our friends, clients and everyone else we’ve had the chance to collaborate with.

+

If you’ve been following thoughtram closely over the years, you might have noticed that we’ve become very quiet. +So it shouldn’t be a surprise to you that today, we’re announcing that thoughtram is shutting down and closing its doors.

+

Almost ten years ago we’ve introduced ourselves to the world and our mission to travel across the globe and help people in levelling up their engineering skills with various technologies. Starting with our very first Git Master Class, followed by organizing local developer meetups and later countless training sessions to master Angular, which were then extended with more advanced classes.

+

We are extremely grateful that we’ve had the opportunity to see the world, learn about different cultures, meet many amazing people of which a lot became our friends, and ultimately experience a journey that has shaped us as humans, for the better. And all of this, thanks to you. Thanks to everyone who helped making our events happen, whether you’ve helped organizing one or travelled the long way to be part of the experience. Thanks to everyone who’s cheered us on. Thanks to everyone that we had the pleasure to learn from. Thanks to everyone who joined the squad to help others levelling up.

+

You know who you are.

+

We proudly look back at many years of fun and challenges, and we take these memories along with us as we’re continuing to explore new paths in our lifes and hopefully grow further.

+

It’s been an amazing ride, but now it’s time to say so long, and thanks for all the fish.

Written by  Author

Pascal Precht

\ No newline at end of file diff --git a/announcements/machine-learning/2017/12/20/machine-learning-jump-start-online-course.html b/announcements/machine-learning/2017/12/20/machine-learning-jump-start-online-course.html new file mode 100644 index 000000000..651dd0dce --- /dev/null +++ b/announcements/machine-learning/2017/12/20/machine-learning-jump-start-online-course.html @@ -0,0 +1,58 @@ +Machine Learning Jump Start - Online Course | Articles by thoughtram

Announcements

Machine Learning Jump Start - Online Course

About one and a half years ago we first started dipping our toes into the brave new world of Machine Learning. The concept of a machine figuring out how to solve problems on its own is fascinating and sticked with us. If you’ve been following along, you’ve probably noticed that, next to thoughtram, we started our second mission to help innovate the Machine Learning space and founded a new company MachineLabs, Inc.

+

Our ultimate goal is it to get more people into Machine Learning. Part of this mission is to provide the community with rich tools for the job. That’s what we’ve been tirelessly working on for the past couple of months. Another part is to teach people how to get started with Machine Learning at all. Especially the latter makes a lot of sense as teaching is what we’re pretty good at.

+

Today we’re super excited to announce our cross-company collaboration, in which we’ll create the Machine Learning Jump Start Online Course!

+

Machine Learning Jump Start - Online Course

+

Machine Learning is already at the heart of many services of our daily use. Yet, the general perception of many developers seems to be that Machine Learning is just not for them. It’s for Phds only. It’s for Math cracks. Existing Machine Learning courses seem to feed this narrative as they mostly start with…yeah Math.

+

We aren’t here to bad-mouth this approach. We are here to embrace diversity and throw in a different attempt to teach Machine Learning. We want to create a new Machine Learning training experience with the following properties:

+
    +
  • Beginner friendly
  • +
  • Driven by practical examples
  • +
  • Built on the MachineLabs platform (zero setup yet powerful!)
  • +
  • Knowledge builds up from higher to lower level (forget Math for a moment!)
  • +
+

To give you an idea, here’s the project’s teaser video!

+ +

What’s inside?

+

This course will take you from zero to hero by making you solve your first Machine Learning task in just 20 lines of pure Python code. After that the course dives into various topics such as:

+
    +
  • Machine Learning Basics
  • +
  • Activation functions, Max Pooling and Dropouts
  • +
  • Common Keras APIs to solve your ML tasks
  • +
  • Convolutional Networks
  • +
  • Recurrent Networks
  • +
  • Reinforcement learning
  • +
+

… and much more!

+

Preorder now!

+

While the course isn’t yet 100 % finished, you can preorder it today and receive some exclusive benefits for early birds!

+
    +
  • 70% discount compared to the regular price
  • +
  • exclusive access to the Early Access Programm starting in April 2018
  • +
  • 20 free monthly GPU hours for an entire year
  • +
+

Notice that we won’t charge you before the Early Access Preview begins!

+

The course is on sale now with a huge preorder discount! Head over to the course website and save 70% on your course!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/meetups/2014/06/24/organizing-hannovers-first-rust-meetup.html b/announcements/meetups/2014/06/24/organizing-hannovers-first-rust-meetup.html new file mode 100644 index 000000000..f3bc2f51c --- /dev/null +++ b/announcements/meetups/2014/06/24/organizing-hannovers-first-rust-meetup.html @@ -0,0 +1,36 @@ +Organizing Hanovers first Rust meetup | Articles by thoughtram

Announcements

Organizing Hanovers first Rust meetup

As thoughtram we are dedicated to invest into interesting new technologies that we feel are exciting and have the potential to become popular. Not for money, but for fun!

+

One of those new exciting technologies is a language called Rust developed entirely in the open by the popular browser company Mozilla.

+

From the Rust website:

+
+

Rust is a systems programming language that runs blazingly fast, prevents almost all crashes*, and eliminates data races

+
+

What makes this language different from any other language is that it aims to provide memory safety without sacrificing performance. It provides modern features like pattern matching, generics, traits etc. while at the same time it’s aimed to eventually replace C/C++.

+

Imagine a world where system programming becomes approachable for the rest of us!

+

Today we are happy to announce the very first Rust meetup in Hanover.

+

Here are the facts:

+

When: July 10th 2014, 18:30
Where: CouchCommerce GmbH, Bödekerstraße 56, 30161 Hannover

+ +

Schedule:

+
    +
  • Christoph will give a lightning talk about Rust introducing it’s core concepts
  • +
  • We will look at the web framework floor that Christoph started implementing in Rust
  • +
  • The plan is to turn every participant into a floor contributor by the end of the session :)
  • +
+

Don’t worry if you don’t have any prior knowledge in system programming languages like C or C++. We are all beginners. That said, if you do have knowledge in system programming languages, we definitely want you to attend, too! Don’t be late, we might have Rust stickers for you :)

+

We like to take the chance to thank CouchCommerce for sponsoring the venue and drinks!

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/announcements/tools/2014/09/18/announcing-clog-a-conventional-changelog-generator-for-the-rest-of-us.html b/announcements/tools/2014/09/18/announcing-clog-a-conventional-changelog-generator-for-the-rest-of-us.html new file mode 100644 index 000000000..1b57f7dec --- /dev/null +++ b/announcements/tools/2014/09/18/announcing-clog-a-conventional-changelog-generator-for-the-rest-of-us.html @@ -0,0 +1,59 @@ +clog - A conventional changelog generator for the rest of us | Articles by thoughtram

Announcements

clog - A conventional changelog generator for the rest of us

At thoughtram we teach people how to master Git. We also teach them how to maintain a semantic history. That’s a history where each commit groups a logical code change. Like a feature or a bugfix. You can go even further and follow a commit message convention to wrap up valuable meta data in the commit message. Once you follow a commit message convention, you can easily generate a nice changelog without any manual work.

+

Let’s take a look at such a commit message.

+
feat(ngInclude): add template url parameter to events
+
+The `src` (i.e. the url of the template to load) is now provided to the
+`$includeContentRequested`, `$includeContentLoaded` and `$includeContentError`
+events.
+
+Closes #8453
+Closes #8454
+

If you look closely, you might notice a pattern behind this commit message. Let’s annotate it to make things more clear.

+
                      component        commit title
+        commit type       /                /      
+                \        |                |
+                 feat(ngInclude): add template url parameter to events
+            
+        body ->  The 'src` (i.e. the url of the template to load) is now provided to the
+                 `$includeContentRequested`, `$includeContentLoaded` and `$includeContentError`
+                 events.
+
+ referenced  ->  Closes #8453
+ issues          Closes #8454
+

Notice how this commit message preserves valuable meta data among the plain message. Namely the commit type that can either be feat, fix, docs, style, refactor, test or chore to indicate the type of the change. What follows is the name of the component that was changed, wrapped in parenthesis. It also contains a short title and an optional body that must have a preceding blank line.

+

References to related issues may follow after another blank line.

+

That’s probably not the one and only commit message convention but it’s one that is battle tested in many high profile projects. It was invented by the smart folks at Google to be used for their AngularJS project. We recommend to check out this guideline to get the full picture of the convention. Also if you like to see how the generated changelog actually looks like, take a look here.

+

Until know there’s only been a Node.js based implementation for the generator. In addition there is a Grunt task to easily integrate it with the popular Grunt task runner.

+

That’s all nice and simple but there’s a problem: We are leaving out a big opportunity to make more developer follow this convention. What’s about all the Java, C#, C++, Haskell, Rust or Go developers out there? They certainly won’t install Node.js or even Grunt to generate their changelog. Having Node.js as a dependency is quite a big technical debt for something as simple as changelog generation.

+

Hello clog!

+

We want changelog generation to be usable for everyone with the most minimal footprint possible. We wanted something that is aligned with the UNIX philosophy of having a small command line tool just like cp or ls. So what should we do? Write a command line tool in C? Well, almost! We’ve written a command line tool called clog in Rust. Rust is a new language by Mozilla that enables you to write low level code in a high level language. Clog is more or less a straight port of the Node.js based generator by Andy Joslin.

+

clogs usage is quite simple. It follows the POSIX standard. Just invoke it with clog --help and you’ll get this output.

+
Usage:
+  clog [--repository=<link> --setversion=<version> --subtitle=<subtitle> 
+        --from=<from> --to=<to> --from-latest-tag]
+

You can invoke clog without any parameter to generate a nice changelog for the entire history of your project. Provide the --repository parameter to set the URL of the github repository to make the changelog include links to the commits on github.

+

Usually you don’t want to regenerate the entire changelog but instead prepend only the changelog for every commit that happend between now and the previous version. In order to do that you can just run clog with the --from-latest-tag parameter. If you know that you want to generate the changelog for a specific range of commits you can just provide the --from and --to parameters (e.g. --from=c667e1e --to=c7a1f1c).

+

In order to also include a nice header you can provide a version and a subtitle as well (e.g. --setversion=0.11.0 --subtitle=lame-duck).

+

Putting it all together, here is how clog generated clogs latest changelog, it’s clogception!

+

clog --repository=https://github.com/thoughtram/clog --from-latest-tag --setversion=0.2.0

+

clog is a work in progress and there are some things missing (like exposing a C interface). That said, it’s ready to be used if you don’t mind the missing features.

+

How to get clog?

+

If you happen to use Rust for your project you can simply get clog via Rust’s package manager Cargo. Otherwise you can also just grab the binary and put it somewhere on your machine. Given the small file size you may also directly put clog into your project folder so that everyone on the team has it and changelog generation can be made part of the build process.

+

clog for Mac (binary) (source)

+

Binaries for Windows will follow shortly. Since none of us uses Windows, we first need figure out how to properly set up the build chain there.

Written by  Author

Christoph Burgdorf

\ No newline at end of file diff --git a/app-db11fdec89ef44788bcc.js b/app-db11fdec89ef44788bcc.js new file mode 100644 index 000000000..192e16889 --- /dev/null +++ b/app-db11fdec89ef44788bcc.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[,function(t,e,n){var r=n(4),o=n(28),i=n(24),a=n(12),c=n(26),u=function(t,e,n){var s,l,f,p,d=t&u.F,h=t&u.G,v=t&u.S,g=t&u.P,m=t&u.B,y=h?r:v?r[e]||(r[e]={}):(r[e]||{}).prototype,w=h?o:o[e]||(o[e]={}),_=w.prototype||(w.prototype={});for(s in h&&(n=e),n)f=((l=!d&&y&&void 0!==y[s])?y:n)[s],p=m&&l?c(f,r):g&&"function"==typeof f?c(Function.call,f):f,y&&a(y,s,f,t&u.U),w[s]!=f&&i(w,s,p),g&&_[s]!=f&&(_[s]=f)};r.core=o,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e,n){n(50),n(9);var r=n(153),o=n(7).publicLoader,i=o.getResourcesForPathname,a=o.getResourcesForPathnameSync,c=o.getResourceURLsForPathname,u=o.loadPage,s=o.loadPageSync;e.apiRunner=function(t,e,n,o){void 0===e&&(e={});var l=r.map((function(n){if(n.plugin[t]){e.getResourcesForPathnameSync=a,e.getResourcesForPathname=i,e.getResourceURLsForPathname=c,e.loadPage=u,e.loadPageSync=s;var r=n.plugin[t](e,n.options);return r&&o&&(e=o({args:e,result:r,plugin:n})),r}}));return(l=l.filter((function(t){return void 0!==t}))).length>0?l:n?[n]:[]},e.apiRunnerAsync=function(t,e,n){return r.reduce((function(n,r){return r.plugin[t]?n.then((function(){return r.plugin[t](e,r.options)})):n}),Promise.resolve())}},function(t,e,n){var r=n(58)("wks"),o=n(48),i=n(4).Symbol,a="function"==typeof i;(t.exports=function(t){return r[t]||(r[t]=a&&i[t]||(a?i:o)("Symbol."+t))}).store=r},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(6);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){"use strict";n.r(e);n(158),n(67),n(53),n(42),n(119),n(13),n(14),n(91),n(124),n(166),n(167),n(17),n(50),n(9),n(168);var r=function(t){if("undefined"==typeof document)return!1;var e=document.createElement("link");try{if(e.relList&&"function"==typeof e.relList.supports)return e.relList.supports(t)}catch(n){return!1}return!1}("prefetch")?function(t){return new Promise((function(e,n){if("undefined"!=typeof document){var r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",t),r.onload=e,r.onerror=n,(document.getElementsByTagName("head")[0]||document.getElementsByName("script")[0].parentNode).appendChild(r)}else n()}))}:function(t){return new Promise((function(e,n){var r=new XMLHttpRequest;r.open("GET",t,!0),r.onload=function(){200===r.status?e():n()},r.send(null)}))},o={},i=function(t){return new Promise((function(e){o[t]?e():r(t).then((function(){e(),o[t]=!0})).catch((function(){}))}))},a=n(45),c=(n(43),n(16)),u=n(55),s=function(t){return void 0===t?t:"/"===t?"/":"/"===t.charAt(t.length-1)?t.slice(0,-1):t},l=[],f=function(t){l=t},p=function(t){var e=d(t),n=l,r=Array.isArray(n),o=0;for(n=r?n:n[Symbol.iterator]();;){var i;if(r){if(o>=n.length)break;i=n[o++]}else{if((o=n.next()).done)break;i=o.value}var a=i,u=a.matchPath,f=a.path;if(Object(c.b)(u,e))return s(f)}return null},d=function(t){var e=function(t){var e=decodeURIComponent(t);return Object(u.a)(e,"").split("#")[0].split("?")[0]}(t);return"/index.html"===e&&(e="/"),e=s(e)};function h(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e=t.length?(this._t=void 0,o(1)):o(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(t,e,n){"use strict";n(72),n(71),n(34),n(18);var r=n(19);e.__esModule=!0,e.withPrefix=d,e.withAssetPrefix=function(t){return[""].concat([t.replace(/^\//,"")]).join("/")},e.navigateTo=e.replace=e.push=e.navigate=e.default=void 0;var o=r(n(132)),i=r(n(133)),a=r(n(75)),c=r(n(76)),u=r(n(96)),s=r(n(38)),l=r(n(0)),f=n(27),p=n(195);function d(t){return function(t){return t.replace(/\/+/g,"/")}(["",t].join("/"))}e.parsePath=p.parsePath;var h={activeClassName:s.default.string,activeStyle:s.default.object,partiallyActive:s.default.bool},v=function(t){function e(e){var n;n=t.call(this,e)||this,(0,u.default)((0,a.default)(n),"defaultGetProps",(function(t){var e=t.isPartiallyCurrent,r=t.isCurrent;return(n.props.partiallyActive?e:r)?{className:[n.props.className,n.props.activeClassName].filter(Boolean).join(" "),style:(0,i.default)({},n.props.style,{},n.props.activeStyle)}:null}));var r=!1;return"undefined"!=typeof window&&window.IntersectionObserver&&(r=!0),n.state={IOSupported:r},n.handleRef=n.handleRef.bind((0,a.default)(n)),n}(0,c.default)(e,t);var n=e.prototype;return n.componentDidUpdate=function(t,e){this.props.to===t.to||this.state.IOSupported||___loader.enqueue((0,p.parsePath)(this.props.to).pathname)},n.componentDidMount=function(){this.state.IOSupported||___loader.enqueue((0,p.parsePath)(this.props.to).pathname)},n.componentWillUnmount=function(){if(this.io){var t=this.io,e=t.instance,n=t.el;e.unobserve(n),e.disconnect()}},n.handleRef=function(t){var e,n,r,o=this;this.props.innerRef&&this.props.innerRef.hasOwnProperty("current")?this.props.innerRef.current=t:this.props.innerRef&&this.props.innerRef(t),this.state.IOSupported&&t&&(this.io=(e=t,n=function(){___loader.enqueue((0,p.parsePath)(o.props.to).pathname)},(r=new window.IntersectionObserver((function(t){t.forEach((function(t){e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(r.unobserve(e),r.disconnect(),n())}))}))).observe(e),{instance:r,el:e}))},n.render=function(){var t=this,e=this.props,n=e.to,r=e.getProps,a=void 0===r?this.defaultGetProps:r,c=e.onClick,u=e.onMouseEnter,s=(e.activeClassName,e.activeStyle,e.innerRef,e.partiallyActive,e.state),h=e.replace,v=(0,o.default)(e,["to","getProps","onClick","onMouseEnter","activeClassName","activeStyle","innerRef","partiallyActive","state","replace"]);var g=d(n);return l.default.createElement(f.Link,(0,i.default)({to:g,state:s,getProps:a,innerRef:this.handleRef,onMouseEnter:function(t){u&&u(t),___loader.hovering((0,p.parsePath)(n).pathname)},onClick:function(e){return c&&c(e),0!==e.button||t.props.target||e.defaultPrevented||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),y(n,{state:s,replace:h})),!0}},v))},e}(l.default.Component);v.propTypes=(0,i.default)({},h,{onClick:s.default.func,to:s.default.string.isRequired,replace:s.default.bool});var g=function(t,e,n){return console.warn('The "'+t+'" method is now deprecated and will be removed in Gatsby v'+n+'. Please use "'+e+'" instead.')},m=l.default.forwardRef((function(t,e){return l.default.createElement(v,(0,i.default)({innerRef:e},t))}));e.default=m;var y=function(t,e){window.___navigate(d(t),e)};e.navigate=y;var w=function(t){g("push","navigate",3),window.___push(d(t))};e.push=w;e.replace=function(t){g("replace","navigate",3),window.___replace(d(t))};e.navigateTo=function(t){return g("navigateTo","navigate",3),w(t)}},function(t,e,n){"use strict";n.d(e,"e",(function(){return i})),n.d(e,"c",(function(){return a})),n.d(e,"b",(function(){return c})),n.d(e,"d",(function(){return u})),n.d(e,"a",(function(){return s})),n.d(e,"f",(function(){return l}));n(18),n(93),n(71),n(172),n(20),n(32),n(43);var r=n(36),o=n.n(r),i=function(t,e){return t.substr(0,e.length)===e},a=function(t,e){for(var n=void 0,r=void 0,i=e.split("?")[0],a=v(i),c=""===a[0],u=h(t),s=0,l=u.length;se.score?-1:t.index-e.index}))},v=function(t){return t.replace(/(^\/+|\/+$)/g,"").split("/")},g=function(t,e){return t+(e?"?"+e:"")},m=["uri","path"]},function(t,e,n){var r=n(1);r(r.S+r.F,"Object",{assign:n(106)})},function(t,e,n){"use strict";var r=n(5),o=n(22),i=n(21),a=n(61),c=n(81),u=n(56),s=Math.max,l=Math.min,f=Math.floor,p=/\$([$&`']|\d\d?|<[^>]*>)/g,d=/\$([$&`']|\d\d?)/g;n(59)("replace",2,(function(t,e,n,h){return[function(r,o){var i=t(this),a=null==r?void 0:r[e];return void 0!==a?a.call(r,i,o):n.call(String(i),r,o)},function(t,e){var o=h(n,t,this,e);if(o.done)return o.value;var f=r(t),p=String(this),d="function"==typeof e;d||(e=String(e));var g=f.global;if(g){var m=f.unicode;f.lastIndex=0}for(var y=[];;){var w=u(f,p);if(null===w)break;if(y.push(w),!g)break;""===String(w[0])&&(f.lastIndex=c(p,i(f.lastIndex),m))}for(var _,b="",S=0,x=0;x=S&&(b+=p.slice(S,O)+k,S=O+P.length)}return b+p.slice(S)}];function v(t,e,r,i,a,c){var u=r+t.length,s=i.length,l=d;return void 0!==a&&(a=o(a),l=p),n.call(c,l,(function(n,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,r);case"'":return e.slice(u);case"<":c=a[o.slice(1,-1)];break;default:var l=+o;if(0===l)return n;if(l>s){var p=f(l/10);return 0===p?n:p<=s?void 0===i[p-1]?o.charAt(1):i[p-1]+o.charAt(1):n}c=i[l-1]}return void 0===c?"":c}))}}))},function(t,e){t.exports=function(t){return t&&t.__esModule?t:{default:t}}},function(t,e,n){"use strict";var r=n(1),o=n(82)(!1),i=[].indexOf,a=!!i&&1/[1].indexOf(1,-0)<0;r(r.P+r.F*(a||!n(33)(i)),"Array",{indexOf:function(t){return a?i.apply(this,arguments)||0:o(this,t,arguments[1])}})},function(t,e,n){var r=n(61),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e,n){var r=n(30);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(22),o=n(40);n(175)("keys",(function(){return function(t){return o(r(t))}}))},function(t,e,n){var r=n(11),o=n(49);t.exports=n(10)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(29);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){"use strict";n.r(e);n(18),n(32),n(50),n(9),n(129),n(70),n(20),n(17);var r=n(0),o=n.n(r),i=(n(130),n(38),n(36)),a=n.n(i),c=o.a.createContext,u=n(137),s=n(16),l=n(35);n.d(e,"Link",(function(){return C})),n.d(e,"Location",(function(){return y})),n.d(e,"LocationProvider",(function(){return w})),n.d(e,"Match",(function(){return D})),n.d(e,"Redirect",(function(){return W})),n.d(e,"Router",(function(){return S})),n.d(e,"ServerLocation",(function(){return _})),n.d(e,"isRedirect",(function(){return A})),n.d(e,"redirectTo",(function(){return F})),n.d(e,"BaseContext",(function(){return b})),n.d(e,"createHistory",(function(){return l.createHistory})),n.d(e,"createMemorySource",(function(){return l.createMemorySource})),n.d(e,"navigate",(function(){return l.navigate})),n.d(e,"globalHistory",(function(){return l.globalHistory}));var f=Object.assign||function(t){for(var e=1;e=0||Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n}function d(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function h(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function v(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var g=function(t,e){var n=c(e);return n.Consumer.displayName=t+".Consumer",n.Provider.displayName=t+".Provider",n},m=g("Location"),y=function(t){var e=t.children;return o.a.createElement(m.Consumer,null,(function(t){return t?e(t):o.a.createElement(w,null,e)}))},w=function(t){function e(){var n,r;d(this,e);for(var o=arguments.length,i=Array(o),a=0;a1&&void 0!==arguments[1]?arguments[1]:{},s=u.state,l=u.replace,f=void 0!==l&&l;s=r({},s,{key:Date.now()+""});try{a||f?t.history.replaceState(s,null,e):t.history.pushState(s,null,e)}catch(d){t.location[f?"replace":"assign"](e)}i=o(t),a=!0;var p=new Promise((function(t){return c=t}));return n.forEach((function(t){return t({location:i,action:"PUSH"})})),p}}},a=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"/",e=0,n=[{pathname:t,search:""}],r=[];return{get location(){return n[e]},addEventListener:function(t,e){},removeEventListener:function(t,e){},history:{get entries(){return n},get index(){return e},get state(){return r[e]},pushState:function(t,o,i){var a=i.split("?"),c=a[0],u=a[1],s=void 0===u?"":u;e++,n.push({pathname:c,search:s}),r.push(t)},replaceState:function(t,o,i){var a=i.split("?"),c=a[0],u=a[1],s=void 0===u?"":u;n[e]={pathname:c,search:s},r[e]=t}}}},c=!("undefined"==typeof window||!window.document||!window.document.createElement),u=i(c?window:a()),s=u.navigate},function(t,e,n){"use strict";n(46),n(18);t.exports=function(t,e,n,r,o,i,a,c){if(!t){var u;if(void 0===e)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[n,r,o,i,a,c],l=0;(u=new Error(e.replace(/%s/g,(function(){return s[l++]})))).name="Invariant Violation"}throw u.framesToPop=1,u}}},function(t,e,n){var r=n(26),o=n(62),i=n(22),a=n(21),c=n(169);t.exports=function(t,e){var n=1==t,u=2==t,s=3==t,l=4==t,f=6==t,p=5==t||f,d=e||c;return function(e,c,h){for(var v,g,m=i(e),y=o(m),w=r(c,h,3),_=a(y.length),b=0,S=n?d(e,_):u?d(e,0):void 0;_>b;b++)if((p||b in y)&&(g=w(v=y[b],b,m),t))if(n)S[b]=g;else if(g)switch(t){case 3:return!0;case 5:return v;case 6:return b;case 2:S.push(v)}else if(l)return!1;return f?-1:s||l?l:S}}},function(t,e,n){t.exports=n(183)()},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(107),o=n(84);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e,n){var r=n(62),o=n(30);t.exports=function(t){return r(o(t))}},function(t,e,n){"use strict";var r=n(4),o=n(25),i=n(10),a=n(1),c=n(12),u=n(54).KEY,s=n(8),l=n(58),f=n(52),p=n(48),d=n(3),h=n(117),v=n(116),g=n(161),m=n(88),y=n(5),w=n(6),_=n(22),b=n(41),S=n(80),x=n(49),P=n(68),O=n(163),E=n(118),R=n(85),j=n(11),T=n(40),k=E.f,C=j.f,L=O.f,A=r.Symbol,F=r.JSON,M=F&&F.stringify,W=d("_hidden"),D=d("toPrimitive"),U={}.propertyIsEnumerable,I=l("symbol-registry"),N=l("symbols"),H=l("op-symbols"),B=Object.prototype,q="function"==typeof A&&!!R.f,G=r.QObject,K=!G||!G.prototype||!G.prototype.findChild,J=i&&s((function(){return 7!=P(C({},"a",{get:function(){return C(this,"a",{value:7}).a}})).a}))?function(t,e,n){var r=k(B,e);r&&delete B[e],C(t,e,n),r&&t!==B&&C(B,e,r)}:C,Y=function(t){var e=N[t]=P(A.prototype);return e._k=t,e},$=q&&"symbol"==typeof A.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof A},V=function(t,e,n){return t===B&&V(H,e,n),y(t),e=S(e,!0),y(n),o(N,e)?(n.enumerable?(o(t,W)&&t[W][e]&&(t[W][e]=!1),n=P(n,{enumerable:x(0,!1)})):(o(t,W)||C(t,W,x(1,{})),t[W][e]=!0),J(t,e,n)):C(t,e,n)},z=function(t,e){y(t);for(var n,r=g(e=b(e)),o=0,i=r.length;i>o;)V(t,n=r[o++],e[n]);return t},X=function(t){var e=U.call(this,t=S(t,!0));return!(this===B&&o(N,t)&&!o(H,t))&&(!(e||!o(this,t)||!o(N,t)||o(this,W)&&this[W][t])||e)},Q=function(t,e){if(t=b(t),e=S(e,!0),t!==B||!o(N,e)||o(H,e)){var n=k(t,e);return!n||!o(N,e)||o(t,W)&&t[W][e]||(n.enumerable=!0),n}},Z=function(t){for(var e,n=L(b(t)),r=[],i=0;n.length>i;)o(N,e=n[i++])||e==W||e==u||r.push(e);return r},tt=function(t){for(var e,n=t===B,r=L(n?H:b(t)),i=[],a=0;r.length>a;)!o(N,e=r[a++])||n&&!o(B,e)||i.push(N[e]);return i};q||(c((A=function(){if(this instanceof A)throw TypeError("Symbol is not a constructor!");var t=p(arguments.length>0?arguments[0]:void 0),e=function(n){this===B&&e.call(H,n),o(this,W)&&o(this[W],t)&&(this[W][t]=!1),J(this,t,x(1,n))};return i&&K&&J(B,t,{configurable:!0,set:e}),Y(t)}).prototype,"toString",(function(){return this._k})),E.f=Q,j.f=V,n(89).f=O.f=Z,n(63).f=X,R.f=tt,i&&!n(47)&&c(B,"propertyIsEnumerable",X,!0),h.f=function(t){return Y(d(t))}),a(a.G+a.W+a.F*!q,{Symbol:A});for(var et="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),nt=0;et.length>nt;)d(et[nt++]);for(var rt=T(d.store),ot=0;rt.length>ot;)v(rt[ot++]);a(a.S+a.F*!q,"Symbol",{for:function(t){return o(I,t+="")?I[t]:I[t]=A(t)},keyFor:function(t){if(!$(t))throw TypeError(t+" is not a symbol!");for(var e in I)if(I[e]===t)return e},useSetter:function(){K=!0},useSimple:function(){K=!1}}),a(a.S+a.F*!q,"Object",{create:function(t,e){return void 0===e?P(t):z(P(t),e)},defineProperty:V,defineProperties:z,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt});var it=s((function(){R.f(1)}));a(a.S+a.F*it,"Object",{getOwnPropertySymbols:function(t){return R.f(_(t))}}),F&&a(a.S+a.F*(!q||s((function(){var t=A();return"[null]"!=M([t])||"{}"!=M({a:t})||"{}"!=M(Object(t))}))),"JSON",{stringify:function(t){for(var e,n,r=[t],o=1;arguments.length>o;)r.push(arguments[o++]);if(n=e=r[1],(w(e)||void 0!==t)&&!$(t))return m(e)||(e=function(t,e){if("function"==typeof n&&(e=n.call(this,t,e)),!$(e))return e}),r[1]=e,M.apply(F,r)}}),A.prototype[D]||n(24)(A.prototype,D,A.prototype.valueOf),f(A,"Symbol"),f(Math,"Math",!0),f(r.JSON,"JSON",!0)},function(t,e,n){"use strict";var r=n(92),o=n(5),i=n(111),a=n(81),c=n(21),u=n(56),s=n(78),l=n(8),f=Math.min,p=[].push,d=!l((function(){RegExp(4294967295,"y")}));n(59)("split",2,(function(t,e,n,l){var h;return h="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,e){var o=String(this);if(void 0===t&&0===e)return[];if(!r(t))return n.call(o,t,e);for(var i,a,c,u=[],l=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),f=0,d=void 0===e?4294967295:e>>>0,h=new RegExp(t.source,l+"g");(i=s.call(h,o))&&!((a=h.lastIndex)>f&&(u.push(o.slice(f,i.index)),i.length>1&&i.index=d));)h.lastIndex===i.index&&h.lastIndex++;return f===o.length?!c&&h.test("")||u.push(""):u.push(o.slice(f)),u.length>d?u.slice(0,d):u}:"0".split(void 0,0).length?function(t,e){return void 0===t&&0===e?[]:n.call(this,t,e)}:n,[function(n,r){var o=t(this),i=null==n?void 0:n[e];return void 0!==i?i.call(n,o,r):h.call(String(o),n,r)},function(t,e){var r=l(h,t,this,e,h!==n);if(r.done)return r.value;var s=o(t),p=String(this),v=i(s,RegExp),g=s.unicode,m=(s.ignoreCase?"i":"")+(s.multiline?"m":"")+(s.unicode?"u":"")+(d?"y":"g"),y=new v(d?s:"^(?:"+s.source+")",m),w=void 0===e?4294967295:e>>>0;if(0===w)return[];if(0===p.length)return null===u(y,p)?[p]:[];for(var _=0,b=0,S=[];b>>0,1)},emit:function(e,n){(t[e]||[]).slice().map((function(t){t(n)})),(t["*"]||[]).slice().map((function(t){t(e,n)}))}}}();e.a=r},function(t,e,n){var r=n(11).f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||n(10)&&r(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},function(t,e){t.exports=!1},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){"use strict";var r,o,i,a,c=n(47),u=n(4),s=n(26),l=n(57),f=n(1),p=n(6),d=n(29),h=n(64),v=n(65),g=n(111),m=n(112).set,y=n(149)(),w=n(115),_=n(150),b=n(151),S=n(152),x=u.TypeError,P=u.process,O=P&&P.versions,E=O&&O.v8||"",R=u.Promise,j="process"==l(P),T=function(){},k=o=w.f,C=!!function(){try{var t=R.resolve(1),e=(t.constructor={})[n(3)("species")]=function(t){t(T,T)};return(j||"function"==typeof PromiseRejectionEvent)&&t.then(T)instanceof e&&0!==E.indexOf("6.6")&&-1===b.indexOf("Chrome/66")}catch(r){}}(),L=function(t){var e;return!(!p(t)||"function"!=typeof(e=t.then))&&e},A=function(t,e){if(!t._n){t._n=!0;var n=t._c;y((function(){for(var r=t._v,o=1==t._s,i=0,a=function(e){var n,i,a,c=o?e.ok:e.fail,u=e.resolve,s=e.reject,l=e.domain;try{c?(o||(2==t._h&&W(t),t._h=1),!0===c?n=r:(l&&l.enter(),n=c(r),l&&(l.exit(),a=!0)),n===e.promise?s(x("Promise-chain cycle")):(i=L(n))?i.call(n,u,s):u(n)):s(r)}catch(f){l&&!a&&l.exit(),s(f)}};n.length>i;)a(n[i++]);t._c=[],t._n=!1,e&&!t._h&&F(t)}))}},F=function(t){m.call(u,(function(){var e,n,r,o=t._v,i=M(t);if(i&&(e=_((function(){j?P.emit("unhandledRejection",o,t):(n=u.onunhandledrejection)?n({promise:t,reason:o}):(r=u.console)&&r.error&&r.error("Unhandled promise rejection",o)})),t._h=j||M(t)?2:1),t._a=void 0,i&&e.e)throw e.v}))},M=function(t){return 1!==t._h&&0===(t._a||t._c).length},W=function(t){m.call(u,(function(){var e;j?P.emit("rejectionHandled",t):(e=u.onrejectionhandled)&&e({promise:t,reason:t._v})}))},D=function(t){var e=this;e._d||(e._d=!0,(e=e._w||e)._v=t,e._s=2,e._a||(e._a=e._c.slice()),A(e,!0))},U=function(t){var e,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===t)throw x("Promise can't be resolved itself");(e=L(t))?y((function(){var r={_w:n,_d:!1};try{e.call(t,s(U,r,1),s(D,r,1))}catch(o){D.call(r,o)}})):(n._v=t,n._s=1,A(n,!1))}catch(r){D.call({_w:n,_d:!1},r)}}};C||(R=function(t){h(this,R,"Promise","_h"),d(t),r.call(this);try{t(s(U,this,1),s(D,this,1))}catch(e){D.call(this,e)}},(r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=n(66)(R.prototype,{then:function(t,e){var n=k(g(this,R));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=j?P.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&A(this,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),i=function(){var t=new r;this.promise=t,this.resolve=s(U,t,1),this.reject=s(D,t,1)},w.f=k=function(t){return t===R||t===a?new i(t):o(t)}),f(f.G+f.W+f.F*!C,{Promise:R}),n(52)(R,"Promise"),n(86)("Promise"),a=n(28).Promise,f(f.S+f.F*!C,"Promise",{reject:function(t){var e=k(this);return(0,e.reject)(t),e.promise}}),f(f.S+f.F*(c||!C),"Promise",{resolve:function(t){return S(c&&this===a?R:this,t)}}),f(f.S+f.F*!(C&&n(87)((function(t){R.all(t).catch(T)}))),"Promise",{all:function(t){var e=this,n=k(e),r=n.resolve,o=n.reject,i=_((function(){var n=[],i=0,a=1;v(t,!1,(function(t){var c=i++,u=!1;n.push(void 0),a++,e.resolve(t).then((function(t){u||(u=!0,n[c]=t,--a||r(n))}),o)})),--a||r(n)}));return i.e&&o(i.v),n.promise},race:function(t){var e=this,n=k(e),r=n.reject,o=_((function(){v(t,!1,(function(t){e.resolve(t).then(n.resolve,r)}))}));return o.e&&r(o.v),n.promise}})},function(t,e){t.exports={}},function(t,e,n){var r=n(11).f,o=n(25),i=n(3)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e,n){n(116)("asyncIterator")},function(t,e,n){var r=n(48)("meta"),o=n(6),i=n(25),a=n(11).f,c=0,u=Object.isExtensible||function(){return!0},s=!n(8)((function(){return u(Object.preventExtensions({}))})),l=function(t){a(t,r,{value:{i:"O"+ ++c,w:{}}})},f=t.exports={KEY:r,NEED:!1,fastKey:function(t,e){if(!o(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,r)){if(!u(t))return"F";if(!e)return"E";l(t)}return t[r].i},getWeak:function(t,e){if(!i(t,r)){if(!u(t))return!0;if(!e)return!1;l(t)}return t[r].w},onFreeze:function(t){return s&&f.NEED&&u(t)&&!i(t,r)&&l(t),t}}},function(t,e,n){"use strict";e.a=function(t,e){return void 0===e&&(e=""),t.substr(0,e.length)===e?t.slice(e.length):t}},function(t,e,n){"use strict";var r=n(57),o=RegExp.prototype.exec;t.exports=function(t,e){var n=t.exec;if("function"==typeof n){var i=n.call(t,e);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,e)}},function(t,e,n){var r=n(39),o=n(3)("toStringTag"),i="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(n){}}(e=Object(t),o))?n:i?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){var r=n(28),o=n(4),i=o["__core-js_shared__"]||(o["__core-js_shared__"]={});(t.exports=function(t,e){return i[t]||(i[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(47)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e,n){"use strict";n(146);var r=n(12),o=n(24),i=n(8),a=n(30),c=n(3),u=n(78),s=c("species"),l=!i((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),f=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var p=c(t),d=!i((function(){var e={};return e[p]=function(){return 7},7!=""[t](e)})),h=d?!i((function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[s]=function(){return n}),n[p](""),!e})):void 0;if(!d||!h||"replace"===t&&!l||"split"===t&&!f){var v=/./[p],g=n(a,p,""[t],(function(t,e,n,r,o){return e.exec===u?d&&!o?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}})),m=g[0],y=g[1];r(String.prototype,t,m),o(RegExp.prototype,p,2==e?function(t,e){return y.call(t,this,e)}:function(t){return y.call(t,this)})}}},function(t,e,n){"use strict";var r=n(5);t.exports=function(){var t=r(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(39);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e){e.f={}.propertyIsEnumerable},function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},function(t,e,n){var r=n(26),o=n(108),i=n(109),a=n(5),c=n(21),u=n(110),s={},l={};(e=t.exports=function(t,e,n,f,p){var d,h,v,g,m=p?function(){return t}:u(t),y=r(n,f,e?2:1),w=0;if("function"!=typeof m)throw TypeError(t+" is not iterable!");if(i(m)){for(d=c(t.length);d>w;w++)if((g=e?y(a(h=t[w])[0],h[1]):y(t[w]))===s||g===l)return g}else for(v=m.call(t);!(h=v.next()).done;)if((g=o(v,y,h.value,e))===s||g===l)return g}).BREAK=s,e.RETURN=l},function(t,e,n){var r=n(12);t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},function(t,e,n){"use strict";n(160);var r=n(5),o=n(60),i=n(10),a=/./.toString,c=function(t){n(12)(RegExp.prototype,"toString",t,!0)};n(8)((function(){return"/a/b"!=a.call({source:"a",flags:"b"})}))?c((function(){var t=r(this);return"/".concat(t.source,"/","flags"in t?t.flags:!i&&t instanceof RegExp?o.call(t):void 0)})):"toString"!=a.name&&c((function(){return a.call(this)}))},function(t,e,n){var r=n(5),o=n(162),i=n(84),a=n(83)("IE_PROTO"),c=function(){},u=function(){var t,e=n(79)("iframe"),r=i.length;for(e.style.display="none",n(114).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" \ No newline at end of file diff --git a/categories/angular/index.html b/categories/angular/index.html new file mode 100644 index 000000000..4152040f4 --- /dev/null +++ b/categories/angular/index.html @@ -0,0 +1,317 @@ +angular Articles | Articles by thoughtram

Articles in angular

  / Angular

Dynamic Angular components inside custom widgets

  / Angular

Advanced caching with RxJS

  / Angular

Custom Overlays with Angular's CDK - Part 2

  / Angular

Custom Overlays with Angular's CDK

  / Angular

Easy Dialogs with Angular Material

  / Angular

A web animations deep dive with Angular

  / Angular

Custom themes with Angular Material

  / Angular

Angular Master Class - Redux and ngrx

  / Angular

Three things you didn't know about the AsyncPipe

  / Angular

Using Zones in Angular for better performance

  / Angular

Making your Angular apps fast

  / Angular

A revamped Angular Master Class

  / Angular

Testing Angular Directives with Custom Matchers

  / Angular

Testing Services with Http in Angular

  / Angular

Two-way Data Binding in Angular

  / Angular

Resolving route data in Angular

  / Angular

Angular Animations - Foundation Concepts

  / Angular

Angular 2 is out - Get started here

  / Angular

Bypassing Providers in Angular

  / Angular

Custom Form Controls in Angular

  / Angular

Protecting Routes using Guards in Angular

  / Angular

Reactive Forms in Angular

  / Angular

Cold vs Hot Observables

  / Angular

Routing in Angular revisited

  / Angular

Component-Relative Paths in Angular

  / Angular

How to prevent name collisions in Angular providers

  / Angular

Exploring Rx Operators: map

  / Angular

Angular Providers using Map Literals

  / Angular

Template-driven Forms in Angular

  / Angular

Custom Validators in Angular

  / Angular

Angular Change Detection Explained

  / Angular

Zones in Angular

  / Angular

Understanding Zones

  / Angular

Taking advantage of Observables in Angular 2 - Part 2

  / Angular

Taking advantage of Observables in Angular

  / Angular

ngMessageFormat - Angular's unheard feature

  / Angular

Multiple Transclusion and named Slots

  / Angular

Upgrading Angular apps using ngUpgrade

  / Angular

Understanding @Injectable in Angular

  / Angular

Forward references in Angular

  / Angular

Host and Visibility in Angular's Dependency Injection

  / Angular

Angular Template Syntax Demystified - Part 1

  / Angular

Service vs Factory - Once and for all

  / Angular

Even better ES5 code for Angular

  / Angular

View Encapsulation in Angular

  / Angular

Taking Angular Master Class to the next level

  / Angular

Styling Angular components

  / Angular

Routing in Angular

  / Angular

Dependency Injection in Angular

  / Angular

Writing Angular code in ES5

  / Angular

The difference between Annotations and Decorators

  / Angular

Developing a tabs component in Angular

  / Angular

Developing a zippy component in Angular

  / Angular

Angular and i18n - The new world

\ No newline at end of file diff --git a/categories/angularjs/index.html b/categories/angularjs/index.html new file mode 100644 index 000000000..a08a9b106 --- /dev/null +++ b/categories/angularjs/index.html @@ -0,0 +1,17 @@ +angularjs Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/annoucements/index.html b/categories/annoucements/index.html new file mode 100644 index 000000000..2fbcb355b --- /dev/null +++ b/categories/annoucements/index.html @@ -0,0 +1,17 @@ +annoucements Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/announcements/index.html b/categories/announcements/index.html new file mode 100644 index 000000000..09be2050a --- /dev/null +++ b/categories/announcements/index.html @@ -0,0 +1,171 @@ +announcements Articles | Articles by thoughtram

Articles in announcements

  / Announcements

Farewell

  / Announcements

More GDE power at thoughtram

  / Announcements

Angular Master Class coming to Málaga

  / Announcements

RxJS Master Class and courseware updates

  / Announcements

Announcing Angular Master Class at Shopware

  / Announcements

Machine Learning Jump Start - Online Course

  / Announcements

Announcing Angular Master Class in Houston

  / Announcements

Announcing Angular Master Class in Berlin

  / Announcements

Join our free meetup in Denmark

  / Announcements

Announcing Angular Master Class in Denmark

  / Announcements

Dominic joins thoughtram

  / Announcements

Announcing Angular Master Class in Freiburg

  / Announcements

Announcing Angular 2 Master Class in Sydney

  / Announcements

Announcing Angular 2 Master Class in NYC

  / Announcements

Updates and announcements

  / Announcements

Thomas joins thoughtram

  / Announcements

Updates and announcements

  / Announcements

Sponsoring AngularConnect. Again.

  / Announcements

Angular 2 Jump Start at NG-NL 2016

  / Announcements

Angular 2 Master Class: Jump Start

  / Announcements

How we run trainings

  / Announcements

Angular Master Class Extended: ngUpgrade

  / Announcements

Pascal becomes a GDE

  / Announcements

Angular Training Day Bangkok

  / Announcements

Going full-time

  / Announcements

Sponsoring AngularConnect

  / Announcements

Speaking at code.talks

  / Announcements

Upcoming events in 2015

  / Announcements

Git Ninja Class in Amsterdam

  / Announcements

clog - A conventional changelog generator for the rest of us

  / Announcements

Git Ninja Class comes to Istanbul

  / Announcements

Tickets are on sale now!

  / Announcements

Organizing Hanovers first Rust meetup

  / Announcements

Announcing our first workshop

  / Announcements

We are thoughtram

\ No newline at end of file diff --git a/categories/company/index.html b/categories/company/index.html new file mode 100644 index 000000000..ed3f53afc --- /dev/null +++ b/categories/company/index.html @@ -0,0 +1,27 @@ +company Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/es-6/index.html b/categories/es-6/index.html new file mode 100644 index 000000000..f649c8b88 --- /dev/null +++ b/categories/es-6/index.html @@ -0,0 +1,17 @@ +es6 Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/git/index.html b/categories/git/index.html new file mode 100644 index 000000000..eb666ecec --- /dev/null +++ b/categories/git/index.html @@ -0,0 +1,27 @@ +git Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/machine-learning/index.html b/categories/machine-learning/index.html new file mode 100644 index 000000000..bd3998077 --- /dev/null +++ b/categories/machine-learning/index.html @@ -0,0 +1,53 @@ +machine-learning Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/meetups/index.html b/categories/meetups/index.html new file mode 100644 index 000000000..91e7f1cd9 --- /dev/null +++ b/categories/meetups/index.html @@ -0,0 +1,17 @@ +meetups Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/rebase-book/index.html b/categories/rebase-book/index.html new file mode 100644 index 000000000..78571c476 --- /dev/null +++ b/categories/rebase-book/index.html @@ -0,0 +1,17 @@ +rebase-book Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/rust/index.html b/categories/rust/index.html new file mode 100644 index 000000000..928869d8a --- /dev/null +++ b/categories/rust/index.html @@ -0,0 +1,63 @@ +rust Articles | Articles by thoughtram

Rust For JavaScript Developers

Because learning Rust with a JavaScript background doesn't have to be hard.

So you want to learn Rust?

I started learning Rust out of curiosity with zero experience in systems programming. I know the pain. With Rust for JavaScript Developers I'm creating an online resource with free content about the Rust programming language, primarily (but not only) aimed at developers with a strong JavaScript background.

- @PascalPrecht

\ No newline at end of file diff --git a/categories/rx/index.html b/categories/rx/index.html new file mode 100644 index 000000000..b698cbb0c --- /dev/null +++ b/categories/rx/index.html @@ -0,0 +1,27 @@ +rx Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/rxjs/index.html b/categories/rxjs/index.html new file mode 100644 index 000000000..2ea4ef429 --- /dev/null +++ b/categories/rxjs/index.html @@ -0,0 +1,27 @@ +rxjs Articles | Articles by thoughtram \ No newline at end of file diff --git a/categories/tools/index.html b/categories/tools/index.html new file mode 100644 index 000000000..5938648a9 --- /dev/null +++ b/categories/tools/index.html @@ -0,0 +1,17 @@ +tools Articles | Articles by thoughtram \ No newline at end of file diff --git a/cec743a8e1904a4f0268047a87341368/cd-4.svg b/cec743a8e1904a4f0268047a87341368/cd-4.svg new file mode 100644 index 000000000..e84b728bf --- /dev/null +++ b/cec743a8e1904a4f0268047a87341368/cd-4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/chunk-map.json b/chunk-map.json new file mode 100644 index 000000000..4b2a6f8b4 --- /dev/null +++ b/chunk-map.json @@ -0,0 +1 @@ +{"app":["/app-db11fdec89ef44788bcc.js"],"component---src-templates-blog-post-js":["/component---src-templates-blog-post-js-6a14d6c49d22aa388ff9.js"],"component---src-templates-category-js":["/component---src-templates-category-js-4376524bed80ab31f5c7.js"],"component---src-pages-404-js":["/component---src-pages-404-js-6a74e57e5569370f5224.js"],"component---src-pages-all-js":["/component---src-pages-all-js-7aa251af6b402c5bb6a7.js"],"component---src-pages-confirmation-thank-you-js":["/component---src-pages-confirmation-thank-you-js-e20b16d362e2acd832ef.js"],"component---src-pages-finish-newsletter-subscription-js":["/component---src-pages-finish-newsletter-subscription-js-9bc9dd352c7e25398f2b.js"],"component---src-pages-index-js":["/component---src-pages-index-js-34313bc70d6e2470e416.js"]} \ No newline at end of file diff --git a/commons-d9ac4630fec9b8cc9044.js b/commons-d9ac4630fec9b8cc9044.js new file mode 100644 index 000000000..0ae4858ec --- /dev/null +++ b/commons-d9ac4630fec9b8cc9044.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{0:function(e,t,n){"use strict";e.exports=n(173)},134:function(e,t,n){var r;e.exports=(r=n(197))&&r.default||r},135:function(e,t,n){"use strict";var r=n(142),a=n(0),l=n.n(a),i=n(143),o=n.n(i);function u(e){var t=e.description,n=e.lang,a=e.meta,i=e.title,u=r.data.site,c=t||u.siteMetadata.description;return l.a.createElement(o.a,{htmlAttributes:{lang:n},title:i,titleTemplate:"%s | "+u.siteMetadata.title,meta:[{name:"description",content:c},{name:"google-site-verification",content:"o9eGPEgIETEqYGombq7NiQuBIB_qa6gz1yAL7PxKyK0"},{name:"theme-color",content:"#384c54"},{property:"og:title",content:i},{property:"og:description",content:c},{property:"og:type",content:"website"},{name:"twitter:card",content:"summary_large_image"},{name:"twitter:creator",content:u.siteMetadata.author},{name:"twitter:title",content:i},{name:"twitter:description",content:c}].concat(a)})}u.defaultProps={lang:"en",meta:[],description:""},t.a=u},136:function(e,t,n){n(93),n(13),n(14),n(9),n(23),n(32),t.__esModule=!0;t.ATTRIBUTE_NAMES={BODY:"bodyAttributes",HTML:"htmlAttributes",TITLE:"titleAttributes"};var r=t.TAG_NAMES={BASE:"base",BODY:"body",HEAD:"head",HTML:"html",LINK:"link",META:"meta",NOSCRIPT:"noscript",SCRIPT:"script",STYLE:"style",TITLE:"title"},a=(t.VALID_TAG_NAMES=Object.keys(r).map((function(e){return r[e]})),t.TAG_PROPERTIES={CHARSET:"charset",CSS_TEXT:"cssText",HREF:"href",HTTPEQUIV:"http-equiv",INNER_HTML:"innerHTML",ITEM_PROP:"itemprop",NAME:"name",PROPERTY:"property",REL:"rel",SRC:"src"},t.REACT_TAG_MAP={accesskey:"accessKey",charset:"charSet",class:"className",contenteditable:"contentEditable",contextmenu:"contextMenu","http-equiv":"httpEquiv",itemprop:"itemProp",tabindex:"tabIndex"});t.HELMET_PROPS={DEFAULT_TITLE:"defaultTitle",DEFER:"defer",ENCODE_SPECIAL_CHARACTERS:"encodeSpecialCharacters",ON_CHANGE_CLIENT_STATE:"onChangeClientState",TITLE_TEMPLATE:"titleTemplate"},t.HTML_TAG_MAP=Object.keys(a).reduce((function(e,t){return e[a[t]]=t,e}),{}),t.SELF_CLOSING_TAGS=[r.NOSCRIPT,r.SCRIPT,r.STYLE],t.HELMET_ATTRIBUTE="data-react-helmet"},142:function(e){e.exports=JSON.parse('{"data":{"site":{"siteMetadata":{"title":"Articles by thoughtram","description":"High-quality, in-depth technical articles on Rust, Angular, Git and more by thoughtram.","author":"thoughtram"}}}}')},143:function(e,t,n){n(44),n(98),n(13),n(14),n(9),n(23),n(34),n(129),n(70),n(20),n(73),n(17),t.__esModule=!0,t.Helmet=void 0;var r=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}var p,h,m,g=(0,o.default)(c.reducePropsToState,c.handleClientStateChange,c.mapStateOnServer)((function(){return null})),v=(p=g,m=h=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.shouldComponentUpdate=function(e){return!(0,u.default)(this.props,e)},t.prototype.mapNestedChildrenToProps=function(e,t){if(!t)return null;switch(e.type){case s.TAG_NAMES.SCRIPT:case s.TAG_NAMES.NOSCRIPT:return{innerHTML:t};case s.TAG_NAMES.STYLE:return{cssText:t}}throw new Error("<"+e.type+" /> elements are self-closing and can not contain children. Refer to our API for more information.")},t.prototype.flattenArrayTypeChildren=function(e){var t,n=e.child,a=e.arrayTypeChildren,l=e.newChildProps,i=e.nestedChildren;return r({},a,((t={})[n.type]=[].concat(a[n.type]||[],[r({},l,this.mapNestedChildrenToProps(n,i))]),t))},t.prototype.mapObjectTypeChildren=function(e){var t,n,a=e.child,l=e.newProps,i=e.newChildProps,o=e.nestedChildren;switch(a.type){case s.TAG_NAMES.TITLE:return r({},l,((t={})[a.type]=o,t.titleAttributes=r({},i),t));case s.TAG_NAMES.BODY:return r({},l,{bodyAttributes:r({},i)});case s.TAG_NAMES.HTML:return r({},l,{htmlAttributes:r({},i)})}return r({},l,((n={})[a.type]=r({},i),n))},t.prototype.mapArrayTypeChildrenToProps=function(e,t){var n=r({},t);return Object.keys(e).forEach((function(t){var a;n=r({},n,((a={})[t]=e[t],a))})),n},t.prototype.warnOnInvalidChildren=function(e,t){return!0},t.prototype.mapChildrenToProps=function(e,t){var n=this,r={};return l.default.Children.forEach(e,(function(e){if(e&&e.props){var a=e.props,l=a.children,i=d(a,["children"]),o=(0,c.convertReactPropstoHtmlAttributes)(i);switch(n.warnOnInvalidChildren(e,l),e.type){case s.TAG_NAMES.LINK:case s.TAG_NAMES.META:case s.TAG_NAMES.NOSCRIPT:case s.TAG_NAMES.SCRIPT:case s.TAG_NAMES.STYLE:r=n.flattenArrayTypeChildren({child:e,arrayTypeChildren:r,newChildProps:o,nestedChildren:l});break;default:t=n.mapObjectTypeChildren({child:e,newProps:t,newChildProps:o,nestedChildren:l})}}})),t=this.mapArrayTypeChildrenToProps(r,t)},t.prototype.render=function(){var e=this.props,t=e.children,n=d(e,["children"]),a=r({},n);return t&&(a=this.mapChildrenToProps(t,a)),l.default.createElement(p,a)},a(t,null,[{key:"canUseDOM",set:function(e){p.canUseDOM=e}}]),t}(l.default.Component),h.propTypes={base:i.default.object,bodyAttributes:i.default.object,children:i.default.oneOfType([i.default.arrayOf(i.default.node),i.default.node]),defaultTitle:i.default.string,defer:i.default.bool,encodeSpecialCharacters:i.default.bool,htmlAttributes:i.default.object,link:i.default.arrayOf(i.default.object),meta:i.default.arrayOf(i.default.object),noscript:i.default.arrayOf(i.default.object),onChangeClientState:i.default.func,script:i.default.arrayOf(i.default.object),style:i.default.arrayOf(i.default.object),title:i.default.string,titleAttributes:i.default.object,titleTemplate:i.default.string},h.defaultProps={defer:!0,encodeSpecialCharacters:!0},h.peek=p.peek,h.rewind=function(){var e=p.rewind();return e||(e=(0,c.mapStateOnServer)({baseTag:[],bodyAttributes:{},encodeSpecialCharacters:!0,htmlAttributes:{},linkTags:[],metaTags:[],noscriptTags:[],scriptTags:[],styleTags:[],title:"",titleAttributes:{}})),e},m);v.renderStatic=v.rewind,t.Helmet=v,t.default=v},144:function(e,t,n){e.exports=n.p+"static/logo-8fe34043fb96d58a991170a33702a469.svg"},173:function(e,t,n){"use strict";n(72),n(67),n(94),n(13),n(14),n(9),n(23),n(44),n(18),n(53),n(42);var r=n(95),a="function"==typeof Symbol&&Symbol.for,l=a?Symbol.for("react.element"):60103,i=a?Symbol.for("react.portal"):60106,o=a?Symbol.for("react.fragment"):60107,u=a?Symbol.for("react.strict_mode"):60108,c=a?Symbol.for("react.profiler"):60114,s=a?Symbol.for("react.provider"):60109,f=a?Symbol.for("react.context"):60110,d=a?Symbol.for("react.forward_ref"):60112,p=a?Symbol.for("react.suspense"):60113,h=a?Symbol.for("react.suspense_list"):60120,m=a?Symbol.for("react.memo"):60115,g=a?Symbol.for("react.lazy"):60116;a&&Symbol.for("react.fundamental"),a&&Symbol.for("react.responder");var v="function"==typeof Symbol&&Symbol.iterator;function y(e){for(var t=e.message,n="https://reactjs.org/docs/error-decoder.html?invariant="+t,r=1;rO.length&&O.push(e)}function z(e,t,n){return null==e?0:function e(t,n,r,a){var o=typeof t;"undefined"!==o&&"boolean"!==o||(t=null);var u=!1;if(null===t)u=!0;else switch(o){case"string":case"number":u=!0;break;case"object":switch(t.$$typeof){case l:case i:u=!0}}if(u)return r(a,t,""===n?"."+U(t,0):n),1;if(u=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;cthis.eventPool.length&&this.eventPool.push(e)}function de(e){e.eventPool=[],e.getPooled=se,e.release=fe}a(ce.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=oe)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=oe)},persist:function(){this.isPersistent=oe},isPersistent:ue,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=ue,this._dispatchInstances=this._dispatchListeners=null}}),ce.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},ce.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var l=new t;return a(l,n.prototype),n.prototype=l,n.prototype.constructor=n,n.Interface=a({},r.Interface,e),n.extend=r.extend,de(n),n},de(ce);var pe=ce.extend({data:null}),he=ce.extend({data:null}),me=[9,13,27,32],ge=q&&"CompositionEvent"in window,ve=null;q&&"documentMode"in document&&(ve=document.documentMode);var ye=q&&"TextEvent"in window&&!ve,be=q&&(!ge||ve&&8=ve),Ee=String.fromCharCode(32),Te={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},we=!1;function Se(e,t){switch(e){case"keyup":return-1!==me.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function ke(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var _e=!1;var xe={eventTypes:Te,extractEvents:function(e,t,n,r){var a=void 0,l=void 0;if(ge)e:{switch(e){case"compositionstart":a=Te.compositionStart;break e;case"compositionend":a=Te.compositionEnd;break e;case"compositionupdate":a=Te.compositionUpdate;break e}a=void 0}else _e?Se(e,n)&&(a=Te.compositionEnd):"keydown"===e&&229===n.keyCode&&(a=Te.compositionStart);return a?(be&&"ko"!==n.locale&&(_e||a!==Te.compositionStart?a===Te.compositionEnd&&_e&&(l=ie()):(ae="value"in(re=r)?re.value:re.textContent,_e=!0)),a=pe.getPooled(a,t,n,r),l?a.data=l:null!==(l=ke(n))&&(a.data=l),W(a),l=a):l=null,(e=ye?function(e,t){switch(e){case"compositionend":return ke(t);case"keypress":return 32!==t.which?null:(we=!0,Ee);case"textInput":return(e=t.data)===Ee&&we?null:e;default:return null}}(e,n):function(e,t){if(_e)return"compositionend"===e||!ge&&Se(e,t)?(e=ie(),le=ae=re=null,_e=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1