Video Tutorial Writing Directives with Misko Hevery

Transcript:

0:00
MALE SPEAKER 1: So directives.
0:02
So what are directives?
0:04
I think directives are the coolest features of Angular
0:06
because what they basically let you do is teach the
0:08
browser new tricks.
0:10
Somebody on Twitter had an awesome quote, which I think–
0:14
I’m very proud of that they use this particular quote,
0:16
but, basically, they said, I’m pretty sure HTML6 goes under
0:20
the name of Angular.
0:22
And I think the reason why the person said that is because
0:25
Angular allows you to make new vocabulary for HTML so that
0:33
you can emulate just about any construct you can think of.
0:38
And the moment you get in the business of emulating
0:40
construct, which you really are in the business of is
0:42
creating a DSL.
0:43
Everybody knows what DSL is, right?
0:45
Domain Specific Language.
0:46
And so DSL for your application becomes–
0:51
HTML’s really good at one thing.
0:53
And that’s static documents.
0:55
But with the introduction of directives, you can extend the
1:00
vocabulary.
1:00
And so now, whatever you think you wish you had in HTML, and
1:06
you don’t have it in there, well, now you can have it with
1:08
directives.
1:10
So directives is basically this magic.
1:13
And there’s also this magic that is
1:15
very unique to Angular.
1:16
No other framework has anything like this.
1:20
There’s other frameworks, for example, Knockout.
1:24
And I’m not an expert in Knockout, but they use the
1:27
same philosophy of using attributes of putting extra
1:31
behavior in side of the HTML and then
1:33
bind to those behaviors.
1:35
But it turns out that, in Knockout, the set of
1:37
vocabulary that they have is fixed.
1:40
If the authors of Knockout didn’t put it in there, you
1:42
can’t have it.
1:44
But Angular’s very, very different in a sense that we
1:47
are the meta-framework.
1:48
We allow you to customize directives for yourself.
1:54
So let’s talk about what directives are because, well,
1:57
judging from the mailing list, and IRC questions, and so on,
2:00
I mean, there’s a lot of confusion about them.
2:02
And that’s probably my fault because I should’ve done a
2:06
better job documenting.
2:08
But at the same time, it is something that’s super
2:11
complicated.
2:12
And we’re trying every day making it simpler.
2:15
But it’s just a complex piece of stuff.
2:17
So anyway, let’s dive into it.
2:19
So the first thing I want to show you is how Angular
2:23
actually bootstraps itself.
2:24
How does the whole thing kicks in?
2:27
So normally, right here, you put in ng-app.
2:30
We’re all familiar with this.
2:33
By the way, I love questions.
2:34
So as I’m talking, if you want to know anything, just come up
2:37
to the microphone and ask.
2:39
AUDIENCE: [INAUDIBLE]
2:40
MALE SPEAKER 1: We have T-shirts for good questions,
2:42
and you don’t have to worry about de-railing me.
2:45
So normally, the ng-app.
2:46
And this is kind of a meta-directive that bootstraps
2:49
the whole application.
2:50
But in order for you to understand what’s actually
2:53
going on under the hood, I’ve created a manual
2:55
bootstrapping process.
2:57
And the manual bootstrapping process is basically here.
3:01
And I’m going to go through it.
3:02
So as every application has to have, you have to wait for
3:05
onload event.
3:06
You have to wait for the DOM to load.
3:08
Once the DOM is loaded, you can do a couple of things to
3:11
kind of get the application going.
3:12
So the first we have to do is, we have to find the root of
3:14
the document.
3:15
Normally, this is the location of ng-app, but in our case,
3:18
it’s just a root elements.
3:19
Then we have to list a set of modules.
3:22
Modules is probably another tech talk we’re going to have
3:24
to have, probably, on our next meetup and just discuss them.
3:28
Anyways, Angular has a built-in module called ng, and
3:31
that’s the thing that has all of the directives and
3:34
all the magic in.
3:35
It turns out that the same APIs we used for building
3:38
Angular are the same APIs you get to use for building your
3:40
own directives.
3:41
So there’s nothing special.
3:42
We don’t have any special access to the system, which is
3:45
important because it means that whatever we can do, you
3:47
can do as well.
3:49
So there’s this hidden module called ng, which you normally
3:53
don’t have to specify explicitly, but because we’re
3:56
doing a manual bootstrap, we have to do that.
3:58
So the next one is my application, and the my
4:00
application, that one simply says, make a new module called
4:04
my app, nothing really interesting there.
4:06
And the last one is an inline module and what this one does,
4:10
it basically tells the module system where the
4:12
root element is.
4:13
The root element which we got, the holdoff says, this is the
4:16
root element, so from now on, this is what it is.
4:19
Anyways, it turns out everything in Angular is
4:21
dependency injected.
4:22
And so this is no different.
4:23
So we create a root injector and we only create one
4:26
injector per application.
4:28
So if you’re ever in the business of creating a new
4:30
injector, you’re probably doing something wrong.
4:33
And from the injector, we get ahold of the compile service.
4:38
This is what does all of the magic.
4:40
And we’ll talk about this a lot more.
4:43
And what the compile service does is, it basically
4:47
traverses the DOM, starting at the root element.
4:50
And it looks for directives.
4:52
And we’ll talk more about how this is happening.
4:55
But once it discovers these directives, it executes its
4:58
compile function.
4:59
And the compile function is responsible for returning the
5:01
link function.
5:02
Again, we’re going to talk about all this
5:03
stuff in more detail.
5:05
And it returns this thing called the composite linking
5:07
function, which is essentially a collection of all the
5:09
linking functions in the system.
5:11
And I know a lot of people, I’m sure have had questions
5:13
about what’s the difference in compiling linking functions?
5:16
So why do we call it a
5:17
compiling and linking function?
5:18
And basically, the reason is, we kind of borrowed it from
5:21
the idea of compiling in C. You have to compile your
5:23
objects and create objects file, then you have to link
5:25
them together.
5:26
And so something very similar is happening here, where the
5:29
first phase is, we’re simply looking for the directives.
5:32
And as we call them, it’s the compile phase.
5:34
And once we locate all the directives, we have the
5:36
linking phase, which actually attaches it to the scope.
5:41
So the next part is, we get a hold of the root scope.
5:45
Every application has exactly one root scope.
5:47
And then we call the composite linking function within the
5:49
root scope.
5:50
And we kick the Angular to say, hey, go.
5:54
And this is basically a whole bootstrapping process.
5:56
So let me show you this inside of a debugger,
5:59
what it looks like.
6:01
So I’m already, I’ve got myself to the injector.
6:04
And so now, if we look at the structure of HTML, sorry, let
6:08
me pull up the application over here on
6:11
the right hand side–
6:12
currently, this is the structure that the browser
6:14
loaded into the DOM.
6:17
This is basically one to one translation from HTML to the
6:20
DOM structure.
6:21
There’s nothing has yet happened because we are
6:24
currently in the–
6:28
we haven’t done anything.
6:29
All we have done is kind of look for things.
6:30
And we have not even created the injector yet.
6:32
So we’re about to create injector.
6:33
So when we create injector, nothing really happens.
6:37
We get ahold of the compile service.
6:40
And now the magic is about to happen.
6:41
Now basically, we’re saying, compile service, go walk the
6:44
DOM starting at root element and go look for all those
6:46
directives.
6:47
And so, inside of our “Hello, world,” we have an li.
6:56
And it’s not cooperating again.
7:02
I’m sorry.
7:04
There is a strange bug inside–
7:05
there we go.
7:07
So we are about to run the compile service.
7:10
So again, let’s go back to HTML.
7:11
And notice that we have basically the raw directives
7:15
as we have them in here.
7:17
So you guys are familiar with ng-model is a directive.
7:18
ng-bind is a directive. ng-repeat is a directive.
7:21
So now something interesting happens that, when we call the
7:23
compile function, we actually are going to execute the
7:25
compile function of individual directives.
7:27
Now, most directives have no compile functions.
7:30
So it’s going to no app.
7:31
But some do, for example, a repeater.
7:34
So pay close attention to what happens to the ui li node when
7:37
I step over the compile.
7:40
Let’s go back here and step over.
7:43
Notice, it has disappeared.
7:45
And if you look at the structure of the DOM, it has
7:48
been replaced instead with a little comment.
7:51
So what’s happening is that the Repeater says, well, I’m
7:55
going to have to make a whole bunch of copies of these
7:56
things, right?
7:57
Because I’m a repeater, and so I need to be able to clone it.
8:00
And so its compile function simply yanks out the content,
8:04
puts it in a placeholder, which is just a comment, so it
8:07
knows where the other items are supposed to go.
8:10
And it, internally, then further compiles
8:13
the template itself.
8:16
And so the result is that at this stage we have transformed
8:21
the DOM structure because we extracted the
8:23
template out of it.
8:26
AUDIENCE: [INAUDIBLE]
8:28
MALE SPEAKER 1: Yes.
8:29
I assume so.
8:30
I mean, if you remove a DOM element, it’s probably
8:33
considered a DOM fragment because it is no longer part
8:36
of the render tree.
8:38
Yes, it’s basically been disconnected from the original
8:41
render tree, so, as you can see, the browser
8:44
doesn’t render it.
8:46
So now comes the linking function.
8:47
And then a linking function is, every directive has a
8:50
linking function, I think.
8:55
And so, when we execute the linking function, and when you
8:58
step over it, notice, again, nothing happened to the DOM.
9:00
And that’s because linking function basically, it is, in
9:04
essense, it creates the view.
9:06
If HTML is your template, which then gets loaded and
9:10
becomes a DOM, then view is the live thing that knows how
9:13
to react to different events.
9:15
And so when the linking function runs, we create all
9:19
the bindings.
9:19
And again, when we write a directive, this is going make
9:21
a little more sense.
9:23
And so, at this point, we have a live view.
9:25
Except, as you can see, nothing’s being rendered on
9:27
the screen.
9:27
And that’s because we have to kick the root scope and say,
9:31
hey, go for it.
9:32
Run it.
9:33
And so the moment we run apply, wow, the whole thing
9:36
comes together.
9:38
Does that make sense, in terms of bootstrapping process?
9:40
We’re going to delve more into these pieces more.
9:43
Any questions, yes?
9:44
AUDIENCE: How do you avoid content flash [INAUDIBLE]?
9:47
MALE SPEAKER 1: How do avoid content flash?
9:48
Excellent question.
9:49
We have a directive called ng-cloak.
9:53
You can check it out.
9:54
Basically, it’s a CSS rule that hides, basically, the
9:59
chunk of DOM until the compiling and linking function
10:02
goes through.
10:03
And then compile function then puts it back in.
10:05
So that’s how we do that trick.
10:07
There’s other tricks you can do.
10:10
Talk to us maybe afterwards.
10:11
But it’s a good question, but it’s slightly off topic.
10:16
OK, so–
10:20
and we have a basic Hello World.
10:22
So let’s go to the next step.
10:27
So let’s build a directive.
10:41
So I created a very simple Hello World app.
10:44
And you can see the source code on the left hand side and
10:46
the actual app running over here.
10:47
And I’m assuming most of you guys are not Angular
10:51
beginners, so you can translate what’s going on.
10:54
But basically, when I type, you can see that
10:56
the name gets updated.
10:58
I have a single controller over here.
11:00
And the controller simply sets up a name and leak variables.
11:07
And then I simply have a piece of debug information to show
11:11
the name and debug down here.
11:13
And I have an input, which allows me to change the name,
11:15
and then there is our controller called Demo Greet.
11:22
So let’s write a directive Demo Greet.
11:32
So I’m sure you guys know how to register a directive.
11:36
Simply, you take the module for the application, and you
11:38
simply say directive, Demo Greet.
11:40
Now the interesting thing to notice over here is that we
11:42
use camel case in this location.
11:45
But when you’re actually invoking it from
11:48
HTML, it’s dash case.
11:51
It’s simply a precedence that jQuery and other people have
11:54
set, that in, HTML, because it’s case insensitive, you
11:58
have to separate the words by dashes.
12:00
But if you separate the words by dashes, then inside of
12:02
JavaScript, you have to put everything in special curly
12:05
brackets and in quotes.
12:06
And so it’s a pain.
12:08
And to help you, basically, with that, we normalize it by
12:11
putting it into camel case for you, then
12:13
the access is simpler.
12:17
So the console over here simply–
12:20
we’ll print the console out over here and we’ll set the
12:23
text the Hello world.
12:24
So as you can see, inside of the div, this div, after the
12:29
application executives, simply has Hello world in there.
12:36
So let’s talk more about the compile and link function.
12:43
Whoops.
12:47
Thank you.
12:50
OK, so let’s make a compile function,
13:02
compile element, patterns.
13:07
And the job of the compile function is
13:08
return the link function.
13:12
And what I’m going to do is, I’m going to put a print out
13:17
over here, and I’m going to change this and say compile.
13:24
And it’s going to say compile function, and I’m going to
13:29
label this just for our benefit.
13:33
And let’s say I am going do element.addclass compiling.
13:44
And this is where the confusion begins, I think.
13:47
So let’s run this.
13:50
And let’s look at the DOM structure.
13:53
And so we see, the DOM structure has Hello World, and
13:56
it also has a class compiling.
13:58
So why in the world do we have two different ways of what
14:02
appears to be doing the same thing?
14:04
So before I do that, I’m going to do one more thing.
14:06
And I’m going to say, well, is the compile element the same
14:10
thing as the link element?
14:23
And when I run this, and I look at the console, notice it
14:27
says, true, over here, saying, the two of them are really the
14:31
same thing.
14:32
So why do we have this?
14:35
Anybody want to take a guess why there’s
14:36
two different things?
14:41
OK, so this is where the confusion starts.
14:43
People say, where do I use a compile function?
14:45
When do I do a link function?
14:46
They are so similar, what’s the difference?
14:48
So hopefully we can explain this.
14:51
So the first difference you notice is the signature
14:52
between those two functions.
14:54
Compile functions doesn’t have access to scope.
14:57
And again, if you go back to the bootstrapping phase, you
15:00
we’ll see that this is when the compile function gets run.
15:03
The scope doesn’t have created until afterwards.
15:06
This is why we don’t have access to the scope.
15:09
But for many practical purposes, the two look the
15:13
same, until I do this.
15:23
And then the differences start to become obvious.
15:28
So notice the compile function got executed exactly once,
15:32
whereas the linking function gets executed once for each
15:35
iteration of the repeater.
15:38
So there’s two of them over here.
15:40
And once we have a repeater, the two are
15:46
no longer the same.
15:46
See, it says false.
15:48
So the compile element and the link element are
15:49
no longer the same.
15:51
You can also see, if I go here, that both of them have
16:01
the class compiling.
16:02
So now, how can it be?
16:03
How can it be that both have the class compiling when the
16:06
compile function gets executed exactly once?
16:10
And so the answer is that the compile function got to modify
16:14
the template, which ng-repeat used to stamp out new
16:20
instances for it.
16:23
So think of the compiler function as the thing that
16:25
works on a template and the thing that is allowed to
16:28
change the template itself by, for example, adding a class to
16:33
it or anything like that.
16:34
But it’s the linking function that actually does the work of
16:37
binding the two together because the linking function
16:39
has access to the scope.
16:41
And it’s the linking function that executives once for each
16:44
instantiation of the particular template.
16:47
So the only kind of things you can placed inside of the
16:50
compile functions are things that are common across all of
16:53
the instances.
16:54
And that’s not very common thing to do because you might
16:59
place things like that adding class elements, but typically
17:04
the class elements you add are different for each instance,
17:07
so you can’t really put it inside of the compile phase.
17:10
Does that make sense?
17:12
I see a lot of confused faces.
17:14
AUDIENCE: [? Singleton ?]
17:15
MALE SPEAKER 1: I’m sorry?
17:16
AUDIENCE: It’s like a singleton?
17:17
MALE SPEAKER 1: Sure, you can think of it that way.
17:19
The point is, it’s the thing that runs on a template,
17:22
whereas the link function is the thing that runs on an
17:26
instance that’s actually instantiated.
17:28
And it’s confusing because, unless you’re inside of a
17:32
repeater, the two look identical because there’s a
17:34
one to one correspondence between the
17:35
template and instance.
17:36
But once you get into a repeater, that’s
17:38
no longer the case.
17:39
And so I’ve seen a lot of people write incorrect
17:44
directives because they put things that don’t belong
17:47
inside of the compile function,
17:48
they put it in there.
17:49
And it kind of works.
17:50
But then they put it inside of a repeater, and all hell
17:52
breaks loose.
17:52
Yes?
17:53
AUDIENCE: [INAUDIBLE]
18:01
MALE SPEAKER 1: No, so typically, unless you’re
18:03
modifying the template, you should forget about the
18:06
compile function, just stick with the linking function.
18:08
Because the linking function is the thing that gives you
18:10
the proper instance and also the proper scope.
18:14
So the compile function is purely for modifying the
18:17
template and nothing else.
18:20
OK, I think we’ve made that point.
18:23
OK, so let’s do more fun stuff.
18:27
So this is kind of a made-up directive, it doesn’t really
18:30
do anything.
18:30
So I’m just going to play with it.
18:33
But let’s get rid of the compile function because, as I
18:34
said, it’s very rare for you to having to use it.
18:40
So let’s make a link function.
18:46
And let’s try to do something useful.
18:54
So there’s a couple things you might want to do it inside of
18:56
a directive.
18:58
You might want to be able to talk to the outside world, for
19:00
example, get ahold of a event from the user, for example,
19:03
like a click event.
19:04
You want to be able to read from the scope, and you also
19:07
would like to be able to write to the scope, maybe because of
19:11
some event.
19:13
So let’s say we would like to make it, so as I type over
19:16
here, this thing updates over here.
19:19
So you might be tempted to say, well, how about we just
19:22
say, Hello world, and we have a scope.
19:23
And the variable’s called name.
19:26
And so let’s just write something like this.
19:30
But if you execute this, it works the first time.
19:33
And then you start typing, and it no longer updates.
19:35
That’s because, at the beginning, the controller set
19:38
the name to world.
19:39
And so you were able to render yourself.
19:42
But then future updates, you simply are not being notified.
19:44
So this isn’t going to work.
19:45
What we need to do is we need to be able to
19:47
have a watch, basically.
19:49
So we have to say scope.watch.
19:52
What are we going to watch?
19:53
We’re going to watch the name property.
19:57
Oops.
19:59
We’ve got to get the name out of this, and
20:04
let’s move this up.
20:07
And let’s put it like that.
20:13
And voila, we essentially have our own binding.
20:18
Pretty easy, huh?
20:20
So keep in mind also that all the DOM manipulations should
20:25
really be happening inside the directive.
20:26
The point of the directive is to be the glue between your
20:29
DOM and your scope.
20:31
So this is nice.
20:33
But if you think about it, it doesn’t really encapsulate the
20:35
system very well because what you really want to be able to
20:38
say– let’s get rid of the repeater for a second.
20:41
What you really want to be able to say is, configure the
20:44
directive with information.
20:46
So rather than hard wiring to name, you really want to say,
20:49
I’m telling you what to bind by passing the
20:51
name into the system.
20:55
And so we can set an attribute.
20:58
And when I set an attribute, we can get ahold of this
21:00
attribute in here.
21:01
So instead of hard coding it to name, there is this object
21:04
called adders.
21:05
And if we look at adders inside of our console, you see
21:13
that it shows all the parameters.
21:15
And one of the parameters is Demo Greet.
21:16
Now when this executed, Demo Greet was pointing to nothing.
21:19
So it shows nothing.
21:21
But because I updated it, let me refresh this, the Demo
21:27
Greet now points to name.
21:28
So instead of watching the name directly, I can simply
21:34
say Demo Greet.
21:36
So watching the attribute on the element called Demo Greet.
21:40
And this will now perform the same operation–
21:45
essentially, the binding.
21:46
But it’s configurable.
21:47
I don’t have to watch the name.
21:49
I can watch other things than name.
21:52
And so this is a good way to parameterize your stuff.
21:55
Does that make sense so far?
21:57
I see a lot of confused faces and serious people.
22:00
I like people that are happy, not serious.
22:02
AUDIENCE: [INAUDIBLE]
22:04
MALE SPEAKER 1: OK, so going right along.
22:08
So the other thing you probably want to do is, let’s
22:11
say we wanted to update the state of the parameters.
22:16
So what you could do is, you could say
22:17
scope name equals abc.
22:20
And it would work the first time.
22:23
But what if you wanted to make it abc every time
22:25
you click on it.
22:27
So we simply say, element, and this is kind of jQuery-like,
22:31
so you say bind click function.
22:38
And we say console.log click.
22:47
And we do something like this.
22:52
And we run it.
22:54
And now, every time I click, you can see
22:56
that the click’s working.
22:57
It’s coming up over here.
22:58
But nothing’s happening.
22:59
It doesn’t say abc.
23:00
It keeps saying world.
23:01
So what’s going on in here?
23:02
Well, what’s going on is that there’s really this Angular
23:06
world and non-Angular world.
23:08
And when an event comes in, when you’re inside of jQuery,
23:11
you really are in non-Angular world.
23:13
And the Angular just doesn’t know that
23:16
something has changed.
23:17
And so you have to tell Angular about it.
23:20
And there’s a lot of ways to tell Angular about it.
23:22
And so you might say, well, let me write something like
23:24
this, [INAUDIBLE].
23:26
And that’s certainly one way to do it.
23:29
And so now, if I click on it, it changes.
23:31
I type, click, changes again.
23:34
But actually, it turns out this is not
23:36
[? an economical ?]
23:37
way to do this because what if name threw an exception?
23:41
You’d never know.
23:43
So the better way to do is to say, function and
23:50
place it like this.
23:52
So now, when I start typing, and I click
23:55
on it, it gets reset.
23:56
And if an exception is thrown inside of this operation,
24:00
everything would work.
24:06
So the apply is basically the gateway that gets you from
24:08
outside of the Angular world into the Angular world.
24:11
OK, but, again, there’s a problem here which is that
24:13
we’re hard coding it to name.
24:14
It would much rather have it as a parameter that’s being
24:19
passed into it.
24:20
And so there’s a service call parse.
24:21
And so I can ask for the parse service
24:24
because this is Angular.
24:25
You can just ask for things.
24:27
So you say, I want a parse service.
24:29
And what the parse service allows you to
24:32
do is to say, parse–
24:37
what are we going to parse?
24:38
We’re going to parse the attribute Demo Greet.
24:44
And it creates this function that you can use for calling.
24:48
And if they function is assignable, it
24:49
has an assign property.
24:52
And then you can pass in a scope and the value you want
24:56
to assign to it.
24:59
And then you are fully parameterized, so if you
25:05
choose to bind to a different thing, the whole thing works.
25:09
So those are the two ends.
25:10
You can read out of the scope, using the watch, and you can
25:13
also write to the scope.
25:14
And if you’re going to write to the scope, you probably
25:16
want to use something like parse to execute the
25:18
expression on your behalf.
25:20
Does that makes sense?
25:21
AUDIENCE: Why wouldn’t you just use the bracket notation?
25:23
MALE SPEAKER 1: Why not just use the bracket notation?
25:25
Because the bracket notation would only work for basic
25:28
things like name.
25:29
But if I said name.first, the bracket notation would create
25:33
a property called name.first, which is not what you meant.
25:36
You meant to say the name property on the scope, and
25:39
then that object has a property called first.
25:42
And so it’s a different expression.
25:44
So all Angular expressions that very much look like
25:47
JavaScript, but they’re actually not JavaScript, they
25:50
execute through the parse service.
25:55
Let’s see what else do I have [INAUDIBLE].
25:59
AUDIENCE: Question.
25:59
MALE SPEAKER 1: Yes?
26:00
AUDIENCE: [INAUDIBLE]
26:10
that’s just something that you kind of know?
26:14
MALE SPEAKER 1: So no.
26:15
You can find out this easily.
26:18
And the way you find this out is you say,
26:21
AUDIENCE: Can you repeat the question?
26:23
MALE SPEAKER 1: Yeah, so how do know that
26:24
you’re inside of Angular?
26:27
Thank you, Igor.
26:29
When I click, this is a stack trace at
26:32
that particular location.
26:34
And if you look at this particular stack trace, you’ll
26:36
notice that we’re essentially in jQuery land.
26:39
In other words, what you’re looking
26:40
for is an apply function.
26:42
There is no apply function below you.
26:45
So all operations inside of Angular need
26:47
to happen in apply.
26:48
And usually, all of the directives properly transition
26:50
you into the apply.
26:51
So unless you’re writing your own directive, when you’re
26:55
using our directives inside your controllers, you never
26:58
have to worry about applies.
26:59
It should just automatically just happen
27:01
at the right moment.
27:02
But if you’re outside, and you’re writing your own
27:04
directive, you basically are outside of it, and you have
27:06
know when to transition into it.
27:09
Does that answer your question?
27:11
AUDIENCE: It does.
27:12
It still seems [? cryptic. ?]
27:16
MALE SPEAKER 1: So basically, the event comes from the
27:18
browser, whether it’s a user event or [? exit char ?]
27:21
event, or whatever, a timer event.
27:23
And it has to pass through apply before we can do the
27:28
operations on their model.
27:31
But you just have to know.
27:34
AUDIENCE: [INAUDIBLE]
27:36
MALE SPEAKER 2: Well, whenever you’re using Angular APIs,
27:39
everything [INAUDIBLE].
27:40
MALE SPEAKER 1: Microphone, sir?
27:43
There’s one right over there.
27:44
[LAUGHTER]
27:46
AUDIENCE: [INAUDIBLE]
27:47
MALE SPEAKER 1: You know, I looked for it, and I
27:48
couldn’t find it.
27:49
MALE SPEAKER 2: So whenever you are interacting with
27:51
Angular APIs, this is all taken care of for you.
27:55
Whenever you’re building directives is when you
27:57
typically interact with DOM events or jQuery plug-ins.
28:02
And that’s when you’re not in the Angular world.
28:04
And that’s what you need to know about apply.
28:06
But directive is something that you usually build when
28:09
you are more advanced because of all the existing APIs.
28:14
So I think you should be fine.
28:18
As long as you know if you’re using Angular
28:20
APIs, you’re fine.
28:21
If the event is coming from jQuery or DOM,
28:23
you need call apply.
28:27
MALE SPEAKER 1: OK, so this is the basic directive.
28:29
And so what we have done is, basically,
28:30
we created an attribute.
28:31
Oh, we forgot different ways of invoking it.
28:36
So before we move on, I want to show you one more thing.
28:38
So this particular thing, we say Demo Greet.
28:41
But it turns out you can also say x-demo.
28:43
You can also say demo-greet if you like HTML.
28:48
These are all equivalent.
28:49
You can say data-demo if you really insist
28:52
on being HTML compliant.
28:54
But you ‘re going to do even crazier stuff.
28:56
You can say class demo, I think it’s a colon, like that.
29:03
And so when I refresh this, it’s not going to work.
29:06
And the reason it’s not going to work because, by default,
29:08
we only bind to attributes.
29:10
And so we have to help it out and say restrict.
29:14
And we say we can do be attribute or
29:16
you can be a class.
29:19
And now it works.
29:21
Now why would you want to bind to class?
29:23
Well, you would like to bind to class because sometimes
29:25
you’re using libraries like Bootstrap.
29:27
And these libraries say, well, if you put these classes then
29:31
the right stuff happens.
29:32
And what you really want to say using Angular is like,
29:33
well, if you put those classes, not only the right
29:35
stuff renders, but the right behavior is going
29:37
to be there as well.
29:38
And so you want to execute that stuff as well.
29:40
And so you can do this, and you can also do this.
29:47
You can also do Demo Greet as a tag.
29:55
Unfortunately, this one also takes an argument, so we have
29:58
to do it twice.
30:07
And this will also work as expected.
30:11
So you have a choice in the way you structure your stuff.
30:14
Yes?
30:16
AUDIENCE: [INAUDIBLE]
30:18
MALE SPEAKER 1: There’s restrictions.
30:20
There is no old browsers.
30:21
There’s only IE and everything else.
30:23
So in IE, yes, good question.
30:26
What about IE?
30:27
IE, it turns out, likes the XML namespace provided that
30:30
you put XML namespace declarations on the top.
30:33
So that’s kind of a good strategy for IE.
30:36
But it doesn’t like elements that it’s not familiar with.
30:39
And there’s a page on our documentation that
30:42
specifically talks about all the quirks of IE.
30:45
And the way you get IE to cooperate is, if you have to
30:49
declare all of the elements ahead of time.
30:51
And the way you declare them is, you say document.create.
30:53
elementmyelement.
30:56
And then, all a sudden, something magically starts
30:59
working inside of IE.
30:59
Don’t ask.
31:01
Read the documentation.
31:02
I think it’s fixed in IE 9.
31:06
But yes, we don’t recommend doing this by default, and
31:10
only because of IE 8.
31:12
You can make it work if you do extra tricks that I’ve
31:15
described over there.
31:16
But by default this is not going to fly with IE 8.
31:21
OK, let’s move on to the next thing.
31:23
So this is basic directive.
31:25
So let’s talk about, now, something that we would call
31:31
components.
31:32
And so [INAUDIBLE] me smaller.
31:38
OK so here’s a basically typical web page.
31:41
Usually, on the upper right corner, you have an I am.
31:45
And that’s who you’re logged in as.
31:47
And then, in this particular case, I’m actually looking at
31:49
a set of developers.
31:50
And this is the Angular team.
31:52
And the code for rendering this guy and the code for
31:58
rendering this guy is the same thing.
32:00
So let’s look.
32:01
Basically, it’s over here.
32:02
All right, saying, if you have these divs and these images
32:06
and this H1, and the set of classes placed over there,
32:11
then we will render something that looks like
32:13
the right hand side.
32:14
But because we have to do it twice, we basically are
32:16
repeating this code along with this code over here.
32:21
And so now we’re getting into the world of reusable
32:23
components.
32:24
We know that repeating yourself is a bad idea inside
32:26
of code, and you want to be able to reuse.
32:28
If this was JavaScript, you’d say, oh, let
32:30
me extract the function.
32:31
And let me call that function because it’s the same thing
32:33
that has to happen in two different locations.
32:35
And so, in essence, what we want to do it’s the same exact
32:38
thing, but in HTML.
32:39
I want to say, this is a reusable component.
32:42
Let’s pull it out and reuse it.
32:46
So the first thing we’re going to do is
32:48
pull it out like this.
32:56
And we’re going to put it inside of a separate HTML
32:58
file, like this.
33:02
And then we have to do a couple of things.
33:04
We have to say, we have a directive called profile.
33:07
And let’s say it’s going to be an element-based directive.
33:11
And let’s point it to that file over there.
33:13
And now, we can go back to our index.html, so obviously, if I
33:18
refresh now, it’s going to be all broken.
33:21
But if I say profile, then now the profile comes up.
33:31
And if I copy the same exact thing over here, then voila,
33:37
I’m back in business.
33:39
So that’s nice, but there’s a couple of problems with it.
33:41
Anybody?
33:45
Well, this makes all kinds of assumptions.
33:47
Mainly, that it assumes that the thing that you’re going
33:51
after is email.
33:52
So it kind of makes sense over here because, Demo, the
33:54
controller, says that the email is me.
34:00
But when we’re inside of the HTML, and we’re iterating over
34:05
all the developers, we have to make it email.
34:07
And if we change it to dev, and we run it,
34:11
whoa, what just happened?
34:12
How comes there’s Mieszko everywhere.
34:15
Like, I might like myself, but this is not the
34:17
thing you want to see.
34:18
Well, what’s happening is that the profile assumed that the
34:23
thing to look at is email.
34:25
And it worked because we said ng-repeat email in developers.
34:28
But when we used the different variable, the profile still
34:32
assumes email.
34:33
And it’s because the repeater doesn’t override the email.
34:36
The way the scopes work, you look at the parent scope.
34:38
And the parents scope happens to contain the
34:40
email for the user.
34:41
And so we have the situation where the user’s being
34:44
replicated everywhere.
34:45
This is not what you want.
34:49
But it gets worse.
34:51
And that is, you know, if you have a function in JavaScript,
34:53
what’s nice about this function is that inside of
34:55
this function you’re kind of encapsulated
34:56
from the outside world.
34:57
You have your private variables.
34:59
And you don’t have to worry about messing anybody up.
35:01
And if I have a variable called x, I don’t have to
35:03
worry that the function that called me also happens to
35:04
variable x.
35:05
I know it’s a different scoping.
35:08
So let’s create a linking function here.
35:17
And so the basic problem is that if I’m in some of the
35:20
scope, and, I say scope.league is equal to–
35:27
and I refresh, notice that I was able to clobber the
35:31
outside world.
35:32
Not only am I might not playing nice because I’m just
35:35
simply assuming that the outside world is what– there
35:38
has to be this special thing called email, otherwise, I
35:39
don’t work.
35:40
But also, if I happen to have an internal state inside of
35:43
it, I’m clobbering it.
35:45
Now there’s a lot of ways to solve it.
35:47
And the simplest way is to say, well,
35:50
we have a new scope.
35:52
So this directive needs to have a new scope.
35:56
And now you see that we’re no longer clobbering
35:58
[? league ?].
35:59
And why are we no longer clobbering [? league ?] is
36:01
because if you look at the scopes, the outermost scope
36:03
has the email, which is myself.
36:08
And the individual iterations, they have a developer called
36:14
Mieszko, a developer [? Minar ?], developer
36:16
[? Voyta ?].
36:17
And each one is writing it’s own scope.
36:20
Each one has its own private kind of place to play.
36:23
So now we solved one particular problem, that we
36:25
are no longer leaking to the outside world.
36:27
But we haven’t solved the other problem that we are not
36:31
parameterized.
36:32
It’s essentially as if you called a
36:34
function that has no arguments.
36:36
And the arguments are in the global location.
36:39
You just have to write into a global location, and then you
36:41
call a function.
36:42
And then that function knows to look
36:43
in the global location.
36:43
This is not how we code.
36:45
We know this is a recipe for disaster.
36:48
So what you really want to do is say, actually, I want to
36:52
have my own private world.
36:54
But not only do I want to have my private world, is, I want
36:56
to be able to take variables from the outside.
36:59
And so, in this particular case, if we look at our HTML,
37:02
it turns out that this email is what we’re interested
37:05
because email is used in both of these locations.
37:09
So what we want to say is that email is what we want.
37:14
Now where do we get email from, is the question.
37:16
And we can do a couple of things.
37:17
And we can say equals.
37:20
And then you can go to index.html and then say–
37:23
so first of all, if I do this, and I refresh, then
37:25
everybody’s broken because I didn’t pass email into it.
37:31
So what we have to do this is we have to say
37:32
email equals email.
37:41
And voila, this guy works.
37:43
And we can do the same thing over here.
37:45
We can say email equals–
37:46
this time we bind it to developer, and, voila, the
37:49
rest of them work.
37:50
So now we basically have segregated ourselves, not only
37:54
from the global state outside of us, so we don’t actually
37:56
clobber or anything, but also we have proper parameters that
37:59
we’re passing into it.
38:02
Now, at this point, a lot of people say, well, why this?
38:05
Why not that?
38:08
What if this is what I want?
38:10
So if I do this, obviously, it’s not going to work
38:12
because, well, that’s not–
38:14
so I have to be consistent.
38:16
I also have to change it over here.
38:19
It’s not going to work because we’re actually passing the
38:22
string email.
38:24
This kind of confusing because it’s double escaped.
38:26
But if we look at the HTML, and we find a directive–
38:35
we find the profile–
38:37
we see that the email–
38:43
Sorry, why is this?
38:45
I think I broke something.
38:50
Oh, of course.
38:51
It is completely broken because it’s trying to read it
38:58
as a string.
38:59
So let me just disable this for a second.
39:01
And what I want to show you it is that the profile has an
39:07
email that should’ve been set.
39:22
OK, I’m not sure why it’s not showing up.
39:24
But anyways, the way to do this is to say, we want to
39:27
read the attribute.
39:33
And it works now.
39:34
So whether you specify equals or you specify @ sign, you’re
39:38
basically deciding whether you want to use this style versus
39:42
the other style.
39:43
So you say, well, why do we have two styles?
39:45
What’s the difference?
39:46
Well, it turns out there is a significant difference.
39:49
And the difference is, basically, that, in this case,
39:52
you’re getting back a string.
39:53
Because what you’re saying is, I’m updating this attributes
39:57
email in a DOM.
39:59
And then the component is watching that attribute, and
40:01
the attributes have to be strings.
40:03
So you cannot pass anything but a string here.
40:08
Whereas, if the first case we had, what we’re really saying
40:12
is that we’re taking the expression.
40:13
And the expression could be an object.
40:15
And so what we’re getting is a reference to the object.
40:17
So the two are really different beasts.
40:19
And what it really comes down to is, are you actually
40:23
intending people to modify the attribute.
40:26
Does the modification of the attribute actually make sense
40:29
in this particular case.
40:31
So for example, ng-click, you don’t really need to modify
40:33
the attribute ng-click, that’s meaningless, right?
40:35
But if you have image source, then you put double curlies
40:37
because you’re saying, I actually want to modify the
40:39
attribute because that makes what the browser is actually
40:42
going to render.
40:43
Kind of make sense?
40:46
Yes.
40:47
AUDIENCE: [INAUDIBLE]
40:55
MALE SPEAKER 1: Yeah, so if you want to pass a URL, should
40:56
we pass it as a string or a curly.
40:59
That’s totally up to you.
41:01
It’s kind of your decision, unless Igor has an opinion
41:05
about this thing.
41:07
It’s hard to have a set of rules that say, oh, you have
41:10
to do this versus that.
41:12
It kind of depends on the context of the component.
41:14
If I want to simulate something that behaves like an
41:18
image tag, then obviously I’m going to use double curly
41:20
because I want to simulate the fact that changing the
41:23
attribute has a behavior.
41:26
If I have a full on component, then I will most likely use as
41:32
a reference to an object rather than as double curlies.
41:37
AUDIENCE: Let’s say if there’s a link, and I want to render
41:39
it as a link in the page, which I can click later on–
41:43
MALE SPEAKER 1: That’s independent because the way
41:47
that the outside world communicates with the
41:51
component is independent to [? where ?] the component
41:54
actually renders the link.
41:56
So this is how you would render it inside of the
41:57
component template.
41:58
And that’s up to you to decide how the rendering happens.
42:02
But keep in mind that the @ sign, the moment you’re using
42:05
an attribute, you’re saying, what you’re
42:06
getting back is a string.
42:08
So the other advantage of a string is that you can have
42:10
interpolation going on inside of the double curlies.
42:13
So you can say something like email/abc.
42:20
And then if you run it, you actually see that abc got
42:24
added to it, to each of the– well, actually, just here
42:27
because of the top one.
42:29
So if what you want is to give the user the ability to have
42:33
interpolation, then you probably want double curlies.
42:36
If what you want is actually get a reference to an object,
42:38
then interpolation is meaningless because
42:41
interpolation always gives you a string.
42:43
AUDIENCE: Thank you.
42:50
MALE SPEAKER 1: OK, I think that’s all for this guy.
42:53
Any other questions before we go to the next [? stop? ?]
42:59
OK, so let’s talk about the most complicated thing that
43:04
people always get confused about.
43:05
And it is?
43:08
Transclusion.
43:09
OK, yes.
43:10
Let’s talk about transclusion.
43:12
So the component I showed you is nice because it
43:15
encapsulated a piece of code.
43:18
But the problem with that component is that that’s the
43:20
end of the line.
43:21
That component cannot have other components inside of it.
43:24
Let me rephrase that–
43:25
the component itself can have components in there, but it’s
43:28
not what you want.
43:29
What you want is to declare a component like this, where you
43:33
are wrapping something, in this case, a simple
43:37
interpolation, inside of the component in there.
43:41
So what you’re saying is, take the content of the components.
43:44
So let’s say I’m a zippy.
43:46
Everybody knows what a zippy is, right?
43:47
It kind of opens up and closes like this.
43:49
Currently, there’s no content.
43:50
We’ll put it in a second in there.
43:54
But the key is that the content itself is getting–
43:59
normally, the component that we have showed you a second
44:03
ago does not allow for the content of that profile to
44:06
transferred.
44:07
The profile was always empty.
44:08
There was nothing inside of it.
44:10
Does that makes sense?
44:14
Some people look still confused.
44:15
Anybody still confused on this?
44:19
We’ll wait for the plane to pass.
44:22
OK, so what we want is, we want to pass hello in.
44:26
And so let’s have a look what’s happening in here.
44:32
So if you look at HTML, here’s a zippy.
44:34
And if you expand zippy, you’ll see that zippy got
44:37
replaced by its content.
44:39
So where’s the content come from?
44:40
Well, here’s a directive.
44:42
And the content for zippy comes from here.
44:45
And so what happens is that the zippy content clobbers the
44:52
content that was there originally.
44:55
And because of this clobbering, it is not possible
44:58
to nest directives.
44:59
But that’s not very useful because there are a lot of
45:03
directives you can think of like tabs, or zippy, or
45:07
window, or a modal dialogue box, although that’s not a
45:10
very good use case–
45:11
that you would like to model as, hey, there’s this thing
45:13
that goes around and then I want to be able to put the
45:16
content of it.
45:17
And I want to be able to put different content depending on
45:19
where I to initiate this particular component.
45:22
And so this is what transclusion is all about.
45:25
So let’s go back to the directive.
45:27
So we want to basically say to the system is, the zippy
45:31
contains the content.
45:33
Don’t clobber it.
45:33
Pull it out.
45:37
So you say transclude is true.
45:39
And so before the directive places its rendering HTML in
45:45
its place, it pulls out the content and says, I’m going to
45:48
save this for later.
45:50
OK?
45:52
And so the next thing we have to do is, we have to, inside
45:56
of the template, of the directive, say, well, where do
45:59
we want to put it back into?
46:01
And so we do this by saying ng-transclude.
46:04
Does that makes sense?
46:07
And then, when we refresh?
46:09
Voila, the Hello world from the
46:11
outside went to the inside.
46:20
So now let’s talk about this some more.
46:24
So let’s have a name here.
46:26
So I have two fields, greeting and world.
46:29
So notice the greeting changes the top portion.
46:32
And this one changes the inside of the component.
46:36
So if you look at the code, the greeting changes the
46:39
title, which we just talked about.
46:40
We can use double curlies for the interpolation.
46:43
And the content is hello name, but really the content could
46:46
be anything.
46:47
You could put another zippy in there, or nest them, or
46:50
whatever you want–
46:51
other components, et cetera.
46:52
But let’s just keep it simple.
46:53
Let’s just have hello name in there.
46:55
So what’s interesting is that, think about how scopes are.
47:00
This name is actually a child of the zippy.
47:06
So let’s go to the zippy.
47:10
And it would be a tragedy of encapsulation if I could say
47:15
esc name equally like this, and that would clobber the
47:20
world in here.
47:23
Do you see what the issue is?
47:26
If I wrote to name, which just happened to be the name that
47:31
is used over here, then because the content got
47:35
transcluded, it got copied to me, as a child, I would be
47:39
clobbering the name.
47:41
And therefore the copied content wouldn’t behave the
47:44
way you would think it would behave.
47:48
And so now we have to do a little bit of [? dance ?] with
47:50
scopes in order to make this thing possible.
47:53
So what we want to do is say, look, the stuff that goes
47:56
inside of it, I know it’s inside of another directive,
47:59
but that directive needs to be isolated for all the good
48:02
reasons we talked about earlier.
48:03
So it has to have its own scope so that it’s isolated.
48:08
We talked about it earlier how the creation of the scope
48:10
creates isolation, so we don’t leak content outside.
48:13
But also, it means that the thing that we copied, in this
48:16
case, the hello name, has to have a scope that is not a
48:21
child scope of the component.
48:25
Because if it would be a child scope of the component, it
48:27
would see all the attributes, all of the
48:30
properties on the scope.
48:32
And so the thing I just showed you would clobber name.
48:36
So by writing into the name inside of the scope, I would
48:39
clobber it for the transcluded content.
48:41
And it would no longer behave as you would expect.
48:45
So what the transclusion does is, not only does it pull out
48:48
the content, but it also pulls the content
48:50
in the correct scope.
48:52
So it copies the scope, it creates a new child scope.
48:55
And it actually creates the two scopes as siblings.
48:59
So in other words, the component itself and the
49:04
transcluded scopes are siblings next to each other so
49:07
that they don’t inherit from each other in order to create
49:10
the correct illusion of isolation.
49:13
Does that make sense?
49:17
Really?
49:18
That makes sense?
49:20
AUDIENCE: They’re both children of the–
49:21
MALE SPEAKER 1: The parents scope, yes.
49:23
AUDIENCE: But the isolated scope doesn’t [INAUDIBLE].
49:27
MALE SPEAKER 1: Right, so isolated scope is not
49:30
inheriting.
49:34
I showed you the earlier problem that if I didn’t
49:36
properly provide a user email, it defaulted to the parent
49:41
email and then rendered the wrong thing.
49:43
So I want to make sure that it doesn’t happen.
49:44
So if I forget to initialize something inside of a
49:47
component, I don’t accidentally
49:48
inherit from somebody.
49:49
So the components have a non-inheriting scope.
49:53
So that isolates the component from the context that
49:56
it is placed in.
49:57
But we have a second problem, which is that we have to
49:59
isolate the transcluded content from the component
50:02
itself because you can imagine a component, and a component
50:05
has a hole where the thing got moved into.
50:13
Yes?
50:14
MALE SPEAKER 2: Can you say that again?
50:15
[LAUGHTER]
50:18
MALE SPEAKER 1: OK, so if we didn’t do any of these
50:21
gymnastics, then what we would have is, we would have a
50:23
component, and the problem with that component would be
50:25
that it would see everything that is around it.
50:27
That would be the equivalent of you having a JavaScript
50:29
function that would inherit all the global properties from
50:33
the parent.
50:34
And that would mean that if you would forget to declare a
50:36
property inside of the component, and that such a
50:40
property would just happen to be declared above you, you
50:43
would see the wrong one.
50:44
And if you don’t want to do that.
50:45
You want to basically say, no, no, no, as a component, you
50:48
don’t get to see the parent attribute because that breaks
50:52
encapsulation.
50:53
So you flip a switch and say, for all components, we do not
50:58
inherit properties.
50:59
And that’s how we isolate the component.
51:01
So that’s the first problem.
51:03
The second problem is, is if you copy the content, in this
51:06
case, if you copy the hello name, hello name right here,
51:10
and move it inside of a zippy, inside of the body– because
51:14
if you look into here, this is where the hello
51:16
name ended up in here.
51:18
If you move it over there, then normal rules are, oh this
51:23
thing would inherit from the parent and
51:26
the parent is zippy.
51:27
But that would means that it would inherit from a scope
51:29
that doesn’t see the parent, which means this particular
51:32
scope wouldn’t be able to get to the name.
51:37
So by copying the hello name into the component, you can no
51:41
longer see the name.
51:42
Worse, not only can you not see the name, if the name is
51:48
happens to be declared inside of the component, now you see
51:51
the component’s name.
51:53
And that’s not what we expect to happen with normal
51:57
encapsulation.
51:59
So what we have to do is, we have to a little bit of
52:01
gymnastics with the scopes, and we have to
52:02
make them as siblings.
52:04
And basically, the parent scope of this binding is the
52:11
controller.
52:12
And the parents scope of the zippy is the controller.
52:17
But one scope inherits.
52:18
So in this case, we want to inherit, so that if I refer to
52:21
a name, I’m actually going to get the name from the
52:24
controller.
52:25
In this case, I don’t want to inherit because I’m going to
52:27
basically say, if I happens to refer to a name, and I didn’t
52:30
declare it inside of the component, I shouldn’t be
52:32
getting the one from parent.
52:36
Does that make sense?
52:37
AUDIENCE: So only the zippy is an isolate.
52:39
MALE SPEAKER 1: Only the zippy is an isolate.
52:41
The transcluded scope is not an isolate.
52:43
AUDIENCE: [INAUDIBLE]
52:44
They don’t inherit, so you can’t implicitly grab the
52:47
parents from an isolated scope.
52:48
Could you explicitly, with the–
52:50
MALE SPEAKER 1: $parent?
52:51
Yes, you can definitely get it using the $parent.
52:55
So the only thing you get is what you declare inside of
52:59
this declaration.
53:00
And you can use the equals @ sign for attributes, and
53:04
there’s also an ampersand for expressions that you can
53:07
execute later, which we haven’t talked about.
53:08
But it’s in the documentation.
53:10
And so the way to think about it is, you have a function
53:14
with parameters.
53:15
That’s what component wants to be.
53:19
Anyways, so this wraps up the directives.
53:22
Hopefully, I’ve answered many of your questions.
53:24
And they’re less mysterious.
53:26
And again, I want to stress that directives is really the
53:29
unique thing about Angular that nobody else has.
53:34
There’s no other framework that I know of that can do
53:37
this trick where it basically gives you the ability to make
53:40
your own directives and your own attributes and build
53:43
a DSL out of it.
53:44
Normally, we build applications because we just
53:47
plaster div and div on top of a div and
53:50
put bunch of classes.
53:51
But with this, you can build yourself a syntax.
53:53
You can say, hey, I really want to have a zippy.
53:56
So I don’t want to create a whole set of divs to make a
53:59
zippy, I just want to say, I want to have a zippy.
54:01
I want to have a tab.
54:02
I just want to say, have a tab, and you get it.
54:04
And this is why it’s so natural for the Flex
54:06
developers, which, I believe some of them are ex-Flex
54:09
folks, right?
54:11
It’s not embarrassing, you can raise your hand.
54:13
[LAUGHTER]
54:13
I’m an ex-Flex guy as well.
54:16
This is why it’s so natural for them because in Flex, this
54:18
is the mode of operation where you just use MXML to declare
54:22
your structure and off you–
AngularJS 4U

AngularJS 4U

AngularJS 4U is a development blog and Angular Community where you can share your latest ng development tips, articles, demos, plugins, modules and more.

Leave a Reply

Your email address will not be published. Required fields are marked *