Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Increasing code reuse through Components

traceypowersg edited this page Oct 17, 2013 · 24 revisions

In the last chapter, we created a basic Recipe Book app. In this chapter, we will add to the app by creating a feature that will allow users to rate each recipe.

###What you will learn Previously, you learned about how Angular implements MVC controllers through Directives. In this chapter, we will introduce you to Components, which are a second way Angular implements MVC controllers. When you’re finished, you will understand the similarities and differences between the two types of controllers, and you will understand when to use Directives and when to use Components.

###The code Here is the code for the next version of our Recipe Book app. Copy the code to your local environment, load it into Dart Editor, and run it. You’ll notice that the rating, which used to be displayed as a boring number has now become a not-so-boring cluster of stars. Play around with the star ratings by changing the rating on each recipe. Click on any of the recipes to display its details, and notice there is also a star rating Component in the recipe details section. Change the recipe rating in the details section and notice that the rating in the recipe list section also changes.

###Angular Components The ratings feature is an Angular Component. Components are light-weight, reusable, self contained UI components that have a single specific purpose. The rating Component is a great example of a small, simple, re-usable component. It can be used anywhere in your app as many times as you want. Nothing about the ratings Component is intrinsically tied to our RecipeBook. We could use this Component in any app to rate anything. ####Using Components from your app Using a component from your app is simple. Just create an HTML element with the name of the Component, and pass any required properties in as HTML attributes.

<rating max-rating="5" rating="ctrl.selectedRecipe.rating"></rating>

####Creating and configuring Components Creating a Component is similar to creating a Directive. To create a Component, simply create a class, and then add the NgComponent annotation. Here is the annotation for our ratings Component:

@NgComponent(
    selector: 'rating',
    templateUrl: 'rating_component.html',
    cssUrl: 'rating_component.css',
    publishAs: 'ctrl',
    map: const {
      'max-rating': '@maxRating',
      'rating': '=rating'
    }
)

The NgComponent annotation is similar to the NgDirective annotation, but Components have a few more moving parts. Let’s look at each property in the annotation.

#####selector The selector property serves the same purpose as the selector property in a Directive. It tells Angular which CSS selector should trigger the Component. By convention, selectors for Components are element names, not attributes (notice the lack of [] chars around the selector). This is because you can only specify one Component on any html element. By contrast, you can specify more than one Directive on an element.

This example shows the basic differences between how Directives and Components selectors are declared, and how they’re used in the HTML template.

@NgComponent(
    selector: 'rating',

<rating></rating>

@NgDirective(
    selector: '[recipe-book]',

<div recipe-book>
  ...
</div>

#####templateUrl and cssUrl Since Components are self contained, they need to know what HTML template and CSS to use for their view. Components do not use the HTML of your app. They have their own.

#####publishAs The publishAs property serves the same purpose as the publishAs property in a Directive. It defines the name that the view should use to reference the Component. Note that the publishAs name is only visible to the Component’s view, not your app’s view. Your app has no direct visibility into the internals of the Component.

Let’s look at our Component’s html template:

<span class="stars"
      ng-if="ctrl.rating != null"
      ng-repeat="star in ctrl.stars"
      ng-click="ctrl.handleClick(star)"
      ng-class="ctrl.starClass(star)">
  {{ctrl.starChar(star)}}
</span>

ctrl here refers to the properties and methods on the RatingComponent.

#####map The last property we see in the NgComponent annotation is an attribute map. It maps HTML attributes to properties on the Component and represents the API for the Component. Users of this Component will be able to supply their own values for the attributes in this map.

map: const {
   'max-rating': '@maxRating',
   'rating': '=rating'
}

On the left is the HTML attribute. Since it’s HTML, it is case insensitive and multiple word properties are written with dashes.

On the right is the property on the Component. Since it’s Dart, it is case sensitive, and property names are camel cased.

The @ and = require some explaining. There are a few different ways to declare how an attribute is evaluated. @ and = are the most common, and we will describe them here. You can read more about all the ways to declare an attribute here.

@ Means map the DOM attribute string. The string will be taken literally, or interpolated if it contains {{}}. In our example, we saw this: max-rating=”5”. You could also get the value from the controller by doing something like max-rating=”{{ctrl.someProperty}}”.

@ attributes are unidirectional. A copy of the attribute is passed to the Component and each instance of the Component has it’s own copy. The Component can change its local value of @ attributes without changing the value outside the Component.

= Means treat the DOM attribute as an expression. Evaluate the expression and pass the result to the Component. You can use any valid expression, for example, some-eq-attribute=”foo + bar”, which would pass the string “foobar” to the Component. In our example, we set an = attribute from a property on the RecipeController: rating=”ctrl.selectedRecipe.rating”

= attributes are bidirectional. The Component can change the value of an = attribute, and it will change outside the Component. In this way, Components can change model objects in your app. In our example, the rating Component changes the rating on your RecipeBook’s model objects.

There are a few other properties that can be set on a Component annotation, but these are the most important. We will cover some of the less common ones in later chapters.

####How is a Component different from a Directive?

The key difference between Components and Directives is that the inner structure of Components are isolated from their surroundings and can be thought of as black boxes.

Components create their own scope hierarchy that is invisible from the outside world.They don’t have direct access to the app’s scope, nor does the app have direct access to a Component’s scope.

Components isolate their views from their surroundings by creating a Shadow DOM. Using a Shadow DOM allows Components to be used anywhere without suffering from things like CSS name collisions.

###Angular features In this chapter, we introduced you to two more built in Angular Directives - ng-if and ng-class. ####ng-if The ng-if Directive allows you use to evaluate sections of the DOM conditionally. The ng-if Directive takes an expression. If the expression is false, then the portion of the DOM underneath the if is removed. ng-if does not change the visibility of the DOM element. It removes it.

####ng-class The ng-class Directive allows you to set CSS classes on an element dynamically using an expression that evaluates to the classes to be added.

Home | Prev