-
Understand the Use Case for Custom Directives
-
Create a Custom Directive
-
Review: What are Directives?
-
What Can Custom Directives Do?
-
Our First Custom Directive
-
More About the DDO
-
templateUrl
andrestrict
Options -
Oh, Behave!
-
Rainbows and Unicorns
-
Lab / After Hours
Take a few minutes to discuss the Angular directives we've used so far and be prepared to discuss what each one does...
-
Add custom behavior to elements:
- Add "click to edit" behavior to HTML content
- Do something on mouseover, like show a popup
- Etc.
-
Make reusable widgets with custom behavior:
- Login forms
- Step-by-Step Wizard
- Movable Panels
- Etc.
-
Make custom elements:
- Turn lots of HTML into a tiny amount
- Make HTML even more semantic
-
Copy and Review
-
Start a local web server
> http-server
If you don't have it installed do this> npm install -g http-server
-
Create a new file for our directives:
js/my-directives.js
-
Remember to load it in
index.html
-
Not much different than the other types of components we've been declaring...
angular.module('app') .directive('wdiFirst', wdiFirst); function wdiFirst() { return { // Directive's DDO }; }
---
### First Direcive
<br>
- The Directive Definition Object (**DDO**) has several options (key/value pairs) we can use to define our directive.
- The first option we'll look at is `template`
```js
function wdiFirst() {
return {
template: 'Hello <strong>{{wdi}}</strong>'
};
}
```
---
### First Directive
<br>
- Where do we _use_ directives?
- Let's add our directive below our `jumbotron`
```html
<wdi-first></wdi-first>
```
Notice how Angular converts Javascript's _camel-casing_ to _kebob-casing_ in the HTML
- Congrats on creating your first custom directive!<br><br>**QUESTIONS?**
---
## More About the **DDO**
<br>
- You've witnessed the power of Angular's built-in directives, so you can imagine that the options in the `DDO` might be a little complex - and you would be right!
- [This page](https://docs.angularjs.org/api/ng/service/$compile) documents the options for the `DDO`.
- We're not going to learn how to create the next `ng-repeat` in this lesson, but let's take a baby step or two...
---
## _templateUrl_ and _restrict_ Options
- Let's say we want to make the form reusable. Let's convert our `wdi-first` to a `person-form`:
```js
.directive('personForm', personForm);
function personForm() {
return {
restrict: 'AE',
templateUrl: 'person-form.html'
};
}
```
- The `restrict` option specifies _how_ the directive can be used in the HTML.<br> `A` - Can be used as an **attribute**<br> `E` - Can be used as an **element**
---
### _templateUrl_ and _restrict_ Options
<br>
```js
...
// template: 'Hello...
templateUrl: 'person-form.html'
...
-
Inline templates using the
template
option are okay for little snippets, however, most of the time we will want to put our HTML into separate template files and loaded with thetemplateUrl
option. -
Create the
person-form.html
file.
- Move this HTML from
index.html
intoperson-form.html
<div class="row col-xs-4 col-xs-offset-4">
<h3>Example Form</h3><hr>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" ng-model="person.name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" id="email" class="form-control" ng-model="person.email">
</div>
<div class="form-group">
<label for="mobile">Mobile Number</label>
<input type="text" id="mobile" class="form-control" ng-model="person.mobile">
</div>
<button class="btn btn-success">Add Person</button>
</div>
-
Now put our new directive where the HTML was in
index.html
<person-form></person-form>
-
Remove the obsolete
<wdi-first></wdi-first>
directive that's below the
jumbotron
-
Refresh!
-
Directives can add "behavior" to the HTML as well.
-
For example, think about what
ng-click
does... -
The
link
option in the DDO is where we can add behavior or perform more complex DOM manipulation. -
We assign a function to
link
that accepts several useful arguments provided by Angular.
-
As an example, let's say our users want all text in an
<input>
to be automatically selected when it is clicked. -
We will create a directive that's added as an attribute to any
<input>
to accomplish this behavior.... .directive('wdiSelText', wdiSelText); function wdiSelText() { return { restrict: 'A' }; }
This is a good start, now let's add our
link
function...
-
Let's enter this
link
function. Don't forget to add a comma after therestrict
option:function wdiSelText() { return { restrict: 'A', link: function(scope, element, attrs) { element.on('click', function() { element[0].select(); }); } }; }
What are we doing here?
-
Now let's test it by adding our
wdi-sel-text
directive to one of the<input>
elements.
-
Take a look at the HTML in
person-form.html
. I don't know about you, but I really dislike having to write this<div class="form-group"> <label for="name">Name</label> <input type="text" id="name" class="form-control" ng-model="person.name"> </div>
every time I want to use a nice Bootstrap
<input>
! -
What if we could just write this instead?
<input ng-model="person.name" bs-input="Name">
Now how much would you pay?
-
Here are the specs for our
bs-input
:- Wrap the
<input>
with a<div>
that has a class ofform-group
- Create a
<label>
element with text set to the string in thebs-input
attribute. - Set the
for
attribute on the<label>
to the value of theid
attribute of the<input>
. Ifid
does not exists, generate a unique id and set it on the<input>
as well. - Add a
form-control
class to the<input>
- If the
type
of the<input>
has not been set, addtype="text"
to the<input>
- Wrap the
-
Here's the code...
function bsInput () {
return {
restrict: 'A',
template: '<div class="form-group" ng-transclude><div>',
replace: true,
transclude: 'element',
link: function (scope, element, attrs) {
var label = angular.element('<label>' + attrs.bsInput + '</label>');
var inp = angular.element(element.children()[0]);
if (inp.attr('id')) {
label.attr('for', inp.attr('id'));
} else {
inp.attr('id', 'id' + scope.$id);
label.attr('for', 'id' + scope.$id);
}
inp.addClass('form-control');
if (!inp.attr('type')) {
inp.attr('type', 'text');
}
element[0].insertBefore(label[0], inp[0]);
}
};
}
-
Before we walk through the code that defines our
bs-input
directive, let's test the sucker out. -
Let's reduce the HTML in
person-form.html
to this:
<div class="row col-xs-4 col-xs-offset-4">
<h3>Example Form</h3><hr>
<input ng-model="person.name" bs-input="Name">
<input ng-model="person.email" bs-input="Email">
<input ng-model="person.mobile" bs-input="Mobile">
<button class="btn btn-success">Add Person</button>
</div>
and let 'er fly!
"I'm pretty sure that HTML6 is going under the codename AngularJS"
Scotch on the Rocks, 2013
#### Questions?
- Here are a couple of exercises to practice the magic of Angular:
- Refactor the directive code from this lesson into a module file.
- Write an attribute
swap-colors
directive that swaps an element'sbackground-color
andcolor
properties whenever the mouse pointer is over the element.
- Refactor the directive code from this lesson into a module file.