From e69c2872939cda30f13a1a8390bbc61662c12d9a Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Wed, 23 Oct 2013 14:04:47 -0700 Subject: docs(guide/directive,guide/compiler,): drastically improve --- docs/content/error/compile/ctreq.ngdoc | 2 +- docs/content/error/compile/iscp.ngdoc | 3 +- docs/content/error/compile/nonassign.ngdoc | 4 +- docs/content/guide/compiler.ngdoc | 266 +++++- docs/content/guide/directive.ngdoc | 1436 +++++++++++++++------------- 5 files changed, 1048 insertions(+), 663 deletions(-) (limited to 'docs') diff --git a/docs/content/error/compile/ctreq.ngdoc b/docs/content/error/compile/ctreq.ngdoc index 9f9a1ec1..e3f46532 100644 --- a/docs/content/error/compile/ctreq.ngdoc +++ b/docs/content/error/compile/ctreq.ngdoc @@ -3,7 +3,7 @@ @fullName Missing Required Controller @description -This error occurs when {@link api/ng.$compile template compiler} tries process the directive that specifies the `require` option in a {@link guide/directive#writing-directives_directive-definition-object directive definition}, +This error occurs when {@link api/ng.$compile HTML compiler} tries to process a directive that specifies the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object `require` option} in a {@link api/ng.$compile#description_comprehensive-directive-api directive definition}, but the required directive controller is not present on the current DOM element (or its ancestor element, if `^` was specified). To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element. diff --git a/docs/content/error/compile/iscp.ngdoc b/docs/content/error/compile/iscp.ngdoc index 0d7dbec1..0bfe216b 100644 --- a/docs/content/error/compile/iscp.ngdoc +++ b/docs/content/error/compile/iscp.ngdoc @@ -21,4 +21,5 @@ myModule.directive('directiveName', function factory() { }); ``` -Please refer to the {@link guide/directive#writing-directives_directive-definition-object directive definition docs} to learn more about the api. +Please refer to the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object +`scope` option} of the directive definition documentation to learn more about the API. diff --git a/docs/content/error/compile/nonassign.ngdoc b/docs/content/error/compile/nonassign.ngdoc index 9f78ed3f..3870c2ed 100644 --- a/docs/content/error/compile/nonassign.ngdoc +++ b/docs/content/error/compile/nonassign.ngdoc @@ -3,7 +3,9 @@ @fullName Non-Assignable Expression @description -This error occurs when a directive defines an isolate scope property that support two-way data-binding (using the `=` mode in the {@link guide/directive#writing-directives_directive-definition-object directive definition}) but the directive is used with an expression that is not-assignable. +This error occurs when a directive defines an isolate scope property +(using the `=` mode in the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object +`scope` option} of a directive definition) but the directive is used with an expression that is not-assignable. In order for the two-way data-binding to work, it must be possible to write new values back into the path defined with the expression. diff --git a/docs/content/guide/compiler.ngdoc b/docs/content/guide/compiler.ngdoc index 257722e6..a8b96aac 100644 --- a/docs/content/guide/compiler.ngdoc +++ b/docs/content/guide/compiler.ngdoc @@ -2,6 +2,15 @@ @name Developer Guide: HTML Compiler @description +
This means that any changes to the data need to be re-merged with the template and then
-`innerHTML`ed into the DOM. Some of the issues with this approach are: reading user input and merging it with data,
-clobbering user input by overwriting it, managing the whole update process, and lack of behavior
-expressiveness.
+`innerHTML`ed into the DOM. Some of the issues with this approach are:
-Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
+1. reading user input and merging it with data
+2. clobbering user input by overwriting it
+3. managing the whole update process
+4. lack of behavior expressiveness
+
+Angular is different. The Angular compiler consumes the DOM, not string templates.
The result is a linking function, which when combined with a scope model results in a live view. The
-view and scope model bindings are transparent. No action from the developer is needed to update
-the view. And because no `innerHTML` is used there are no issues of clobbering user input.
+view and scope model bindings are transparent. The developer does not need to make any special calls to update
+the view. And because `innerHTML` is not used, you won't accidentally clobber user input.
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
well.
-The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
+The Angular approach produces a stable DOM. The DOM element instance bound to a model
item instance does not change for the lifetime of the binding. This means that the code can get
hold of the elements and register event handlers and know that the reference will not be destroyed
by template data merge.
+
+
+
+## How directives are compiled
+
+It's important to note that Angular operates on DOM nodes rather than strings. Usually, you don't
+notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically.
+
+However it's important to keep this in mind when calling `$compile` yourself, because passing it a string
+will fail. Instead, use `angular.element` to convert a string to DOM before passing elements into
+Angular's `$compile` service.
+
+HTML compilation happens in three phases:
+
+ 1. {@link api/ng.$compile `$compile`} traverses the DOM and matches directives.
+
+ If the compiler finds than an element matches a directive, then the directive is added to the list of
+ directives that match the DOM element. A single element may match multiple directives.
+
+ 2. Once all directives matching a DOM element have been identified, the compiler sorts the directives
+ by their `priority`.
+
+ Each directive's `compile` functions are executed. Each `compile` function has a chance to
+ modify the DOM. Each `compile` function returns a `link` function. These functions are composed into
+ a "combined" link function, which invokes each directive's returned `link` function.
+
+ 3. `$compile` links the template with the scope by calling the combined linking function from the previous step.
+ This in turn will call the linking function of the individual directives, registering listeners on the elements
+ and setting up {@link api/ng.$rootScope.Scope#methods_$watch `$watch`s} with the {@link api/ng.$rootScope.Scope `scope`}
+ as each directive is configured to do.
+
+The result of this is a live binding between the scope and the DOM. So at this point, a change in
+a model on the compiled scope will be reflected in the DOM.
+
+Below is the corresponding code using the `$compile` service.
+This should help give you an idea of what Angular does internally.
+
++ var $compile = ...; // injected into your code + var scope = ...; + + var html = ''; + + // Step 1: parse HTML into DOM element + var template = angular.element(html); + + // Step 2: compile the template + var linkFn = $compile(template); + + // Step 3: link the compiled template with the scope. + linkFn(scope); ++ +### The difference between Compile and Link + +At this point you may wonder why the compile process has separate compile and link phases. The +short answer is that compile and link separation is needed any time a change in a model causes +a change in the **structure** of the DOM. + +It's rare for directives to have a **compile function**, since most directives are concerned with +working with a specific DOM element instance rather than changing its overall structure. + +Directives often have a **link function**. A link function allows the directive to register +listeners to the specific cloned DOM element instance as well as to copy content into the DOM +from the scope. + +
+Hello {{user}}, you have these actions:
+++ +Clicking on the "show" button will open the dialog. The dialog will have a title, which is +data bound to `username`, and it will also have a body which we would like to transclude +into the dialog. + +Here is an example of what the template definition for the `dialog` widget may look like. + ++ + + ++
++ +This will not render properly, unless we do some scope magic. + +The first issue we have to solve is that the dialog box template expects `title` to be defined, but +the place of instantiation would like to bind to `username`. Furthermore the buttons expect the +`onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the +widget. To solve the mapping issue we use the `locals` to create local variables which the template +expects as follows: + +++{{title}}
+ + +
+ scope: {
+ title: '@', // the title uses the data-binding from the parent scope
+ onOk: '&', // create a delegate onOk function
+ onCancel: '&', // create a delegate onCancel function
+ visible: '=' // set up visible to accept data-binding
+ }
+
+
+Creating local properties on widget scope creates two problems:
+
+ 1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
+ template will bind to parent scope property. This is unpredictable and undesirable.
+
+ 2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
+ properties which the transclusion needs for data-binding. In our example the `title`
+ property of the widget clobbers the `title` property of the transclusion.
+
+
+To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
+isolated scope does not prototypically inherit from the child scope, and therefore we don't have
+to worry about accidentally clobbering any properties.
+
+However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
+isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
+is a child of the original scope, before the widget created an isolated scope for its local
+variables. This makes the transcluded and widget isolated scope siblings.
+
+This may seem to be unexpected complexity, but it gives the widget user and developer the least
+surprise.
+
+Therefore the final directive definition looks something like this:
+
+
+transclude: true,
+scope: {
+ title: '@', // the title uses the data-binding from the parent scope
+ onOk: '&', // create a delegate onOk function
+ onCancel: '&', // create a delegate onCancel function
+ visible: '=' // set up visible to accept data-binding
+},
+restrict: 'E',
+replace: true
+
+
diff --git a/docs/content/guide/directive.ngdoc b/docs/content/guide/directive.ngdoc
index f548164f..b709b7e1 100644
--- a/docs/content/guide/directive.ngdoc
+++ b/docs/content/guide/directive.ngdoc
@@ -2,741 +2,889 @@
@name Directives
@description
-Directives are a way to teach HTML new tricks. During DOM compilation directives are matched
-against the HTML and executed. This allows directives to register behavior, or transform the DOM.
-
-Angular comes with a built in set of directives which are useful for building web applications but
-can be extended such that HTML can be turned into a declarative domain specific language (DSL).
-
-# Invoking directives from HTML
-
-Directives have camel cased names such as `ngBind`. The directive can be invoked by translating
-the camel case name into snake case with these special characters `:`, `-`, or `_`. Optionally the
-directive can be prefixed with `x-`, or `data-` to make it HTML validator compliant. Here is a
-list of some of the possible directive names: `ng:bind`, `ng-bind`, `ng_bind`, `x-ng-bind` and
-`data-ng-bind`.
-
-The directives can be placed as element names, attributes, CSS class names, and inside comments.
-However, most directives are restricted to attribute only. Here are some equivalent examples of
-invoking `myDir`:
-
-- - -- -The following demonstrates the various ways a Directive (ngBind in this case) can be referenced from within a template: - -- -
-Hello {{username}}! -+All of the Angular-provided directives match attribute name, tag name, comments, or class name. +The following demonstrates the various ways a directive (`ngBind` in this case) can be referenced from within a template: +```html +