Update everything
This commit is contained in:
parent
bf368181a4
commit
72a485d6e8
319 changed files with 67958 additions and 13948 deletions
101
lib/angular/docs/partials/guide/bootstrap.html
Normal file
101
lib/angular/docs/partials/guide/bootstrap.html
Normal file
|
@ -0,0 +1,101 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>Overview</h2>
|
||||
|
||||
<p>This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.</p>
|
||||
|
||||
<h2>Angular <code><script></code> Tag</h2>
|
||||
|
||||
<p>This example shows the recommended path for integrating Angular with what we call automatic
|
||||
initialization.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng-app>
|
||||
<body>
|
||||
...
|
||||
<script src="angular.js">
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<ul>
|
||||
<li>Place the <code>script</code> tag at the bottom of the page. Placing script tags at the end of the page
|
||||
improves app load time because the HTML loading is not blocked by loading of the <code>angular.js</code>
|
||||
script. You can get the latest bits from <a href="http://code.angularjs.org">http://code.angularjs.org</a>. Please don't link
|
||||
your production code to this URL, as it will expose a security hole on your site. For
|
||||
experimental development linking to our site is fine.
|
||||
<ul><li>Choose: <code>angular-[version].js</code> for a human-readable file, suitable for development and
|
||||
debugging.</li>
|
||||
<li>Choose: <code>angular-[version].min.js</code> for a compressed and obfuscated file, suitable for use in
|
||||
production.</li></ul></li>
|
||||
<li><p>Place <code>ng-app</code> to the root of your application, typically on the <code><html></code> tag if you want
|
||||
angular to auto-bootstrap your application.</p>
|
||||
|
||||
<pre><code><html ng-app>
|
||||
</code></pre></li>
|
||||
<li><p>If you choose to use the old style directive syntax <code>ng:</code> then include xml-namespace in <code>html</code>
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
<code>ng:</code>.)</p>
|
||||
|
||||
<pre><code><html xmlns:ng="http://angularjs.org">
|
||||
</code></pre></li>
|
||||
</ul>
|
||||
|
||||
<h2>Automatic Initialization</h2>
|
||||
|
||||
<p>Angular initializes automatically upon <code>DOMContentLoaded</code> event, at which point Angular looks for
|
||||
the <a href="api/ng.directive:ngApp"><code><code>ng-app</code></code></a> directive which
|
||||
designates your application root. If the <a href="api/ng.directive:ngApp"><code><code>ng-app</code></code></a> directive is found then Angular
|
||||
will:</p>
|
||||
|
||||
<ul>
|
||||
<li>load the <a href="guide/module">module</a> associated with the directive.</li>
|
||||
<li>create the application <a href="api/AUTO.$injector"><code>injector</code></a></li>
|
||||
<li>compile the DOM treating the <a href="api/ng.directive:ngApp"><code><code>ng-app</code></code></a> directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
portion of the DOM as an Angular application.</li>
|
||||
</ul>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<!doctype html>
|
||||
<html ng-app="optionalModuleName">
|
||||
<body>
|
||||
I can add: {{ 1+2 }}.
|
||||
<script src="angular.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<h2>Manual Initialization</h2>
|
||||
|
||||
<p>If you need to have more control over the initialization process, you can use a manual
|
||||
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
|
||||
or the need to perform an operation before Angular compiles a page.</p>
|
||||
|
||||
<p>Here is an example of manually initializing Angular. The example is equivalent to using the <a href="api/ng.directive:ngApp"><code>ng-app</code></a> directive.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<p>This is the sequence that your code should follow:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>After the page and all of the code is loaded, find the root of the HTML template, which is
|
||||
typically the root of the document.</p></li>
|
||||
<li><p>Call <a href="api/angular.bootstrap"><code>api/angular.bootstrap</code></a> to <a href="guide/compiler">compile</a> the template into an
|
||||
executable, bi-directionally bound application.</p></li>
|
||||
</ol></div>
|
142
lib/angular/docs/partials/guide/compiler.html
Normal file
142
lib/angular/docs/partials/guide/compiler.html
Normal file
|
@ -0,0 +1,142 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>Overview</h2>
|
||||
|
||||
<p>Angular's <a href="api/ng.$compile"><code>HTML compiler</code></a> allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
|
||||
extensions <a href="api/ng.$compileProvider#directive"><code>directives</code></a>.</p>
|
||||
|
||||
<p>HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
browser how the window size needs to be divided in half so that center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add <code>align="center"</code> attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.</p>
|
||||
|
||||
<p>But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach browser new HTML syntax.</p>
|
||||
|
||||
<p>Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
expect that you will create directives that are specific to your app. These extension become a
|
||||
Domain Specific Language for building your application.</p>
|
||||
|
||||
<p>All of this compilation takes place in the web browser; no server side or pre-compilation step is
|
||||
involved.</p>
|
||||
|
||||
<h2>Compiler</h2>
|
||||
|
||||
<p>Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens into two phases.</p>
|
||||
|
||||
<ol>
|
||||
<li><p><strong>Compile:</strong> traverse the DOM and collect all of the directives. The result is a linking
|
||||
function.</p></li>
|
||||
<li><p><strong>Link:</strong> combine the directives with a scope and produce a live view. Any changes in the
|
||||
scope model are reflected in the view, and any user interactions with the view are reflected
|
||||
in the scope model. This makes the scope model the single source of truth.</p></li>
|
||||
</ol>
|
||||
|
||||
<p>Some directives such <a href="api/ng.directive:ngRepeat"><code><code>ng-repeat</code></code></a> clone DOM elements once for each item in collection. Having a compile and link phase
|
||||
improves performance since the cloned template only needs to be compiled once, and then linked
|
||||
once for each clone instance.</p>
|
||||
|
||||
<h2>Directive</h2>
|
||||
|
||||
<p>A directive is a behavior which should be triggered when specific HTML constructs are encountered in
|
||||
the compilation process. The directives can be placed in element names, attributes, class names, as
|
||||
well as comments. Here are some equivalent examples of invoking the <a href="api/ng.directive:ngBind"><code><code>ng-bind</code></code></a> directive.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<span ng-bind="exp"></span>
|
||||
<span class="ng-bind: exp;"></span>
|
||||
<ng-bind></ng-bind>
|
||||
<!-- directive: ng-bind exp -->
|
||||
</pre>
|
||||
|
||||
<p>A directive is just a function which executes when the compiler encounters it in the DOM. See <a href="api/ng.$compileProvider#directive"><code>directive API</code></a> for in-depth documentation on how
|
||||
to write directives.</p>
|
||||
|
||||
<p>Here is a directive which makes any element draggable. Notice the <code>draggable</code> attribute on the
|
||||
<code><span></code> element.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="drag" source-edit-deps="angular.js script.js" source-edit-html="index.html-16" source-edit-css="" source-edit-js="script.js-15" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-16" ng-html-wrap="drag angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-16">
|
||||
<span draggable>Drag ME</span>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-15"></pre>
|
||||
<script type="text/ng-template" id="script.js-15">
|
||||
angular.module('drag', []).
|
||||
directive('draggable', function($document) {
|
||||
var startX=0, startY=0, x = 0, y = 0;
|
||||
return function(scope, element, attr) {
|
||||
element.css({
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.bind('mousedown', function(event) {
|
||||
startX = event.screenX - x;
|
||||
startY = event.screenY - y;
|
||||
$document.bind('mousemove', mousemove);
|
||||
$document.bind('mouseup', mouseup);
|
||||
});
|
||||
|
||||
function mousemove(event) {
|
||||
y = event.screenY - startY;
|
||||
x = event.screenX - startX;
|
||||
element.css({
|
||||
top: y + 'px',
|
||||
left: x + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function mouseup() {
|
||||
$document.unbind('mousemove', mousemove);
|
||||
$document.unbind('mouseup', mouseup);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="drag" ng-set-html="index.html-16" ng-eval-javascript="script.js-15"></div>
|
||||
|
||||
<p>The presence of the <code>draggable</code> attribute on any element gives the element new behavior. The beauty of
|
||||
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
|
||||
what the browser understands in a way which is natural to anyone who is familiar with HTML
|
||||
principles.</p>
|
||||
|
||||
<h2>Understanding View</h2>
|
||||
|
||||
<p>There are many templating systems out there. Most of them consume a static string template and
|
||||
combine it with data, resulting in a new string. The resulting text is then <code>innerHTML</code>ed into
|
||||
an element.</p>
|
||||
|
||||
<p><img src="img/One_Way_Data_Binding.png"></p>
|
||||
|
||||
<p>This means that any changes to the data need to be re-merged with the template and then
|
||||
<code>innerHTML</code>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.</p>
|
||||
|
||||
<p>Angular is different. The Angular compiler consumes the DOM with directives, 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 <code>innerHTML</code> is used there are no issues of clobbering user input.
|
||||
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
|
||||
well.</p>
|
||||
|
||||
<p><img src="img/Two_Way_Data_Binding.png"></p>
|
||||
|
||||
<p>The Angular approach produces a stable DOM. This means that 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.</p></div>
|
523
lib/angular/docs/partials/guide/concepts.html
Normal file
523
lib/angular/docs/partials/guide/concepts.html
Normal file
|
@ -0,0 +1,523 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>Overview</h2>
|
||||
|
||||
<p>This document gives a quick overview of the main angular components and how they work together.
|
||||
These are:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/concepts#startup">startup</a> - bring up hello world</li>
|
||||
<li><a href="guide/concepts#runtime">runtime</a> - overview of angular runtime</li>
|
||||
<li><a href="guide/concepts#scope">scope</a> - the glue between the view and the controller</li>
|
||||
<li><a href="guide/concepts#controller">controller</a> - application behavior</li>
|
||||
<li><a href="guide/concepts#model">model</a> - your application data</li>
|
||||
<li><a href="guide/concepts#view">view</a> - what the user sees</li>
|
||||
<li><a href="guide/concepts#directives">directives</a> - extend HTML vocabulary</li>
|
||||
<li><a href="guide/concepts#filters">filters</a> - format the data in user locale</li>
|
||||
<li><a href="guide/concepts#injector">injector</a> - assembles your application</li>
|
||||
<li><a href="guide/concepts#module">module</a> - configures the injector</li>
|
||||
<li><a href="guide/concepts#angular_namespace"><code>$</code></a> - angular namespace</li>
|
||||
</ul>
|
||||
|
||||
<p><a name="startup"></a></p>
|
||||
|
||||
<h2>Startup</h2>
|
||||
|
||||
<p>This is how we get the ball rolling (refer to the diagram and example below):</p>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png"></p>
|
||||
|
||||
<ol>
|
||||
<li>The browser loads the HTML and parses it into a DOM</li>
|
||||
<li>The browser loads <code>angular.js</code> script</li>
|
||||
<li>Angular waits for <code>DOMContentLoaded</code> event</li>
|
||||
<li>Angular looks for <a href="api/ng.directive:ngApp"><code>ng-app</code></a>
|
||||
<a href="guide/directive">directive</a>, which designates the application boundary</li>
|
||||
<li>The <a href="guide/module">Module</a> specified in <a href="api/ng.directive:ngApp"><code>ng-app</code></a> (if any) is used to configure
|
||||
the <a href="api/AUTO.$injector"><code>$injector</code></a></li>
|
||||
<li>The <a href="api/AUTO.$injector"><code>$injector</code></a> is used to create the <a href="api/ng.$compile"><code>$compile</code></a> service as well as <a href="api/ng.$rootScope"><code>$rootScope</code></a></li>
|
||||
<li>The <a href="api/ng.$compile"><code>$compile</code></a> service is used to compile the DOM and link
|
||||
it with <a href="api/ng.$rootScope"><code>$rootScope</code></a></li>
|
||||
<li>The <a href="api/ng.directive:ngInit"><code>ng-init</code></a> <a href="guide/directive">directive</a> assigns <code>World</code> to the <code>name</code> property on the <a href="guide/scope">scope</a></li>
|
||||
<li>The <code>{{name}}</code> <a href="api/ng.$interpolate"><code>interpolates</code></a> the expression to
|
||||
<code>Hello World!</code></li>
|
||||
</ol>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js" source-edit-html="index.html-17" source-edit-css="" source-edit-js="" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-17" ng-html-wrap=" angular.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-17">
|
||||
<p ng-init=" name='World' ">Hello {{name}}!</p>
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-17" ng-eval-javascript=""></div>
|
||||
|
||||
<p><a name="runtime"></a></p>
|
||||
|
||||
<h2>Runtime</h2>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png"></p>
|
||||
|
||||
<p>The diagram and the example below describe how Angular interacts with the browser's event loop.</p>
|
||||
|
||||
<ol>
|
||||
<li>The browser's event-loop waits for an event to arrive. An event is a user interactions, timer event,
|
||||
or network event (response from a server).</li>
|
||||
<li>The event's callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.</li>
|
||||
<li>Once the callback executes, the browser leaves the JavaScript context and
|
||||
re-renders the view based on DOM changes.</li>
|
||||
</ol>
|
||||
|
||||
<p>Angular modifies the normal JavaScript flow by providing its own event processing loop. This
|
||||
splits the JavaScript into classical and Angular execution context. Only operations which are
|
||||
applied in Angular execution context will benefit from Angular data-binding, exception handling,
|
||||
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) $apply has already been called for you by the
|
||||
directive which is handling the event. An explicit call to $apply is needed only when
|
||||
implementing custom event callbacks, or when working with a third-party library callbacks.</p>
|
||||
|
||||
<ol>
|
||||
<li>Enter Angular execution context by calling <a href="guide/scope">scope</a><code>.</code><a href="api/ng.$rootScope.Scope#$apply"><code>$apply</code></a><code>(stimulusFn)</code>. Where <code>stimulusFn</code> is
|
||||
the work you wish to do in Angular execution context.</li>
|
||||
<li>Angular executes the <code>stimulusFn()</code>, which typically modifies application state.</li>
|
||||
<li>Angular enters the <a href="api/ng.$rootScope.Scope#$digest"><code>$digest</code></a> loop. The
|
||||
loop is made up of two smaller loops which process <a href="api/ng.$rootScope.Scope#$evalAsync"><code>$evalAsync</code></a> queue and the <a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a> list. The <a href="api/ng.$rootScope.Scope#$digest"><code>$digest</code></a> loop keeps iterating until the model
|
||||
stabilizes, which means that the <a href="api/ng.$rootScope.Scope#$evalAsync"><code>$evalAsync</code></a> queue is empty and the <a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a> list does not detect any changes.</li>
|
||||
<li>The <a href="api/ng.$rootScope.Scope#$evalAsync"><code>$evalAsync</code></a> queue is used to
|
||||
schedule work which needs to occur outside of current stack frame, but before the browser's
|
||||
view render. This is usually done with <code>setTimeout(0)</code>, but the <code>setTimeout(0)</code> approach
|
||||
suffers from slowness and may cause view flickering since the browser renders the view after
|
||||
each event.</li>
|
||||
<li>The <a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a> list is a set of expressions
|
||||
which may have changed since last iteration. If a change is detected then the <code>$watch</code>
|
||||
function is called which typically updates the DOM with the new value.</li>
|
||||
<li>Once the Angular <a href="api/ng.$rootScope.Scope#$digest"><code>$digest</code></a> loop finishes
|
||||
the execution leaves the Angular and JavaScript context. This is followed by the browser
|
||||
re-rendering the DOM to reflect any changes.</li>
|
||||
</ol>
|
||||
|
||||
<p>Here is the explanation of how the <code>Hello wold</code> example achieves the data-binding effect when the
|
||||
user enters text into the text field.</p>
|
||||
|
||||
<ol>
|
||||
<li>During the compilation phase:
|
||||
<ol><li>the <a href="api/ng.directive:ngModel"><code>ng-model</code></a> and <a href="api/ng.directive:input"><code>input</code></a> <a href="guide/directive">directive</a> set up a <code>keydown</code> listener on the <code><input></code> control.</li>
|
||||
<li>the <a href="api/ng.$interpolate"><code>{{name}}</code></a> interpolation
|
||||
sets up a <a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a> to be notified of
|
||||
<code>name</code> changes.</li></ol></li>
|
||||
<li>During the runtime phase:
|
||||
<ol><li>Pressing an '<code>X</code>' key causes the browser to emit a <code>keydown</code> event on the input control.</li>
|
||||
<li>The <a href="api/ng.directive:input"><code>input</code></a> directive
|
||||
captures the change to the input's value and calls <a href="api/ng.$rootScope.Scope#$apply"><code>$apply</code></a><code>("name = 'X';")</code> to update the
|
||||
application model inside the Angular execution context.</li>
|
||||
<li>Angular applies the <code>name = 'X';</code> to the model.</li>
|
||||
<li>The <a href="api/ng.$rootScope.Scope#$digest"><code>$digest</code></a> loop begins</li>
|
||||
<li>The <a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a> list detects a change
|
||||
on the <code>name</code> property and notifies the <a href="api/ng.$interpolate"><code>{{name}}</code></a> interpolation, which in turn updates the DOM.</li>
|
||||
<li>Angular exits the execution context, which in turn exits the <code>keydown</code> event and with it
|
||||
the JavaScript execution context.</li>
|
||||
<li>The browser re-renders the view with update text.</li></ol></li>
|
||||
</ol>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js" source-edit-html="index.html-18" source-edit-css="" source-edit-js="" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-18" ng-html-wrap=" angular.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-18">
|
||||
<input ng-model="name">
|
||||
<p>Hello {{name}}!</p>
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-18" ng-eval-javascript=""></div>
|
||||
|
||||
<p><a name="scope"></a></p>
|
||||
|
||||
<h2>Scope</h2>
|
||||
|
||||
<p>The <a href="guide/scope">scope</a> is responsible for detecting changes to the model section and
|
||||
provides the execution context for expressions. The scopes are nested in a hierarchical structure
|
||||
which closely follow the DOM structure. (See individual directive documentation to see which
|
||||
directives cause a creation of new scopes.)</p>
|
||||
|
||||
<p>The following example demonstrates how <code>name</code> <a href="guide/expression">expression</a> will evaluate
|
||||
into different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.</p>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<div class="show-scope">
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-19" source-edit-css="style.css-21" source-edit-js="script.js-20" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-19" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-19">
|
||||
<div ng-controller="GreetCtrl">
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
<div ng-controller="ListCtrl">
|
||||
<ol>
|
||||
<li ng-repeat="name in names">{{name}}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="style.css">
|
||||
<pre class="prettyprint linenums" ng-set-text="style.css-21"></pre>
|
||||
<style type="text/css" id="style.css-21">
|
||||
.show-scope .doc-example-live.ng-scope,
|
||||
.show-scope .doc-example-live .ng-scope {
|
||||
border: 1px solid red;
|
||||
margin: 3px;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-20"></pre>
|
||||
<script type="text/ng-template" id="script.js-20">
|
||||
function GreetCtrl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
|
||||
function ListCtrl($scope) {
|
||||
$scope.names = ['Igor', 'Misko', 'Vojta'];
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-19" ng-eval-javascript="script.js-20"></div>
|
||||
</div>
|
||||
|
||||
<p><img class="center" src="img/guide/concepts-scope.png"></p>
|
||||
|
||||
<p><a name="controller"></a></p>
|
||||
|
||||
<h2>Controller</h2>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-controller.png"></p>
|
||||
|
||||
<p>A controller is the code behind the view. Its job is to construct the model and publish it to the
|
||||
view along with callback methods. The view is a projection of the scope onto the template (the
|
||||
HTML). The scope is the glue which marshals the model to the view and forwards the events to the
|
||||
controller.</p>
|
||||
|
||||
<p>The separation of the controller and the view is important because:</p>
|
||||
|
||||
<ul>
|
||||
<li>The controller is written in JavaScript. JavaScript is imperative. Imperative is a good fit
|
||||
for specifying application behavior. The controller should not contain any rendering
|
||||
information (DOM references or HTML fragments).</li>
|
||||
<li>The view template is written in HTML. HTML is declarative. Declarative is a good fit for
|
||||
specifying UI. The View should not contain any behavior.</li>
|
||||
<li>Since the controller is unaware of the view, there could be many views for the same
|
||||
controller. This is important for re-skinning, device specific views (i.e. mobile vs desktop),
|
||||
and testability.</li>
|
||||
</ul>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-22" source-edit-css="" source-edit-js="script.js-23" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-22" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-22">
|
||||
<div ng-controller="MyCtrl">
|
||||
Hello {{name}}!
|
||||
<button ng-click="action()">
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-23"></pre>
|
||||
<script type="text/ng-template" id="script.js-23">
|
||||
function MyCtrl($scope) {
|
||||
$scope.action = function() {
|
||||
$scope.name = 'OK';
|
||||
}
|
||||
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-22" ng-eval-javascript="script.js-23"></div>
|
||||
|
||||
<p><a name="model"></a></p>
|
||||
|
||||
<h2>Model</h2>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png"></p>
|
||||
|
||||
<p>The model is the data which is used merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.</p>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<p><a name="view"></a></p>
|
||||
|
||||
<h2>View</h2>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png"></p>
|
||||
|
||||
<p>The view is what the users sees. The view begins its life as a template, it is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view, compared to most other templating systems.</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Others</strong> - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
edited by an HTML editor. The template string is then parsed by the template engine, and
|
||||
merged with the data. The result of the merge is an HTML string. The HTML string is then
|
||||
written to the browser using the <code>.innerHTML</code>, which causes the browser to render the HTML.
|
||||
When the model changes the whole process needs to be repeated. The granularity of the template
|
||||
is the granularity of the DOM updates. The key here is that the templating system manipulates
|
||||
strings.</li>
|
||||
<li><strong>Angular</strong> - Angular is different, since its templating system works on DOM objects not on
|
||||
strings. The template is still written in an HTML string, but it is HTML (not HTML with
|
||||
template sprinkled in.) The browser parses the HTML into the DOM, and the DOM becomes the input to
|
||||
the template engine known as the <a href="api/ng.$compile"><code>compiler</code></a>. The compiler
|
||||
looks for <a href="guide/directive">directives</a> which in turn set up <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a> on the model. The result is a
|
||||
continuously updating view which does not need template model re-merging. Your model becomes
|
||||
the single source-of-truth for your view.</li>
|
||||
</ul>
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js" source-edit-html="index.html-24" source-edit-css="" source-edit-js="" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-24" ng-html-wrap=" angular.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-24">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<pre>list={{list}}</pre> <br>
|
||||
<ol>
|
||||
<li ng-repeat="item in list">
|
||||
{{item}}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-24" ng-eval-javascript=""></div>
|
||||
|
||||
<p><a name="directives"></a></p>
|
||||
|
||||
<h2>Directives</h2>
|
||||
|
||||
<p>A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute,
|
||||
element name, or a class name. A directive allows you to extend the HTML vocabulary in a
|
||||
declarative fashion. Following is an example which enables data-binding for the <code>contenteditable</code>
|
||||
in HTML.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="directive" source-edit-deps="angular.js script.js" source-edit-html="index.html-26" source-edit-css="style.css-27" source-edit-js="script.js-25" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-26" ng-html-wrap="directive angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-26">
|
||||
<div contentEditable="true" ng-model="content">Edit Me</div>
|
||||
<pre>model = {{content}}</pre>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="style.css">
|
||||
<pre class="prettyprint linenums" ng-set-text="style.css-27"></pre>
|
||||
<style type="text/css" id="style.css-27">
|
||||
div[contentEditable] {
|
||||
cursor: pointer;
|
||||
background-color: #D0D0D0;
|
||||
margin-bottom: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-25"></pre>
|
||||
<script type="text/ng-template" id="script.js-25">
|
||||
angular.module('directive', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.bind('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
});
|
||||
|
||||
// model -> view
|
||||
ctrl.$render = function(value) {
|
||||
elm.html(value);
|
||||
};
|
||||
|
||||
// load init value from DOM
|
||||
ctrl.$setViewValue(elm.html());
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="directive" ng-set-html="index.html-26" ng-eval-javascript="script.js-25"></div>
|
||||
|
||||
<p><a name="filters"></a></p>
|
||||
|
||||
<h2>Filters</h2>
|
||||
|
||||
<p><a href="api/ng.$filter"><code>Filters</code></a> perform data transformation. Typically
|
||||
they are used in conjunction with the locale to format the data in locale specific output.
|
||||
They follow the spirit of UNIX filters and use similar syntax <code>|</code> (pipe).</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js" source-edit-html="index.html-28" source-edit-css="" source-edit-js="" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-28" ng-html-wrap=" angular.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-28">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
Number formatting: {{ 1234567890 | number }} <br>
|
||||
array filtering <input ng-model="predicate">
|
||||
{{ list | filter:predicate | json }}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-28" ng-eval-javascript=""></div>
|
||||
|
||||
<p><a name="module"></a>
|
||||
<a name="injector"></a></p>
|
||||
|
||||
<h2>Modules and the Injector</h2>
|
||||
|
||||
<p><img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png"></p>
|
||||
|
||||
<p>The <a href="api/AUTO.$injector"><code>injector</code></a> is a service locator. There is a single
|
||||
<a href="api/AUTO.$injector"><code>injector</code></a> per Angular <a href="api/ng.directive:ngApp"><code>application</code></a>. The <a href="api/AUTO.$injector"><code>injector</code></a> provides a way to look up an object instance by its
|
||||
name. The injector keeps an internal cache of all objects so that repeated calls to get the same
|
||||
object name result in the same instance. If the object does not exist, then the <a href="api/AUTO.$injector"><code>injector</code></a> asks the instance factory to create a new instance.</p>
|
||||
|
||||
<p>A <a href="api/angular.Module"><code>module</code></a> is a way to configure the injector's instance factory, known
|
||||
as a <a href="api/AUTO.$provide"><code>provider</code></a>.</p>
|
||||
|
||||
<div class='clear'>
|
||||
</div>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
// Create a module
|
||||
var myModule = angular.module('myModule', [])
|
||||
|
||||
// Configure the injector
|
||||
myModule.factory('serviceA', function() {
|
||||
return {
|
||||
// instead of {}, put your object creation here
|
||||
};
|
||||
});
|
||||
|
||||
// create an injector and configure it from 'myModule'
|
||||
var $injector = angular.injector(['myModule']);
|
||||
|
||||
// retrieve an object from the injector by name
|
||||
var serviceA = $injector.get('serviceA');
|
||||
|
||||
// always true because of instance cache
|
||||
$injector.get('serviceA') === $injector.get('serviceA');
|
||||
</pre>
|
||||
|
||||
<p>But the real magic of the <a href="api/AUTO.$injector"><code>injector</code></a> is that it can be
|
||||
used to <a href="api/AUTO.$injector#invoke"><code>call</code></a> methods and <a href="api/AUTO.$injector#instantiate"><code>instantiate</code></a> types. This subtle feature is what
|
||||
allows the methods and types to ask for their dependencies instead of having to look for them.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
// You write functions such as this one.
|
||||
function doSomething(serviceA, serviceB) {
|
||||
// do something here.
|
||||
}
|
||||
|
||||
// Angular provides the injector for your application
|
||||
var $injector = ...;
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the old-school way of getting dependencies.
|
||||
var serviceA = $injector.get('serviceA');
|
||||
var serviceB = $injector.get('serviceB');
|
||||
|
||||
// now call the function
|
||||
doSomething(serviceA, serviceB);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the cool way of getting dependencies.
|
||||
// the $injector will supply the arguments to the function automatically
|
||||
$injector.invoke(doSomething); // This is how the framework calls your functions
|
||||
</pre>
|
||||
|
||||
<p>Notice that the only thing you needed to write was the function, and list the dependencies in the
|
||||
function arguments. When angular calls the function, it will use the <a href="api/AUTO.$injector#invoke"><code>call</code></a> which will automatically fill the function
|
||||
arguments.</p>
|
||||
|
||||
<p>Examine the <code>ClockCtrl</code> bellow, and notice how it lists the dependencies in the constructor. When the
|
||||
<a href="api/ng.directive:ngController"><code>ng-controller</code></a> instantiates
|
||||
the controller it automatically provides the dependencies. There is no need to create
|
||||
dependencies, look for dependencies, or even get a reference to the injector.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="timeExampleModule" source-edit-deps="angular.js script.js" source-edit-html="index.html-29" source-edit-css="" source-edit-js="script.js-30" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-29" ng-html-wrap="timeExampleModule angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-29">
|
||||
<div ng-controller="ClockCtrl">
|
||||
Current time is: {{ time.now }}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-30"></pre>
|
||||
<script type="text/ng-template" id="script.js-30">
|
||||
angular.module('timeExampleModule', []).
|
||||
// Declare new object called time,
|
||||
// which will be available for injection
|
||||
factory('time', function($timeout) {
|
||||
var time = {};
|
||||
|
||||
(function tick() {
|
||||
time.now = new Date().toString();
|
||||
$timeout(tick, 1000);
|
||||
})();
|
||||
return time;
|
||||
});
|
||||
|
||||
// Notice that you can simply ask for time
|
||||
// and it will be provided. No need to look for it.
|
||||
function ClockCtrl($scope, time) {
|
||||
$scope.time = time;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="timeExampleModule" ng-set-html="index.html-29" ng-eval-javascript="script.js-30"></div>
|
||||
|
||||
<p><a name="angular_namespace"></a></p>
|
||||
|
||||
<h2>Angular Namespace</h2>
|
||||
|
||||
<p>To prevent accidental name collision, Angular prefixes names of objects which could potentially
|
||||
collide with <code>$</code>. Please do not use the <code>$</code> prefix in your code as it may accidentally collide
|
||||
with Angular code.</p></div>
|
211
lib/angular/docs/partials/guide/dev_guide.e2e-testing.html
Normal file
211
lib/angular/docs/partials/guide/dev_guide.e2e-testing.html
Normal file
|
@ -0,0 +1,211 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
verify the correctness of new features, catch bugs and notice regressions.</p>
|
||||
|
||||
<p>To solve this problem, we have built an Angular Scenario Runner which simulates user interactions
|
||||
that will help you verify the health of your Angular application.</p>
|
||||
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>You will write scenario tests in JavaScript, which describe how your application should behave,
|
||||
given a certain interaction in a specific state. A scenario is comprised of one or more <code>it</code> blocks
|
||||
(you can think of these as the requirements of your application), which in turn are made of
|
||||
<strong>commands</strong> and <strong>expectations</strong>. Commands tell the Runner to do something with the application
|
||||
(such as navigate to a page or click on a button), and expectations tell the Runner to assert
|
||||
something about the state (such as the value of a field or the current URL). If any expectation
|
||||
fails, the runner marks the <code>it</code> as "failed" and continues on to the next one. Scenarios may also
|
||||
have <strong>beforeEach</strong> and <strong>afterEach</strong> blocks, which will be run before (or after) each <code>it</code> block,
|
||||
regardless of whether they pass or fail.</p>
|
||||
|
||||
<p><img src="img/guide/scenario_runner.png"></p>
|
||||
|
||||
<p>In addition to the above elements, scenarios may also contain helper functions to avoid duplicating
|
||||
code in the <code>it</code> blocks.</p>
|
||||
|
||||
<p>Here is an example of a simple scenario:
|
||||
<pre class="prettyprint linenums">
|
||||
describe('Buzz Client', function() {
|
||||
it('should filter results', function() {
|
||||
input('user').enter('jacksparrow');
|
||||
element(':button').click();
|
||||
expect(repeater('ul li').count()).toEqual(10);
|
||||
input('filterText').enter('Bees');
|
||||
expect(repeater('ul li').count()).toEqual(1);
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
|
||||
filter the stream of the user. It starts by entering a value in the 'user' input field, clicking
|
||||
the only button on the page, and then it verifies that there are 10 items listed. It then enters
|
||||
'Bees' in the 'filterText' input field and verifies that the list is reduced to a single item.</p>
|
||||
|
||||
<p>The API section below lists the available commands and expectations for the Runner.</p>
|
||||
|
||||
<h2>API</h2>
|
||||
|
||||
<p>Source: <a href="https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js">https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js</a></p>
|
||||
|
||||
<h3>pause()</h3>
|
||||
|
||||
<p>Pauses the execution of the tests until you call <code>resume()</code> in the console (or click the resume
|
||||
link in the Runner UI).</p>
|
||||
|
||||
<h3>sleep(seconds)</h3>
|
||||
|
||||
<p>Pauses the execution of the tests for the specified number of <code>seconds</code>.</p>
|
||||
|
||||
<h3>browser().navigateTo(url)</h3>
|
||||
|
||||
<p>Loads the <code>url</code> into the test frame.</p>
|
||||
|
||||
<h3>browser().navigateTo(url, fn)</h3>
|
||||
|
||||
<p>Loads the URL returned by <code>fn</code> into the testing frame. The given <code>url</code> is only used for the test
|
||||
output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
|
||||
write the test).</p>
|
||||
|
||||
<h3>browser().reload()</h3>
|
||||
|
||||
<p>Refreshes the currently loaded page in the test frame.</p>
|
||||
|
||||
<h3>browser().window().href()</h3>
|
||||
|
||||
<p>Returns the window.location.href of the currently loaded page in the test frame.</p>
|
||||
|
||||
<h3>browser().window().path()</h3>
|
||||
|
||||
<p>Returns the window.location.pathname of the currently loaded page in the test frame.</p>
|
||||
|
||||
<h3>browser().window().search()</h3>
|
||||
|
||||
<p>Returns the window.location.search of the currently loaded page in the test frame.</p>
|
||||
|
||||
<h3>browser().window().hash()</h3>
|
||||
|
||||
<p>Returns the window.location.hash (without <code>#</code>) of the currently loaded page in the test frame.</p>
|
||||
|
||||
<h3>browser().location().url()</h3>
|
||||
|
||||
<p>Returns the <a href="api/ng.$location"><code>$location.url()</code></a> of the currently loaded page in
|
||||
the test frame.</p>
|
||||
|
||||
<h3>browser().location().path()</h3>
|
||||
|
||||
<p>Returns the <a href="api/ng.$location"><code>$location.path()</code></a> of the currently loaded page in
|
||||
the test frame.</p>
|
||||
|
||||
<h3>browser().location().search()</h3>
|
||||
|
||||
<p>Returns the <a href="api/ng.$location"><code>$location.search()</code></a> of the currently loaded page
|
||||
in the test frame.</p>
|
||||
|
||||
<h3>browser().location().hash()</h3>
|
||||
|
||||
<p>Returns the <a href="api/ng.$location"><code>$location.hash()</code></a> of the currently loaded page in
|
||||
the test frame.</p>
|
||||
|
||||
<h3>expect(future).{matcher}</h3>
|
||||
|
||||
<p>Asserts the value of the given <code>future</code> satisfies the <code>matcher</code>. All API statements return a
|
||||
<code>future</code> object, which get a <code>value</code> assigned after they are executed. Matchers are defined using
|
||||
<code>angular.scenario.matcher</code>, and they use the value of futures to run the expectation. For example:
|
||||
<code>expect(browser().location().href()).toEqual('http://www.google.com')</code></p>
|
||||
|
||||
<h3>expect(future).not().{matcher}</h3>
|
||||
|
||||
<p>Asserts the value of the given <code>future</code> satisfies the negation of the <code>matcher</code>.</p>
|
||||
|
||||
<h3>using(selector, label)</h3>
|
||||
|
||||
<p>Scopes the next DSL element selection.</p>
|
||||
|
||||
<h3>binding(name)</h3>
|
||||
|
||||
<p>Returns the value of the first binding matching the given <code>name</code>.</p>
|
||||
|
||||
<h3>input(name).enter(value)</h3>
|
||||
|
||||
<p>Enters the given <code>value</code> in the text field with the given <code>name</code>.</p>
|
||||
|
||||
<h3>input(name).check()</h3>
|
||||
|
||||
<p>Checks/unchecks the checkbox with the given <code>name</code>.</p>
|
||||
|
||||
<h3>input(name).select(value)</h3>
|
||||
|
||||
<p>Selects the given <code>value</code> in the radio button with the given <code>name</code>.</p>
|
||||
|
||||
<h3>input(name).val()</h3>
|
||||
|
||||
<p>Returns the current value of an input field with the given <code>name</code>.</p>
|
||||
|
||||
<h3>repeater(selector, label).count()</h3>
|
||||
|
||||
<p>Returns the number of rows in the repeater matching the given jQuery <code>selector</code>. The <code>label</code> is
|
||||
used for test output.</p>
|
||||
|
||||
<h3>repeater(selector, label).row(index)</h3>
|
||||
|
||||
<p>Returns an array with the bindings in the row at the given <code>index</code> in the repeater matching the
|
||||
given jQuery <code>selector</code>. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>repeater(selector, label).column(binding)</h3>
|
||||
|
||||
<p>Returns an array with the values in the column with the given <code>binding</code> in the repeater matching
|
||||
the given jQuery <code>selector</code>. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>select(name).option(value)</h3>
|
||||
|
||||
<p>Picks the option with the given <code>value</code> on the select with the given <code>name</code>.</p>
|
||||
|
||||
<h3>select(name).option(value1, value2...)</h3>
|
||||
|
||||
<p>Picks the options with the given <code>values</code> on the multi select with the given <code>name</code>.</p>
|
||||
|
||||
<h3>element(selector, label).count()</h3>
|
||||
|
||||
<p>Returns the number of elements that match the given jQuery <code>selector</code>. The <code>label</code> is used for test
|
||||
output.</p>
|
||||
|
||||
<h3>element(selector, label).click()</h3>
|
||||
|
||||
<p>Clicks on the element matching the given jQuery <code>selector</code>. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>element(selector, label).query(fn)</h3>
|
||||
|
||||
<p>Executes the function <code>fn(selectedElements, done)</code>, where selectedElements are the elements that
|
||||
match the given jQuery <code>selector</code> and <code>done</code> is a function that is called at the end of the <code>fn</code>
|
||||
function. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>element(selector, label).{method}()</h3>
|
||||
|
||||
<p>Returns the result of calling <code>method</code> on the element matching the given jQuery <code>selector</code>, where
|
||||
<code>method</code> can be any of the following jQuery methods: <code>val</code>, <code>text</code>, <code>html</code>, <code>height</code>,
|
||||
<code>innerHeight</code>, <code>outerHeight</code>, <code>width</code>, <code>innerWidth</code>, <code>outerWidth</code>, <code>position</code>, <code>scrollLeft</code>,
|
||||
<code>scrollTop</code>, <code>offset</code>. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>element(selector, label).{method}(value)</h3>
|
||||
|
||||
<p>Executes the <code>method</code> passing in <code>value</code> on the element matching the given jQuery <code>selector</code>, where
|
||||
<code>method</code> can be any of the following jQuery methods: <code>val</code>, <code>text</code>, <code>html</code>, <code>height</code>,
|
||||
<code>innerHeight</code>, <code>outerHeight</code>, <code>width</code>, <code>innerWidth</code>, <code>outerWidth</code>, <code>position</code>, <code>scrollLeft</code>,
|
||||
<code>scrollTop</code>, <code>offset</code>. The <code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>element(selector, label).{method}(key)</h3>
|
||||
|
||||
<p>Returns the result of calling <code>method</code> passing in <code>key</code> on the element matching the given jQuery
|
||||
<code>selector</code>, where <code>method</code> can be any of the following jQuery methods: <code>attr</code>, <code>prop</code>, <code>css</code>. The
|
||||
<code>label</code> is used for test output.</p>
|
||||
|
||||
<h3>element(selector, label).{method}(key, value)</h3>
|
||||
|
||||
<p>Executes the <code>method</code> passing in <code>key</code> and <code>value</code> on the element matching the given jQuery
|
||||
<code>selector</code>, where <code>method</code> can be any of the following jQuery methods: <code>attr</code>, <code>prop</code>, <code>css</code>. The
|
||||
<code>label</code> is used for test output.</p>
|
||||
|
||||
<p>JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not testing.</p></div>
|
25
lib/angular/docs/partials/guide/dev_guide.mvc.html
Normal file
25
lib/angular/docs/partials/guide/dev_guide.mvc.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>While Model-View-Controller (MVC) has acquired different shades of meaning over the years since it
|
||||
first appeared, Angular incorporates the basic principles behind the original <a href="http://en.wikipedia.org/wiki/Model–view–controller">MVC</a> software design pattern into its way of
|
||||
building client-side web applications.</p>
|
||||
|
||||
<p>The MVC pattern summarized:</p>
|
||||
|
||||
<ul>
|
||||
<li>Separate applications into distinct presentation, data, and logic components</li>
|
||||
<li>Encourage loose coupling between these components</li>
|
||||
</ul>
|
||||
|
||||
<p>Along with <a href="guide/dev_guide.services">services</a> and <a href="guide/di">dependency injection</a>, MVC
|
||||
makes angular applications better structured, easier to maintain and more testable.</p>
|
||||
|
||||
<p>The following topics explain how angular incorporates the MVC pattern into the angular way of
|
||||
developing web applications:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_model">Understanding the Model Component</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_controller">Understanding the Controller Component</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_view">Understanding the View Component</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,289 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular <a href="guide/scope">Scope</a>, excluding the root scope. When you or Angular create a new
|
||||
child scope object via the <a href="api/ng.$rootScope.Scope#$new"><code>scope.$new</code></a> API , there is an
|
||||
option to pass in a controller as a method argument. This will tell Angular to associate the
|
||||
controller with the new scope and to augment its behavior.</p>
|
||||
|
||||
<p>Use controllers to:</p>
|
||||
|
||||
<ul>
|
||||
<li>Set up the initial state of a scope object.</li>
|
||||
<li>Add behavior to the scope object.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Setting up the initial state of a scope object</h2>
|
||||
|
||||
<p>Typically, when you create an application you need to set up an initial state for an Angular scope.</p>
|
||||
|
||||
<p>Angular applies (in the sense of JavaScript's <code>Function#apply</code>) the controller constructor function
|
||||
to a new Angular scope object, which sets up an initial scope state. This means that Angular never
|
||||
creates instances of the controller type (by invoking the <code>new</code> operator on the controller
|
||||
constructor). Constructors are always applied to an existing scope object.</p>
|
||||
|
||||
<p>You set up the initial state of a scope by creating model properties. For example:</p>
|
||||
|
||||
<p>function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}</p>
|
||||
|
||||
<p>The <code>GreetingCtrl</code> controller creates a <code>greeting</code> model which can be referred to in a template.</p>
|
||||
|
||||
<p><strong>NOTE</strong>: Many of the examples in the documentation show the creation of functions
|
||||
in the global scope. This is only for demonstration purposes - in a real
|
||||
application you should use the <code>.controller</code> method of your Angular module for
|
||||
your application as follows:</p>
|
||||
|
||||
<p>var myApp = angular.module('myApp',[]);</p>
|
||||
|
||||
<p>myApp.controller('GreetingCtrl', ['$scope', function(scope) {
|
||||
scope.greeting = 'Hola!';
|
||||
}]);</p>
|
||||
|
||||
<p>Note also that we use the array notation to explicitly specify the dependency
|
||||
of the controller on the <code>$scope</code> service provided by Angular.</p>
|
||||
|
||||
<h2>Adding Behavior to a Scope Object</h2>
|
||||
|
||||
<p>Behavior on an Angular scope object is in the form of scope method properties available to the
|
||||
template/view. This behavior interacts with and modifies the application model.</p>
|
||||
|
||||
<p>As discussed in the <a href="guide/dev_guide.mvc.understanding_model">Model</a> section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any functions assigned to
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and <code>ng</code> event handler directives (e.g. <a href="api/ng.directive:ngClick"><code>ngClick</code></a>).</p>
|
||||
|
||||
<h2>Using Controllers Correctly</h2>
|
||||
|
||||
<p>In general, a controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.</p>
|
||||
|
||||
<p>The most common way to keep controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in controllers via dependency injection.
|
||||
This is discussed in the <a href="guide/di">Dependency Injection</a> <a href="guide/dev_guide.services">Services</a> sections of this guide.</p>
|
||||
|
||||
<p>Do not use controllers for:</p>
|
||||
|
||||
<ul>
|
||||
<li>Any kind of DOM manipulation — Controllers should contain only business logic. DOM
|
||||
manipulation—the presentation logic of an application—is well known for being hard to test.
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
logic. Angular offers <a href="guide/dev_guide.templates.databinding">databinding</a> for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
<a href="guide/directive">directives</a>.</li>
|
||||
<li>Input formatting — Use <a href="guide/forms">angular form controls</a> instead.</li>
|
||||
<li>Output filtering — Use <a href="guide/dev_guide.templates.filters">angular filters</a> instead.</li>
|
||||
<li>To run stateless or stateful code shared across controllers — Use <a href="guide/dev_guide.services">angular services</a> instead.</li>
|
||||
<li>To instantiate or manage the life-cycle of other components (for example, to create service
|
||||
instances).</li>
|
||||
</ul>
|
||||
|
||||
<h2>Associating Controllers with Angular Scope Objects</h2>
|
||||
|
||||
<p>You can associate controllers with scope objects explicitly via the <a href="api/ng.$rootScope.Scope#$new"><code>scope.$new</code></a> api or implicitly via the <a href="api/ng.directive:ngController"><code>ngController directive</code></a> or <a href="api/ng.$route"><code>$route service</code></a>.</p>
|
||||
|
||||
<h3>Controller Constructor and Methods Example</h3>
|
||||
|
||||
<p>To illustrate how the controller component works in angular, let's create a little app with the
|
||||
following components:</p>
|
||||
|
||||
<ul>
|
||||
<li>A <a href="guide/dev_guide.templates">template</a> with two buttons and a simple message</li>
|
||||
<li>A model consisting of a string named <code>spice</code></li>
|
||||
<li>A controller with two functions that set the value of <code>spice</code></li>
|
||||
</ul>
|
||||
|
||||
<p>The message in our template contains a binding to the <code>spice</code> model, which by default is set to the
|
||||
string "very". Depending on which button is clicked, the <code>spice</code> model is set to <code>chili</code> or
|
||||
<code>jalapeño</code>, and the message is automatically updated by data-binding.</p>
|
||||
|
||||
<h3>A Spicy Controller Example</h3>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
}
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</pre>
|
||||
|
||||
<p>Things to notice in the example above:</p>
|
||||
|
||||
<ul>
|
||||
<li>The <code>ngController</code> directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the <code>SpicyCtrl</code> controller.</li>
|
||||
<li><code>SpicyCtrl</code> is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".</li>
|
||||
<li>Assigning a property to <code>$scope</code> creates or updates the model.</li>
|
||||
<li>Controller methods can be created through direct assignment to scope (the <code>chiliSpicy</code> method)</li>
|
||||
<li>Both controller methods are available in the template (for the <code>body</code> element and and its
|
||||
children).</li>
|
||||
<li>NB: Previous versions of Angular (pre 1.0 RC) allowed you to use <code>this</code> interchangeably with
|
||||
the $scope method, but this is no longer the case. Inside of methods defined on the scope
|
||||
<code>this</code> and $scope are interchangeable (angular sets <code>this</code> to $scope), but not otherwise
|
||||
inside your controller constructor.</li>
|
||||
<li>NB: Previous versions of Angular (pre 1.0 RC) added prototype methods into the scope
|
||||
automatically, but this is no longer the case; all methods need to be added manually to
|
||||
the scope.</li>
|
||||
</ul>
|
||||
|
||||
<p>Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.</p>
|
||||
|
||||
<h3>Controller Method Arguments Example</h3>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice" value="wasabi">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.spicy = function(spice) {
|
||||
$scope.spice = spice;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Notice that the <code>SpicyCtrl</code> controller now defines just one method called <code>spicy</code>, which takes one
|
||||
argument called <code>spice</code>. The template then refers to this controller method and passes in a string
|
||||
constant <code>'chili'</code> in the binding for the first button and a model property <code>spice</code> (bound to an
|
||||
input box) in the second button.</p>
|
||||
|
||||
<h3>Controller Inheritance Example</h3>
|
||||
|
||||
<p>Controller inheritance in Angular is based on <a href="api/ng.$rootScope.Scope"><code>Scope</code></a> inheritance. Let's
|
||||
have a look at an example:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</body>
|
||||
|
||||
function MainCtrl($scope) {
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}
|
||||
|
||||
function ChildCtrl($scope) {
|
||||
$scope.name = 'Mattie';
|
||||
}
|
||||
|
||||
function BabyCtrl($scope) {
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Notice how we nested three <code>ngController</code> directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:</p>
|
||||
|
||||
<ul>
|
||||
<li>The root scope</li>
|
||||
<li>The <code>MainCtrl</code> scope, which contains <code>timeOfDay</code> and <code>name</code> models</li>
|
||||
<li>The <code>ChildCtrl</code> scope, which shadows the <code>name</code> model from the previous scope and inherits the
|
||||
<code>timeOfDay</code> model</li>
|
||||
<li>The <code>BabyCtrl</code> scope, which shadows both the <code>timeOfDay</code> model defined in <code>MainCtrl</code> and <code>name</code>
|
||||
model defined in the ChildCtrl</li>
|
||||
</ul>
|
||||
|
||||
<p>Inheritance works between controllers in the same way as it does with models. So in our previous
|
||||
examples, all of the models could be replaced with controller methods that return string values.</p>
|
||||
|
||||
<p>Note: Standard prototypical inheritance between two controllers doesn't work as one might expect,
|
||||
because as we mentioned earlier, controllers are not instantiated directly by Angular, but rather
|
||||
are applied to the scope object.</p>
|
||||
|
||||
<h3>Testing Controllers</h3>
|
||||
|
||||
<p>Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the <code>$rootScope</code> and <code>$controller</code></p>
|
||||
|
||||
<p>Controller Function:
|
||||
<pre class="prettyprint linenums">
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Controller Test:
|
||||
<pre class="prettyprint linenums">
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var scope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>If you need to test a nested controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = childCtrl.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(babyScope.timeOfDay).toBe('evening');
|
||||
expect(babyScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.mvc">About MVC in Angular</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_model">Understanding the Model Component</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_view">Understanding the View Component</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,77 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Depending on the context of the discussion in the Angular documentation, the term <em>model</em> can refer to
|
||||
either a single object representing one entity (for example, a model called "phones" with its value
|
||||
being an array of phones) or the entire data model for the application (all entities).</p>
|
||||
|
||||
<p>In Angular, a model is any data that is reachable as a property of an angular <a href="guide/scope">Scope</a> object. The name of the property is the model identifier and the value is
|
||||
any JavaScript object (including arrays and primitives).</p>
|
||||
|
||||
<p>The only requirement for a JavaScript object to be a model in Angular is that the object must be
|
||||
referenced by an Angular scope as a property of that scope object. This property reference can be
|
||||
created explicitly or implicitly.</p>
|
||||
|
||||
<p>You can create models by explicitly creating scope properties referencing JavaScript objects in the
|
||||
following ways:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Make a direct property assignment to the scope object in JavaScript code; this most commonly
|
||||
occurs in controllers:</p>
|
||||
|
||||
<pre><code> function MyCtrl($scope) {
|
||||
// create property 'foo' on the MyCtrl's scope
|
||||
// and assign it an initial value 'bar'
|
||||
$scope.foo = 'bar';
|
||||
}
|
||||
</code></pre></li>
|
||||
<li><p>Use an <a href="guide/expression">angular expression</a> with an assignment operator in templates:</p>
|
||||
|
||||
<pre><code> <button ng-click="{{foos='ball'}}">Click me</button>
|
||||
</code></pre></li>
|
||||
<li><p>Use <a href="api/ng.directive:ngInit"><code>ngInit directive</code></a> in templates (for toy/example apps
|
||||
only, not recommended for real applications):</p>
|
||||
|
||||
<pre><code> <body ng-init=" foo = 'bar' ">
|
||||
</code></pre></li>
|
||||
</ul>
|
||||
|
||||
<p>Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
|
||||
when processing the following template constructs:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Form input, select, textarea and other form elements:</p>
|
||||
|
||||
<pre><code> <input ng-model="query" value="fluffy cloud">
|
||||
</code></pre>
|
||||
|
||||
<p>The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".</p></li>
|
||||
<li><p>An iterator declaration in <a href="api/ng.directive:ngRepeat"><code>ngRepeater</code></a>:</p>
|
||||
|
||||
<pre><code> <p ng-repeat="phone in phones"></p>
|
||||
</code></pre>
|
||||
|
||||
<p>The code above creates one child scope for each item in the "phones" array and creates a "phone"
|
||||
object (model) on each of these scopes with its value set to the value of "phone" in the array.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>In Angular, a JavaScript object stops being a model when:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>No Angular scope contains a property that references the object.</p></li>
|
||||
<li><p>All Angular scopes that contain a property referencing the object become stale and eligible for
|
||||
garbage collection.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>The following illustration shows a simple data model created implicitly from a simple template:</p>
|
||||
|
||||
<p><img src="img/guide/about_model_final.png"></p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.mvc">About MVC in Angular</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_controller">Understanding the Controller Component</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_view">Understanding the View Component</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,21 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>In Angular, the view is the DOM loaded and rendered in the browser, after Angular has transformed
|
||||
the DOM based on information in the template, controller and model.</p>
|
||||
|
||||
<p><img src="img/guide/about_view_final.png"></p>
|
||||
|
||||
<p>In the Angular implementation of MVC, the view has knowledge of both the model and the controller.
|
||||
The view knows about the model where two-way data-binding occurs. The view has knowledge of the
|
||||
controller through Angular directives, such as <a href="api/ng.directive:ngController"><code>ngController</code></a> and <a href="api/ng.directive:ngView"><code>ngView</code></a>, and through bindings of this form:
|
||||
<code>{{someControllerFunction()}}</code>. In these ways, the view can call functions in an associated
|
||||
controller function.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.mvc">About MVC in Angular</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_model">Understanding the Model Component</a></li>
|
||||
<li><a href="guide/dev_guide.mvc.understanding_controller">Understanding the Controller Component</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,652 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>What does it do?</h2>
|
||||
|
||||
<p>The <code>$location</code> service parses the URL in the browser address bar (based on the <a href="https://developer.mozilla.org/en/window.location">window.location</a>) and makes the URL available to
|
||||
your application. Changes to the URL in the address bar are reflected into $location service and
|
||||
changes to $location are reflected into the browser address bar.</p>
|
||||
|
||||
<p><strong>The $location service:</strong></p>
|
||||
|
||||
<ul>
|
||||
<li>Exposes the current URL in the browser address bar, so you can
|
||||
<ul><li>Watch and observe the URL.</li>
|
||||
<li>Change the URL.</li></ul></li>
|
||||
<li>Synchronizes the URL with the browser when the user
|
||||
<ul><li>Changes the address bar.</li>
|
||||
<li>Clicks the back or forward button (or clicks a History link).</li>
|
||||
<li>Clicks on a link.</li></ul></li>
|
||||
<li>Represents the URL object as a set of methods (protocol, host, port, path, search, hash).</li>
|
||||
</ul>
|
||||
|
||||
<h3>Comparing $location to window.location</h3>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th class="empty-corner-lt"></th>
|
||||
<th>window.location</th>
|
||||
<th>$location service</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="head">purpose</td>
|
||||
<td>allow read/write access to the current browser location</td>
|
||||
<td>same</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">API</td>
|
||||
<td>exposes "raw" object with properties that can be directly modified</td>
|
||||
<td>exposes jQuery-style getters and setters</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">integration with angular application life-cycle</td>
|
||||
<td>none</td>
|
||||
<td>knows about all internal life-cycle phases, integrates with $watch, ...</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">seamless integration with HTML5 API</td>
|
||||
<td>no</td>
|
||||
<td>yes (with a fallback for legacy browsers)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">aware of docroot/context from which the application is loaded</td>
|
||||
<td>no - window.location.path returns "/docroot/actual/path"</td>
|
||||
<td>yes - $location.path() returns "/actual/path"</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>When should I use $location?</h3>
|
||||
|
||||
<p>Any time your application needs to react to a change in the current URL or if you want to change
|
||||
the current URL in the browser.</p>
|
||||
|
||||
<h3>What does it not do?</h3>
|
||||
|
||||
<p>It does not cause a full page reload when the browser URL is changed. To reload the page after
|
||||
changing the URL, use the lower-level API, <code>$window.location.href</code>.</p>
|
||||
|
||||
<h2>General overview of the API</h2>
|
||||
|
||||
<p>The <code>$location</code> service can behave differently, depending on the configuration that was provided to
|
||||
it when it was instantiated. The default configuration is suitable for many applications, for
|
||||
others customizing the configuration can enable new features.</p>
|
||||
|
||||
<p>Once the <code>$location</code> service is instantiated, you can interact with it via jQuery-style getter and
|
||||
setter methods that allow you to get or change the current URL in the browser.</p>
|
||||
|
||||
<h3>$location service configuration</h3>
|
||||
|
||||
<p>To configure the <code>$location</code> service, retrieve the
|
||||
<a href="api/ng.$locationProvider"><code>$locationProvider</code></a> and set the parameters as follows:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><strong>html5Mode(mode)</strong>: {boolean}<br />
|
||||
<code>true</code> - see HTML5 mode<br />
|
||||
<code>false</code> - see Hashbang mode<br />
|
||||
default: <code>false</code></p></li>
|
||||
<li><p><strong>hashPrefix(prefix)</strong>: {string}<br />
|
||||
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
|
||||
default: <code>'!'</code></p></li>
|
||||
</ul>
|
||||
|
||||
<h4>Example configuration</h4>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
</pre>
|
||||
|
||||
<h3>Getter and setter methods</h3>
|
||||
|
||||
<p><code>$location</code> service provides getter methods for read-only parts of the URL (absUrl, protocol, host,
|
||||
port) and getter / setter methods for url, path, search, hash:
|
||||
<pre class="prettyprint linenums">
|
||||
// get the current path
|
||||
$location.path();
|
||||
|
||||
// change the path
|
||||
$location.path('/newValue')
|
||||
</pre>
|
||||
|
||||
<p>All of the setter methods return the same <code>$location</code> object to allow chaining. For example, to
|
||||
change multiple segments in one go, chain setters like this:
|
||||
<pre class="prettyprint linenums">$location.path('/newValue').search({key: value});</pre>
|
||||
|
||||
<p>There is a special <code>replace</code> method which can be used to tell the $location service that the next
|
||||
time the $location service is synced with the browser, the last history record should be replaced
|
||||
instead of creating a new one. This is useful when you want to implement redirection, which would
|
||||
otherwise break the back button (navigating back would retrigger the redirection). To change the
|
||||
current URL without creating a new browser history record you can call:
|
||||
<pre class="prettyprint linenums">
|
||||
$location.path('/someNewPath');
|
||||
$location.replace();
|
||||
// or you can chain these as: $location.path('/someNewPath').replace();
|
||||
</pre>
|
||||
|
||||
<p>Note that the setters don't update <code>window.location</code> immediately. Instead, the <code>$location</code> service is
|
||||
aware of the <a href="api/ng.$rootScope.Scope"><code>scope</code></a> life-cycle and coalesces multiple <code>$location</code>
|
||||
mutations into one "commit" to the <code>window.location</code> object during the scope <code>$digest</code> phase. Since
|
||||
multiple changes to the $location's state will be pushed to the browser as a single change, it's
|
||||
enough to call the <code>replace()</code> method just once to make the entire "commit" a replace operation
|
||||
rather than an addition to the browser history. Once the browser is updated, the $location service
|
||||
resets the flag set by <code>replace()</code> method and future mutations will create new history records,
|
||||
unless <code>replace()</code> is called again.</p>
|
||||
|
||||
<h4>Setters and character encoding</h4>
|
||||
|
||||
<p>You can pass special characters to <code>$location</code> service and it will encode them according to rules
|
||||
specified in <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>. When you access the methods:</p>
|
||||
|
||||
<ul>
|
||||
<li>All values that are passed to <code>$location</code> setter methods, <code>path()</code>, <code>search()</code>, <code>hash()</code>, are
|
||||
encoded.</li>
|
||||
<li>Getters (calls to methods without parameters) return decoded values for the following methods
|
||||
<code>path()</code>, <code>search()</code>, <code>hash()</code>.</li>
|
||||
<li>When you call the <code>absUrl()</code> method, the returned value is a full url with its segments encoded.</li>
|
||||
<li>When you call the <code>url()</code> method, the returned value is path, search and hash, in the form
|
||||
<code>/path?search=a&b=c#hash</code>. The segments are encoded as well.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Hashbang and HTML5 Modes</h2>
|
||||
|
||||
<p><code>$location</code> service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: <strong>Hashbang mode</strong> (the default) and the <strong>HTML5 mode</strong> which is based on using the
|
||||
HTML5 <a href="http://www.w3.org/TR/html5/history.html">History API</a>. Applications use the same API in
|
||||
both modes and the <code>$location</code> service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.</p>
|
||||
|
||||
<p><img src="img/guide/hashbang_vs_regular_url.jpg"></p>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th class="empty-corner-lt"></th>
|
||||
<th>Hashbang mode</th>
|
||||
<th>HTML5 mode</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="head">configuration</td>
|
||||
<td>the default</td>
|
||||
<td>{ html5Mode: true }</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">URL format</td>
|
||||
<td>hashbang URLs in all browsers</td>
|
||||
<td>regular URLs in modern browser, hashbang URLs in old browser</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head"><a href=""> link rewriting</td>
|
||||
<td>no</td>
|
||||
<td>yes</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">requires server-side configuration</td>
|
||||
<td>no</td>
|
||||
<td>yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Hashbang mode (default mode)</h3>
|
||||
|
||||
<p>In this mode, <code>$location</code> uses Hashbang URLs in all browsers.</p>
|
||||
|
||||
<h4>Example</h4>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(false);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
},
|
||||
function($location) {
|
||||
// open http://host.com/base/index.html#!/a
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/a'
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo')
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/new?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
||||
<h4>Crawling your app</h4>
|
||||
|
||||
<p>To allow indexing of your AJAX application, you have to add special meta tag in the head section of
|
||||
your document:
|
||||
<pre class="prettyprint linenums"><meta name="fragment" content="!" /></pre>
|
||||
|
||||
<p>This will cause crawler bot to request links with <code>_escaped_fragment_</code> param so that your server
|
||||
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
|
||||
see <a href="http://code.google.com/web/ajaxcrawling/docs/specification.html">Making AJAX Applications Crawlable</a>.</p>
|
||||
|
||||
<h3>HTML5 mode</h3>
|
||||
|
||||
<p>In HTML5 mode, the <code>$location</code> service getters and setters interact with the browser URL address
|
||||
through the HTML5 history API, which allows for use of regular URL path and search segments,
|
||||
instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
|
||||
<code>$location</code> service will fall back to using the hashbang URLs automatically. This frees you from
|
||||
having to worry about whether the browser displaying your app supports the history API or not; the
|
||||
<code>$location</code> service transparently uses the best available option.</p>
|
||||
|
||||
<ul>
|
||||
<li>Opening a regular URL in a legacy browser -> redirects to a hashbang URL</li>
|
||||
<li>Opening hashbang URL in a modern browser -> rewrites to a regular URL</li>
|
||||
</ul>
|
||||
|
||||
<h4>Example</h4>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
},
|
||||
function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
// open http://host.com/#!/a -> rewrite to http://host.com/a
|
||||
// (replacing the http://host.com/#!/a history record)
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo');
|
||||
$location.absUrl() == 'http://host.com/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.url() == 'new?x=y'
|
||||
$location.absUrl() == 'http://host.com/new?x=y'
|
||||
|
||||
// in browser without html5 history support:
|
||||
// open http://host.com/new?x=y -> redirect to http://host.com/#!/new?x=y
|
||||
// (again replacing the http://host.com/new?x=y history item)
|
||||
$location.path() == '/new'
|
||||
$location.search() == {x: 'y'}
|
||||
|
||||
$location.path('/foo/bar');
|
||||
$location.path() == '/foo/bar'
|
||||
$location.url() == '/foo/bar?x=y'
|
||||
$location.absUrl() == 'http://host.com/#!/foo/bar?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
||||
<h4>Fallback for legacy browsers</h4>
|
||||
|
||||
<p>For browsers that support the HTML5 history API, <code>$location</code> uses the HTML5 history API to write
|
||||
path and search. If the history API is not supported by a browser, <code>$location</code> supplies a Hasbang
|
||||
URL. This frees you from having to worry about whether the browser viewing your app supports the
|
||||
history API or not; the <code>$location</code> service makes this transparent to you.</p>
|
||||
|
||||
<h4>Html link rewriting</h4>
|
||||
|
||||
<p>When you use HTML5 history API mode, you will need different links in different browsers, but all you
|
||||
have to do is specify regular URL links, such as: <code><a href="/some?foo=bar">link</a></code></p>
|
||||
|
||||
<p>When a user clicks on this link,</p>
|
||||
|
||||
<ul>
|
||||
<li>In a legacy browser, the URL changes to <code>/index.html#!/some?foo=bar</code></li>
|
||||
<li>In a modern browser, the URL changes to <code>/some?foo=bar</code></li>
|
||||
</ul>
|
||||
|
||||
<p>In cases like the following, links are not rewritten; instead, the browser will perform a full page
|
||||
reload to the original link.</p>
|
||||
|
||||
<ul>
|
||||
<li>Links that contain <code>target</code> element<br>
|
||||
Example: <code><a href="/ext/link?a=b" target="_self">link</a></code></li>
|
||||
<li>Absolute links that go to a different domain<br>
|
||||
Example: <code><a href="http://angularjs.org/">link</a></code></li>
|
||||
<li>Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: <code><a href="/not-my-base/link">link</a></code></li>
|
||||
</ul>
|
||||
|
||||
<h4>Server side</h4>
|
||||
|
||||
<p>Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
to entry point of your application (e.g. index.html)</p>
|
||||
|
||||
<h4>Crawling your app</h4>
|
||||
|
||||
<p>If you want your AJAX application to be indexed by web crawlers, you will need to add the following
|
||||
meta tag to the HEAD section of your document:
|
||||
<pre class="prettyprint linenums"><meta name="fragment" content="!" /></pre>
|
||||
|
||||
<p>This statement causes a crawler to request links with an empty <code>_escaped_fragment_</code> parameter so that
|
||||
your server can recognize the crawler and serve it HTML snapshots. For more information about this
|
||||
technique, see <a href="http://code.google.com/web/ajaxcrawling/docs/specification.html">Making AJAX Applications Crawlable</a>.</p>
|
||||
|
||||
<h4>Relative links</h4>
|
||||
|
||||
<p>Be sure to check all relative links, images, scripts etc. You must either specify the url base in
|
||||
the head of your main html file (<code><base href="/my-base"></code>) or you must use absolute urls
|
||||
(starting with <code>/</code>) everywhere because relative urls will be resolved to absolute urls using the
|
||||
initial absolute url of the document, which is often different from the root of the application.</p>
|
||||
|
||||
<p>Running Angular apps with the History API enabled from document root is strongly encouraged as it
|
||||
takes care of all relative link issues.</p>
|
||||
|
||||
<h4>Sending links among different browsers</h4>
|
||||
|
||||
<p>Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
|
||||
legacy browsers and hashbang links in modern browser:</p>
|
||||
|
||||
<ul>
|
||||
<li>Modern browser will rewrite hashbang URLs to regular URLs.</li>
|
||||
<li>Older browsers will redirect regular URLs to hashbang URLs.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Example</h4>
|
||||
|
||||
<p>Here you can see two <code>$location</code> instances, both in <strong>Html5 mode</strong>, but on different browsers, so
|
||||
that you can see the differences. These <code>$location</code> services are connected to a fake browsers. Each
|
||||
input represents address bar of the browser.</p>
|
||||
|
||||
<p>Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.</p>
|
||||
|
||||
<p>In this examples we use <code><base href="/base/index.html" /></code>
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-32" source-edit-css="" source-edit-js="script.js-31" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-32" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-32">
|
||||
|
||||
<div ng-non-bindable class="html5-hashbang-example">
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h4>Browser with History API</h4>
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h4>Browser without History API</h4>
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-31"></pre>
|
||||
<script type="text/ng-template" id="script.js-31">
|
||||
function FakeBrowser(initUrl, baseHref) {
|
||||
this.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
this.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
this.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
this.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
|
||||
var browsers = {
|
||||
html5: new FakeBrowser('http://www.host.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$document', root);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.bind('keypress keyup keydown', function() {
|
||||
if (!delay) {
|
||||
delay = setTimeout(fireUrlChange, 250);
|
||||
}
|
||||
});
|
||||
|
||||
browser.url = function(url) {
|
||||
return input.val(url);
|
||||
};
|
||||
|
||||
elm.append('Address: ').append(input);
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
browser.urlChange(input.val());
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
root.bind('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
initEnv('html5');
|
||||
initEnv('hashbang');
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-32" ng-eval-javascript="script.js-31"></div>
|
||||
|
||||
<h2>Caveats</h2>
|
||||
|
||||
<h3>Page reload navigation</h3>
|
||||
|
||||
<p>The <code>$location</code> service allows you to change only the URL; it does not allow you to reload the
|
||||
page. When you need to change the URL and reload the page or navigate to a different page, please
|
||||
use a lower level API, <a href="api/ng.$window"><code>$window.location.href</code></a>.</p>
|
||||
|
||||
<h3>Using $location outside of the scope life-cycle</h3>
|
||||
|
||||
<p><code>$location</code> knows about Angular's <a href="api/ng.$rootScope.Scope"><code>scope</code></a> life-cycle. When a URL changes in
|
||||
the browser it updates the <code>$location</code> and calls <code>$apply</code> so that all $watchers / $observers are
|
||||
notified.
|
||||
When you change the <code>$location</code> inside the <code>$digest</code> phase everything is ok; <code>$location</code> will
|
||||
propagate this change into browser and will notify all the $watchers / $observers.
|
||||
When you want to change the <code>$location</code> from outside Angular (for example, through a DOM Event or
|
||||
during testing) - you must call <code>$apply</code> to propagate the changes.</p>
|
||||
|
||||
<h3>$location.path() and ! or / prefixes</h3>
|
||||
|
||||
<p>A path should always begin with forward slash (<code>/</code>); the <code>$location.path()</code> setter will add the
|
||||
forward slash if it is missing.</p>
|
||||
|
||||
<p>Note that the <code>!</code> prefix in the hashbang mode is not part of <code>$location.path()</code>; it is actually
|
||||
hashPrefix.</p>
|
||||
|
||||
<h2>Testing with the $location service</h2>
|
||||
|
||||
<p>When using <code>$location</code> service during testing, you are outside of the angular's <a href="api/ng.$rootScope.Scope"><code>scope</code></a> life-cycle. This means it's your responsibility to call <code>scope.$apply()</code>.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
describe('serviceUnderTest', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.factory('serviceUnderTest', function($location){
|
||||
// whatever it does...
|
||||
});
|
||||
});
|
||||
|
||||
it('should...', inject(function($location, $rootScope, serviceUnderTest) {
|
||||
$location.path('/new/path');
|
||||
$rootScope.$apply();
|
||||
|
||||
// test whatever the service should do...
|
||||
|
||||
}));
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h2>Migrating from earlier AngularJS releases</h2>
|
||||
|
||||
<p>In earlier releases of Angular, <code>$location</code> used <code>hashPath</code> or <code>hashSearch</code> to process path and
|
||||
search methods. With this release, the <code>$location</code> service processes path and search methods and
|
||||
then uses the information it obtains to compose hashbang URLs (such as
|
||||
<code>http://server.com/#!/path?search=a</code>), when necessary.</p>
|
||||
|
||||
<h3>Changes to your code</h3>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="head">
|
||||
<th>Navigation inside the app</th>
|
||||
<th>Change to</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>$location.href = value<br />$location.hash = value<br />$location.update(value)<br
|
||||
/>$location.updateHash(value)</td>
|
||||
<td>$location.path(path).search(search)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashPath = path</td>
|
||||
<td>$location.path(path)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashSearch = search</td>
|
||||
<td>$location.search(search)</td>
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Navigation outside the app</td>
|
||||
<td>Use lower level API</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.href = value<br />$location.update(value)</td>
|
||||
<td>$window.location.href = value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location[protocol | host | port | path | search]</td>
|
||||
<td>$window.location[protocol | host | port | path | search]</td>
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Read access</td>
|
||||
<td>Change to</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashPath</td>
|
||||
<td>$location.path()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashSearch</td>
|
||||
<td>$location.search()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.href<br />$location.protocol<br />$location.host<br />$location.port<br
|
||||
/>$location.hash</td>
|
||||
<td>$location.absUrl()<br />$location.protocol()<br />$location.host()<br />$location.port()<br
|
||||
/>$location.path() + $location.search()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.path<br />$location.search</td>
|
||||
<td>$window.location.path<br />$window.location.search</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Two-way binding to $location</h3>
|
||||
|
||||
<p>The Angular's compiler currently does not support two-way binding for methods (see <a href="https://github.com/angular/angular.js/issues/404">issue</a>). If you should require two-way binding
|
||||
to the $location object (using <a href="api/ng.directive:input.text"><code>ngModel</code></a> directive on an input field), you will need to specify an extra model property
|
||||
(e.g. <code>locationPath</code>) with two watchers which push $location updates in both directions. For
|
||||
example:
|
||||
<pre class="prettyprint linenums">
|
||||
<!-- html -->
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</pre>
|
||||
<pre class="prettyprint linenums">
|
||||
// js - controller
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
|
||||
$scope.$watch('$location.path()', function(path) {
|
||||
scope.locationPath = path;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h2>Related API</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng.$location"><code>$location API</code></a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,101 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>While Angular offers several useful services, for any nontrivial application you'll find it useful
|
||||
to write your own custom services. To do this you begin by registering a service factory function
|
||||
with a module either via the <a href="api/angular.module"><code>Module#factory api</code></a> or directly
|
||||
via the <a href="api/AUTO.$provide"><code>$provide</code></a> api inside of module config function.</p>
|
||||
|
||||
<p>All Angular services participate in <a href="guide/di">dependency injection (DI)</a> by registering
|
||||
themselves with Angular's DI system (injector) under a <code>name</code> (id) as well as by declaring
|
||||
dependencies which need to be provided for the factory function of the registered service. The
|
||||
ability to swap dependencies for mocks/stubs/dummies in tests allows for services to be highly
|
||||
testable.</p>
|
||||
|
||||
<h2>Registering Services</h2>
|
||||
|
||||
<p>To register a service, you must have a module that this service will be part of. Afterwards, you
|
||||
can register the service with the module either via the <a href="api/angular.Module"><code>Module api</code></a> or
|
||||
by using the <a href="api/AUTO.$provide"><code>$provide</code></a> service in the module configuration
|
||||
function.The following pseudo-code shows both approaches:</p>
|
||||
|
||||
<p>Using the angular.Module api:
|
||||
<pre class="prettyprint linenums">
|
||||
var myModule = angular.module('myModule', []);
|
||||
myModule.factory('serviceId', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>Using the $provide service:
|
||||
<pre class="prettyprint linenums">
|
||||
angular.module('myModule', [], function($provide) {
|
||||
$provide.factory('serviceId', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>Note that you are not registering a service instance, but rather a factory function that will
|
||||
create this instance when called.</p>
|
||||
|
||||
<h2>Dependencies</h2>
|
||||
|
||||
<p>Services can not only be depended upon, but can also have their own dependencies. These can be specified
|
||||
as arguments of the factory function. <a href="guide/di">Read more</a> about dependency injection (DI)
|
||||
in Angular and the use of array notation and the $inject property to make DI annotation
|
||||
minification-proof.</p>
|
||||
|
||||
<p>Following is an example of a very simple service. This service depends on the <code>$window</code> service
|
||||
(which is passed as a parameter to the factory function) and is just a function. The service simply
|
||||
stores all notifications; after the third one, the service displays all of the notifications by
|
||||
window alert.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
angular.module('myModule', [], function($provide) {
|
||||
$provide.factory('notify', ['$window', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h2>Instantiating Angular Services</h2>
|
||||
|
||||
<p>All services in Angular are instantiated lazily. This means that a service will be created
|
||||
only when it is needed for instantiation of a service or an application component that depends on it.
|
||||
In other words, Angular won't instantiate services unless they are requested directly or
|
||||
indirectly by the application.</p>
|
||||
|
||||
<h2>Services as singletons</h2>
|
||||
|
||||
<p>Lastly, it is important to realize that all Angular services are application singletons. This means
|
||||
that there is only one instance of a given service per injector. Since Angular is lethally allergic
|
||||
to global state, it is possible to create multiple injectors, each with its own instance of a
|
||||
given service, but that is rarely needed, except in tests where this property is crucially
|
||||
important.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.services.understanding_services">Understanding Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.managing_dependencies">Managing Service Dependencies</a></li>
|
||||
<li><a href="guide/dev_guide.services.injecting_controllers">Injecting Services Into Controllers</a></li>
|
||||
<li><a href="guide/dev_guide.services.testing_services">Testing Angular Services</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng">Angular Service API</a></li>
|
||||
</ul></div>
|
22
lib/angular/docs/partials/guide/dev_guide.services.html
Normal file
22
lib/angular/docs/partials/guide/dev_guide.services.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Services are a feature that Angular brings to client-side web apps from the server side, where
|
||||
services have been commonly used for a long time. Services in Angular apps are substitutable
|
||||
objects that are wired together using <a href="guide/di">dependency injection (DI)</a>.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.services.understanding_services">Understanding Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.creating_services">Creating Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.managing_dependencies">Managing Service Dependencies</a></li>
|
||||
<li><a href="guide/dev_guide.services.injecting_controllers">Injecting Services Into Controllers</a></li>
|
||||
<li><a href="guide/dev_guide.services.testing_services">Testing Angular Services</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng">Angular Service API</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,138 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Using services as dependencies for controllers is very similar to using services as dependencies
|
||||
for another service.</p>
|
||||
|
||||
<p>Since JavaScript is a dynamic language, DI can't figure out which services to inject by static
|
||||
types (like in static typed languages). Therefore, you can specify the service name by using the
|
||||
<code>$inject</code> property, which is an array containing strings with names of services to be injected.
|
||||
The name must match the corresponding service ID registered with angular. The order of the service
|
||||
IDs matters: the order of the services in the array will be used when calling the factory function
|
||||
with injected parameters. The names of parameters in factory function don't matter, but by
|
||||
convention they match the service IDs, which has added benefits discussed below.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function myController($loc, $log) {
|
||||
this.firstMethod = function() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
}
|
||||
// which services to inject ?
|
||||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="MyServiceModule" source-edit-deps="angular.js script.js" source-edit-html="index.html-34" source-edit-css="" source-edit-js="script.js-33" source-edit-unit="" source-edit-scenario="scenario.js-35"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-34" ng-html-wrap="MyServiceModule angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-34">
|
||||
|
||||
|
||||
<div ng-controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-33"></pre>
|
||||
<script type="text/ng-template" id="script.js-33">
|
||||
angular.
|
||||
module('MyServiceModule', []).
|
||||
factory('notify', ['$window', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
function myController(scope, notifyService) {
|
||||
scope.callNotify = function(msg) {
|
||||
notifyService(msg);
|
||||
};
|
||||
}
|
||||
|
||||
myController.$inject = ['$scope','notify'];
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-35"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-35">
|
||||
it('should test service', function() {
|
||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="MyServiceModule" ng-set-html="index.html-34" ng-eval-javascript="script.js-33"></div>
|
||||
|
||||
<h3>Implicit Dependency Injection</h3>
|
||||
|
||||
<p>A new feature of Angular DI allows it to determine the dependency from the name of the parameter.
|
||||
Let's rewrite the above example to show the use of this implicit dependency injection of
|
||||
<code>$window</code>, <code>$scope</code>, and our <code>notify</code> service:</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="MyServiceModuleDI" source-edit-deps="angular.js script.js" source-edit-html="index.html-37" source-edit-css="" source-edit-js="script.js-36" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-37" ng-html-wrap="MyServiceModuleDI angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-37">
|
||||
|
||||
<div ng-controller="myController">
|
||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-36"></pre>
|
||||
<script type="text/ng-template" id="script.js-36">
|
||||
angular.
|
||||
module('MyServiceModuleDI', []).
|
||||
factory('notify', function($window) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
$window.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function myController($scope, notify) {
|
||||
$scope.callNotify = function(msg) {
|
||||
notify(msg);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="MyServiceModuleDI" ng-set-html="index.html-37" ng-eval-javascript="script.js-36"></div>
|
||||
|
||||
<p>However, if you plan to <a href="http://en.wikipedia.org/wiki/Minification_(programming)">minify</a> your
|
||||
code, your variable names will get renamed in which case you will still need to explicitly specify
|
||||
dependencies with the <code>$inject</code> property.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<p><a href="guide/dev_guide.services.understanding_services">Understanding Angular Services</a>
|
||||
<a href="guide/dev_guide.services.creating_services">Creating Angular Services</a>
|
||||
<a href="guide/dev_guide.services.managing_dependencies">Managing Service Dependencies</a>
|
||||
<a href="guide/dev_guide.services.testing_services">Testing Angular Services</a></p>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<p><a href="api/ng">Angular Service API</a></p></div>
|
|
@ -0,0 +1,113 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Angular allows services to declare other services as dependencies needed for construction of their
|
||||
instances.</p>
|
||||
|
||||
<p>To declare dependencies, you specify them in the factory function signature and annotate the
|
||||
function with the inject annotations either using by setting the <code>$inject</code> property, as an array of
|
||||
string identifiers or using the array notation. Optionally the <code>$inject</code> property declaration can be
|
||||
dropped (see "Inferring <code>$inject</code>" but note that that is currently an experimental feature).</p>
|
||||
|
||||
<p>Using the array notation:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function myModuleCfgFn($provide) {
|
||||
$provide.factory('myService', ['dep1', 'dep2', function(dep1, dep2) {}]);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Using the $inject property:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function myModuleCfgFn($provide) {
|
||||
var myServiceFactory = function(dep1, dep2) {};
|
||||
myServiceFactory.$inject = ['dep1', 'dep2'];
|
||||
$provide.factory('myService', myServiceFactory);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Using DI inference (incompatible with minifiers):</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function myModuleCfgFn($provide) {
|
||||
$provide.factory('myService', function(dep1, dep2) {});
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Here is an example of two services, one of which depends on the other and both
|
||||
of which depend on other services that are provided by the Angular framework:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
/**
|
||||
* batchLog service allows for messages to be queued in memory and flushed
|
||||
* to the console.log every 50 seconds.
|
||||
*
|
||||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
function batchLogModule($provide){
|
||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$timeout(log, 50000);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
log();
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
}
|
||||
}]);
|
||||
|
||||
/**
|
||||
* routeTemplateMonitor monitors each $route change and logs the current
|
||||
* template via the batchLog service.
|
||||
*/
|
||||
$provide.factory('routeTemplateMonitor',
|
||||
['$route', 'batchLog', '$rootScope',
|
||||
function($route, batchLog, $rootScope) {
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
// get the main service to kick of the application
|
||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||
</pre>
|
||||
|
||||
<p>Things to notice in this example:</p>
|
||||
|
||||
<ul>
|
||||
<li>The <code>batchLog</code> service depends on the built-in <a href="api/ng.$timeout"><code>$timeout</code></a> and
|
||||
<a href="api/ng.$log"><code>$log</code></a> services, and allows messages to be logged into the
|
||||
<code>console.log</code> in batches.</li>
|
||||
<li>The <code>routeTemplateMonitor</code> service depends on the built-in <a href="api/ng.$route"><code>$route</code></a> service as well as our custom <code>batchLog</code> service.</li>
|
||||
<li>Both of our services use the factory function signature and array notation for inject annotations
|
||||
to declare their dependencies. It is important that the order of the string identifiers in the array
|
||||
is the same as the order of argument names in the signature of the factory function. Unless the
|
||||
dependencies are inferred from the function signature, it is this array with IDs and their order
|
||||
that the injector uses to determine which services and in which order to inject.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.services.understanding_services">Understanding Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.creating_services">Creating Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.injecting_controllers">Injecting Services Into Controllers</a></li>
|
||||
<li><a href="guide/dev_guide.services.testing_services">Testing Angular Services</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng">Angular Service API</a></li>
|
||||
<li><a href="api/angular.injector"><code>Angular Injector API</code></a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,63 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>The following is a unit test for the 'notify' service in the 'Dependencies' example in <a href="guide/dev_guide.services.creating_services">Creating Angular Services</a>. The unit test example uses Jasmine
|
||||
spy (mock) instead of a real browser alert.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var mock, notify;
|
||||
|
||||
beforeEach(function() {
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('$window', mock);
|
||||
});
|
||||
|
||||
inject(function($injector) {
|
||||
notify = $injector.get('notify');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not alert first two notifications', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
|
||||
expect(mock.alert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should alert all after third notification', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('three');
|
||||
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.services.understanding_services">Understanding Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.creating_services">Creating Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.managing_dependencies">Managing Service Dependencies</a></li>
|
||||
<li><a href="guide/dev_guide.services.injecting_controllers">Injecting Services Into Controllers</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng">Angular Service API</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,39 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Angular services are singletons that carry out specific tasks common to web apps, such as the
|
||||
<a href="api/ng.$http"><code>$http service</code></a> that provides low level access to the browser's
|
||||
<code>XMLHttpRequest</code> object.</p>
|
||||
|
||||
<p>To use an Angular service, you identify it as a dependency for the dependent (a controller, or
|
||||
another service) that depends on the service. Angular's dependency injection subsystem takes care
|
||||
of the rest. The Angular injector subsystem is in charge of service instantiation, resolution of
|
||||
dependencies, and provision of dependencies to factory functions as requested.</p>
|
||||
|
||||
<p>Angular injects dependencies using "constructor" injection (the service is passed in via a factory
|
||||
function). Because JavaScript is a dynamically typed language, Angular's dependency injection
|
||||
subsystem cannot use static types to identify service dependencies. For this reason a dependent
|
||||
must explicitly define its dependencies by using the <code>$inject</code> property. For example:</p>
|
||||
|
||||
<pre><code> myController.$inject = ['$location'];
|
||||
</code></pre>
|
||||
|
||||
<p>The Angular web framework provides a set of services for common operations. Like other core Angular
|
||||
variables and identifiers, the built-in services always start with <code>$</code> (such as <code>$http</code> mentioned
|
||||
above). You can also create your own custom services.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/di">About Angular Dependency Injection</a></li>
|
||||
<li><a href="guide/dev_guide.services.creating_services">Creating Angular Services</a></li>
|
||||
<li><a href="guide/dev_guide.services.managing_dependencies">Managing Service Dependencies</a></li>
|
||||
<li><a href="guide/dev_guide.services.testing_services">Testing Angular Services</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng">Angular Service API</a></li>
|
||||
<li><a href="api/angular.injector"><code>Injector API</code></a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,25 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Angular sets these CSS classes. It is up to your application to provide useful styling.</p>
|
||||
|
||||
<h2>CSS classes used by angular</h2>
|
||||
|
||||
<ul>
|
||||
<li><p><code>ng-invalid</code>, <code>ng-valid</code></p>
|
||||
|
||||
<ul><li><strong>Usage:</strong> angular applies this class to an input widget element if that element's input does
|
||||
not pass validation. (see <a href="api/ng.directive:input"><code>input</code></a> directive).</li></ul></li>
|
||||
<li><p><code>ng-pristine</code>, <code>ng-dirty</code></p>
|
||||
|
||||
<ul><li><strong>Usage:</strong> angular <a href="api/ng.directive:input"><code>input</code></a> directive applies <code>ng-pristine</code> class
|
||||
to a new input widget element which did not have user interaction. Once the user interacts with
|
||||
the input widget the class is changed to <code>ng-dirty</code>.</li></ul></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.templates">Angular Templates</a></li>
|
||||
<li><a href="guide/forms">Angular Forms</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,38 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Data-binding in Angular web apps is the automatic syncronization of data between the model and view
|
||||
components. The way that Angular implements data-binding lets you treat the model as the
|
||||
single-source-of-truth in your application. The view is a projection of the model at all times.
|
||||
When the model changes, the view reflects the change, and vice versa.</p>
|
||||
|
||||
<h3>Data Binding in Classical Template Systems</h3>
|
||||
|
||||
<p><img class="right" src="img/One_Way_Data_Binding.png"/>
|
||||
Most templating systems bind data in only one direction: they merge template and model components
|
||||
together into a view, as illustrated in the diagram. After the merge occurs, changes to the model
|
||||
or related sections of the view are NOT automatically reflected in the view. Worse, any changes
|
||||
that the user makes to the view are not reflected in the model. This means that the developer has
|
||||
to write code that constantly syncs the view with the model and the model with the view.</p>
|
||||
|
||||
<h3>Data Binding in Angular Templates</h3>
|
||||
|
||||
<p><img class="right" src="img/Two_Way_Data_Binding.png"/>
|
||||
The way Angular templates works is different, as illustrated in the diagram. They are different
|
||||
because first the template (which is the uncompiled HTML along with any additional markup or
|
||||
directives) is compiled on the browser, and second, the compilation step produces a live view. We
|
||||
say live because any changes to the view are immediately reflected in the model, and any changes in
|
||||
the model are propagated to the view. This makes the model always the single-source-of-truth for
|
||||
the application state, greatly simplifying the programming model for the developer. You can think of
|
||||
the view as simply an instant projection of your model.</p>
|
||||
|
||||
<p>Because the view is just a projection of the model, the controller is completely separated from the
|
||||
view and unaware of it. This makes testing a snap because it is easy to test your controller in
|
||||
isolation without the view and the related DOM/browser dependency.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/scope">Angular Scopes</a></li>
|
||||
<li><a href="guide/dev_guide.templates">Angular Templates</a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,74 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Writing your own filter is very easy: just register a new filter (injectable) factory function with
|
||||
your module. This factory function should return a new filter function which takes the input value
|
||||
as the first argument. Any filter arguments are passed in as additional arguments to the filter
|
||||
function.</p>
|
||||
|
||||
<p>The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case and assigns color.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="MyReverseModule" source-edit-deps="angular.js script.js" source-edit-html="index.html-39" source-edit-css="" source-edit-js="script.js-38" source-edit-unit="" source-edit-scenario="scenario.js-40"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-39" ng-html-wrap="MyReverseModule angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-39">
|
||||
|
||||
|
||||
<div ng-controller="Ctrl">
|
||||
<input ng-model="greeting" type="greeting"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-38"></pre>
|
||||
<script type="text/ng-template" id="script.js-38">
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
function Ctrl($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-40"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-40">
|
||||
it('should reverse greeting', function() {
|
||||
expect(binding('greeting|reverse')).toEqual('olleh');
|
||||
input('greeting').enter('ABC');
|
||||
expect(binding('greeting|reverse')).toEqual('CBA');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="MyReverseModule" ng-set-html="index.html-39" ng-eval-javascript="script.js-38"></div>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.templates.filters">Understanding Angular Filters</a></li>
|
||||
<li><a href="guide/compiler">Angular HTML Compiler</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng.$filter"><code>Angular Filter API</code></a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,28 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Angular filters format data for display to the user. In addition to formatting data, filters can
|
||||
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
|
||||
to filtered output.</p>
|
||||
|
||||
<p>For example, you might have a data object that needs to be formatted according to the locale before
|
||||
displaying it to the user. You can pass expressions through a chain of filters like this:</p>
|
||||
|
||||
<pre><code> name | uppercase
|
||||
</code></pre>
|
||||
|
||||
<p>The expression evaluator simply passes the value of name to
|
||||
<a href="api/ng.filter:uppercase"><code>uppercase filter</code></a>.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.templates.filters.using_filters">Using Angular Filters</a></li>
|
||||
<li><a href="guide/dev_guide.templates.filters.creating_filters">Creating Angular Filters</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng.$filter"><code>Angular Filter API</code></a></li>
|
||||
</ul></div>
|
|
@ -0,0 +1,47 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Filters can be part of any <a href="api/ng.$rootScope.Scope"><code>api/ng.$rootScope.Scope</code></a> evaluation but are typically used to format
|
||||
expressions in bindings in your templates:</p>
|
||||
|
||||
<pre><code> {{ expression | filter }}
|
||||
</code></pre>
|
||||
|
||||
<p>Filters typically transform the data to a new data type, formatting the data in the process.
|
||||
Filters can also be chained, and can take optional arguments.</p>
|
||||
|
||||
<p>You can chain filters using this syntax:</p>
|
||||
|
||||
<pre><code> {{ expression | filter1 | filter2 }}
|
||||
</code></pre>
|
||||
|
||||
<p>You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
|
||||
2 decimal points:</p>
|
||||
|
||||
<pre><code> 123 | number:2
|
||||
</code></pre>
|
||||
|
||||
<p>Here are some examples that show values before and after applying different filters to an
|
||||
expression in a binding:</p>
|
||||
|
||||
<ul>
|
||||
<li>No filter: <code>{{1234.5678}}</code> => <code>1234.5678</code></li>
|
||||
<li>Number filter: <code>{{1234.5678|number}}</code> => <code>1,234.57</code>. Notice the "," and rounding to two
|
||||
significant digits.</li>
|
||||
<li>Filter with arguments: <code>{{1234.5678|number:5}}</code> => <code>1,234.56780</code>. Filters can take optional
|
||||
arguments, separated by colons in a binding. For example, the "number" filter takes a number
|
||||
argument that specifies how many digits to display to the right of the decimal point.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.templates.filters">Understanding Angular Filters</a></li>
|
||||
<li><a href="guide/dev_guide.templates.filters.creating_filters">Creating Angular Filters</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/ng.$filter"><code>Angular Filter API</code></a></li>
|
||||
</ul></div>
|
60
lib/angular/docs/partials/guide/dev_guide.templates.html
Normal file
60
lib/angular/docs/partials/guide/dev_guide.templates.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>An Angular template is the declarative specification that, along with information from the model
|
||||
and controller, becomes the rendered view that a user sees in the browser. It is the static DOM,
|
||||
containing HTML, CSS, and angular-specific elements and angular-specific element attributes. The
|
||||
Angular elements and attributes direct angular to add behavior and transform the template DOM into
|
||||
the dynamic view DOM.</p>
|
||||
|
||||
<p>These are the types of Angular elements and element attributes you can use in a template:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/directive">Directive</a> — An attribute or element that
|
||||
augments an existing DOM element or represents a reusable DOM component - a widget.</li>
|
||||
<li><a href="api/ng.$interpolate"><code>Markup</code></a> — The double
|
||||
curly brace notation <code>{{ }}</code> to bind expressions to elements is built-in angular markup.</li>
|
||||
<li><a href="guide/dev_guide.templates.filters">Filter</a> — Formats your data for display to the user.</li>
|
||||
<li><a href="guide/forms">Form controls</a> — Lets you validate user input.</li>
|
||||
</ul>
|
||||
|
||||
<p>Note: In addition to declaring the elements above in templates, you can also access these elements
|
||||
in JavaScript code.</p>
|
||||
|
||||
<p>The following code snippet shows a simple Angular template made up of standard HTML tags along with
|
||||
Angular <a href="guide/directive">directives</a> and curly-brace bindings
|
||||
with <a href="guide/expression">expressions</a>:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<html ng-app>
|
||||
<!-- Body tag augmented with ngController directive -->
|
||||
<body ng-controller="MyController">
|
||||
<input ng-model="foo" value="bar">
|
||||
<!-- Button tag with ng-click directive, and
|
||||
string expression 'buttonText'
|
||||
wrapped in "{{ }}" markup -->
|
||||
<button ng-click="changeFoo()">{{buttonText}}</button>
|
||||
<script src="angular.js">
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<p>In a simple single-page app, the template consists of HTML, CSS, and angular directives contained
|
||||
in just one HTML file (usually <code>index.html</code>). In a more complex app, you can display multiple views
|
||||
within one main page using "partials", which are segments of template located in separate HTML
|
||||
files. You "include" the partials in the main page using the <a href="api/ng.$route"><code>$route</code></a> service in conjunction with the <a href="api/ng.directive:ngView"><code>ngView</code></a> directive. An
|
||||
example of this technique is shown in the <a href="tutorial/index">angular tutorial</a>, in steps seven and
|
||||
eight.</p>
|
||||
|
||||
<h3>Related Topics</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="guide/dev_guide.templates.filters">Angular Filters</a></li>
|
||||
<li><a href="guide/forms">Angular Forms</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Related API</h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/index">API Reference</a></li>
|
||||
</ul></div>
|
302
lib/angular/docs/partials/guide/dev_guide.unit-testing.html
Normal file
302
lib/angular/docs/partials/guide/dev_guide.unit-testing.html
Normal file
|
@ -0,0 +1,302 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.</p>
|
||||
|
||||
<h2>It is all about NOT mixing concerns</h2>
|
||||
|
||||
<p>Unit testing as the name implies is about testing individual units of code. Unit tests try to
|
||||
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
|
||||
in the right order?"</p>
|
||||
|
||||
<p>In order to answer such question it is very important that we can isolate the unit of code under test.
|
||||
That is because when we are testing the sort function we don't want to be forced into creating
|
||||
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort.</p>
|
||||
|
||||
<p>While
|
||||
this may seem obvious it usually is very difficult to be able to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
manipulates the DOM.</p>
|
||||
|
||||
<p>With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstraction which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
it is easy to write a sort function which sorts some data, so that your test can create a data set,
|
||||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
have to wait for XHR, or create the right kind of DOM, or assert that your function has mutated the
|
||||
DOM in the right way.</p>
|
||||
|
||||
<h3>With great power comes great responsibility</h3>
|
||||
|
||||
<p>Angular is written with testability in mind, but it still requires that you
|
||||
do the right thing. We tried to make the right thing easy, but Angular is not magic, which means if
|
||||
you don't follow these guidelines you may very well end up with an untestable application.</p>
|
||||
|
||||
<h3>Dependency Injection</h3>
|
||||
|
||||
<p>There are several ways in which you can get a hold of a dependency:
|
||||
1. You could create it using the <code>new</code> operator.
|
||||
2. You could look for it in a well known place, also known as global singleton.
|
||||
3. You could ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2)
|
||||
4. You could expect that it be handed to you.</p>
|
||||
|
||||
<p>Out of the four options in the list above, only the last one is testable. Let's look at why:</p>
|
||||
|
||||
<h4>Using the <code>new</code> operator</h4>
|
||||
|
||||
<p>While there is nothing wrong with the <code>new</code> operator fundamentally the issue is that calling a new
|
||||
on a constructor permanently binds the call site to the type. For example lets say that we are
|
||||
trying to instantiate an <code>XHR</code> so that we can get some data from the server.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = function() {...}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The issue becomes that in tests, we would very much like to instantiate a <code>MockXHR</code> which would
|
||||
allow us to return fake data and simulate network failures. By calling <code>new XHR()</code> we are
|
||||
permanently bound to the actual XHR, and there is no good way to replace it. Yes there is monkey
|
||||
patching. That is a bad idea for many reasons which are outside the scope of this document.</p>
|
||||
|
||||
<p>The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldXHR = XHR;
|
||||
XHR = function MockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that MockXHR got called with the right arguments
|
||||
XHR = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Global look-up:</h4>
|
||||
|
||||
<p>Another way to approach the problem is to look for the service in a well known location.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
global.xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>While no new instance of the dependency is being created, it is fundamentally the same as <code>new</code>, in
|
||||
that there is no good way to intercept the call to <code>global.xhr</code> for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: <a href="http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/">Brittle Global State & Singletons</a></p>
|
||||
|
||||
<p>The class above is hard to test since we have to change global state:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
global.xhr = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Service Registry:</h4>
|
||||
|
||||
<p>It may seem as that this can be solved by having a registry for all of the services, and then
|
||||
having the tests replace the services as needed.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
this.doWork = function() {
|
||||
var xhr = serviceRegistry.get('xhr');
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>However, where does the serviceRegistry come from? if it is:
|
||||
* <code>new</code>-ed up, the the test has no chance to reset the services for testing
|
||||
* global look-up, then the service returned is global as well (but resetting is easier, since
|
||||
there is only one global variable to be reset).</p>
|
||||
|
||||
<p>The class above is hard to test since we have to change global state:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Passing in Dependencies:</h4>
|
||||
|
||||
<p>Lastly the dependency can be passed in.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function() {
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This is the preferred way since the code makes no assumptions as to where the <code>xhr</code> comes from,
|
||||
rather that whoever created the class was responsible for passing it in. Since the creator of the
|
||||
class should be different code than the user of the class, it separates the responsibility of
|
||||
creation from the logic, and that is what dependency-injection is in a nutshell.</p>
|
||||
|
||||
<p>The class above is very testable, since in the test we can write:
|
||||
<pre class="prettyprint linenums">
|
||||
function xhrMock(args) {...}
|
||||
var myClass = new MyClass(xhrMock);
|
||||
myClass.doWork();
|
||||
// assert that xhrMock got called with the right arguments
|
||||
</pre>
|
||||
|
||||
<p>Notice that no global variables were harmed in the writing of this test.</p>
|
||||
|
||||
<p>Angular comes with <a href="guide/di">dependency injection</a> built in which makes the right thing
|
||||
easy to do, but you still need to do it if you wish to take advantage of the testability story.</p>
|
||||
|
||||
<h3>Controllers</h3>
|
||||
|
||||
<p>What makes each application unique is its logic, which is what we would like to test. If the logic
|
||||
for your application is mixed in with DOM manipulation, it will be hard to test as in the example
|
||||
below:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function PasswordCtrl() {
|
||||
// get references to DOM elements
|
||||
var msg = $('.ex1 span');
|
||||
var input = $('.ex1 input');
|
||||
var strength;
|
||||
|
||||
this.grade = function() {
|
||||
msg.removeClass(strength);
|
||||
var pwd = input.val();
|
||||
password.text(pwd);
|
||||
if (pwd.length > 8) {
|
||||
strength = 'strong';
|
||||
} else if (pwd.length > 3) {
|
||||
strength = 'medium';
|
||||
} else {
|
||||
strength = 'weak';
|
||||
}
|
||||
msg
|
||||
.addClass(strength)
|
||||
.text(strength);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The code above is problematic from a testability point of view, since it requires your test to have the right kind
|
||||
of DOM present when the code executes. The test would look like this:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var input = $('<input type="text"/>');
|
||||
var span = $('<span>');
|
||||
$('body').html('<div class="ex1">')
|
||||
.find('div')
|
||||
.append(input)
|
||||
.append(span);
|
||||
var pc = new PasswordCtrl();
|
||||
input.val('abc');
|
||||
pc.grade();
|
||||
expect(span.text()).toEqual('weak');
|
||||
$('body').html('');
|
||||
</pre>
|
||||
|
||||
<p>In angular the controllers are strictly separated from the DOM manipulation logic which results in
|
||||
a much easier testability story as can be seen in this example:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function PasswordCtrl($scope) {
|
||||
$scope.password = '';
|
||||
$scope.grade = function() {
|
||||
var size = $scope.password.length;
|
||||
if (size > 8) {
|
||||
$scope.strength = 'strong';
|
||||
} else if (size > 3) {
|
||||
$scope.strength = 'medium';
|
||||
} else {
|
||||
$scope.strength = 'weak';
|
||||
}
|
||||
};
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>and the test is straight forward</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var pc = new PasswordCtrl();
|
||||
pc.password('abc');
|
||||
pc.grade();
|
||||
expect(pc.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
<p>Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.</p>
|
||||
|
||||
<h3>Filters</h3>
|
||||
|
||||
<p><a href="api/ng.$filter"><code>Filters</code></a> are functions which transform the data into user readable
|
||||
format. They are important because they remove the formatting responsibility from the application
|
||||
logic, further simplifying the application logic.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
myModule.filter('length', function() {
|
||||
return function(text){
|
||||
return (''+(text||'')).length;
|
||||
}
|
||||
});
|
||||
|
||||
var length = $filter('length');
|
||||
expect(length(null)).toEqual(0);
|
||||
expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
<h3>Directives</h3>
|
||||
|
||||
<p>Directives in angular are responsible for updating the DOM when the state of the model changes.</p>
|
||||
|
||||
<h3>Mocks</h3>
|
||||
|
||||
<p>oue</p>
|
||||
|
||||
<h3>Global State Isolation</h3>
|
||||
|
||||
<p>oue</p>
|
||||
|
||||
<h2>Preferred way of Testing</h2>
|
||||
|
||||
<p>uo</p>
|
||||
|
||||
<h3>JavaScriptTestDriver</h3>
|
||||
|
||||
<p>ou</p>
|
||||
|
||||
<h3>Jasmine</h3>
|
||||
|
||||
<p>ou</p>
|
||||
|
||||
<h3>Sample project</h3>
|
||||
|
||||
<p>uoe</p></div>
|
223
lib/angular/docs/partials/guide/di.html
Normal file
223
lib/angular/docs/partials/guide/di.html
Normal file
|
@ -0,0 +1,223 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>Dependency Injection</h2>
|
||||
|
||||
<p>Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its
|
||||
dependencies.</p>
|
||||
|
||||
<p>For in-depth discussion about DI, see <a href="http://en.wikipedia.org/wiki/Dependency_injection">Dependency Injection</a> at Wikipedia, <a href="http://martinfowler.com/articles/injection.html">Inversion of Control</a> by Martin Fowler, or read about DI in your favorite software design pattern
|
||||
book.</p>
|
||||
|
||||
<h3>DI in a nutshell</h3>
|
||||
|
||||
<p>There are only three ways how an object or a function can get a hold of its dependencies:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>The dependency can be created, typically using the <code>new</code> operator.</p></li>
|
||||
<li><p>The dependency can be looked up by referring to a global variable.</p></li>
|
||||
<li><p>The dependency can be passed in to where it is needed.</p></li>
|
||||
</ol>
|
||||
|
||||
<p>The first two option of creating or looking up dependencies are not optimal, because they hard
|
||||
code the dependency, making it difficult, if not impossible, to modify the dependencies.
|
||||
This is especially problematic in tests, where it is often desirable to provide mock dependencies
|
||||
for test isolation.</p>
|
||||
|
||||
<p>The third option is the most viable, since it removes the responsibility of locating the
|
||||
dependency from the component. The dependency is simply handed to the component.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function SomeClass(greeter) {
|
||||
this.greeter = greeter
|
||||
}
|
||||
|
||||
SomeClass.prototype.doSomething = function(name) {
|
||||
this.greeter.greet(name);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>In the above example the <code>SomeClass</code> is not concerned with locating the <code>greeter</code> dependency, it
|
||||
is simply handed the <code>greeter</code> at runtime.</p>
|
||||
|
||||
<p>This is desirable, but it puts the responsibility of getting hold of the dependency onto the
|
||||
code responsible for the construction of <code>SomeClass</code>.</p>
|
||||
|
||||
<p>To manage the responsibility of dependency creation, each Angular application has an <a href="api/angular.injector"><code>injector</code></a>. The injector is a service locator that is responsible for
|
||||
construction and lookup of dependencies.</p>
|
||||
|
||||
<p>Here is an example of using the injector service.
|
||||
<pre class="prettyprint linenums">
|
||||
// Provide the wiring information in a module
|
||||
angular.module('myModule', []).
|
||||
|
||||
// Teach the injector how to build a 'greeter'
|
||||
// Notice that greeter itself is dependent on '$window'
|
||||
factory('greeter', function($window) {
|
||||
// This is a factory function, and is responsible for
|
||||
// creating the 'greet' service.
|
||||
return {
|
||||
greet: function(text) {
|
||||
$window.alert(text);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// New injector is created from the module.
|
||||
// (This is usually done automatically by angular bootstrap)
|
||||
var injector = angular.injector(['myModule', 'ng']);
|
||||
|
||||
// Request any dependency from the injector
|
||||
var greeter = injector.get('greeter');
|
||||
</pre>
|
||||
|
||||
<p>Asking for dependencies solves the issue of hard coding, but it also means that the injector needs
|
||||
to be passed throughout the application. Passing the injector breaks the <a href="http://en.wikipedia.org/wiki/Law_of_Demeter">Law of Demeter</a>. To remedy this, we turn the
|
||||
dependency lookup responsibility to the injector by declaring the dependencies as in this example:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<!-- Given this HTML -->
|
||||
<div ng-controller="MyController">
|
||||
<button ng-click="sayHello()">Hello</button>
|
||||
</div>
|
||||
</pre> <br />
|
||||
<pre class="prettyprint linenums">
|
||||
// And this controller definition
|
||||
function MyController($scope, greeter) {
|
||||
$scope.sayHello = function() {
|
||||
greeter.greet('Hello World');
|
||||
};
|
||||
}
|
||||
|
||||
// The 'ng-controller' directive does this behind the scenes
|
||||
injector.instantiate(MyController);
|
||||
</pre>
|
||||
|
||||
<p>Notice that by having the <code>ng-controller</code> instantiate the class, it can satisfy all of the
|
||||
dependencies of the <code>MyController</code> without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply ask for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.</p>
|
||||
|
||||
<h2>Dependency Annotation</h2>
|
||||
|
||||
<p>How does the injector know what service needs to be injected? </p>
|
||||
|
||||
<p>The application developer needs to provide annotation information that the injector uses in order
|
||||
to resolve the dependencies. Throughout Angular certain API functions are invoked using the
|
||||
injector, as per the API documentation. The injector needs to know what services to inject into
|
||||
the function. Below are three equivalent ways of annotating your code with service name
|
||||
information. These can be used interchangeably as you see fit and are equivalent.</p>
|
||||
|
||||
<h2>Inferring Dependencies</h2>
|
||||
|
||||
<p>The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
are the names of the dependencies.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyController($scope, greeter) {
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Given a function the injector can infer the names of the service to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example <code>$scope</code>, and
|
||||
<code>greeter</code> are two services which need to be injected into the function.</p>
|
||||
|
||||
<p>While straightforward, this method will not work with JavaScript minifiers/obfuscators as they
|
||||
rename the method parameter names. This makes this way of annotating only useful for <a href="http://www.pretotyping.org/">pretotyping</a>, and demo applications.</p>
|
||||
|
||||
<h2><code>$inject</code> Annotation</h2>
|
||||
|
||||
<p>To allow the minifers to rename the function parameters and still be able to inject right services
|
||||
the function needs to be annotate with the <code>$inject</code> property. The <code>$inject</code> property is an array
|
||||
of service names to inject.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var MyController = function(renamed$scope, renamedGreeter) {
|
||||
...
|
||||
}
|
||||
MyController.$inject = ['$scope', 'greeter'];
|
||||
</pre>
|
||||
|
||||
<p>Care must be taken that the <code>$inject</code> annotation is kept in sync with the actual arguments in the
|
||||
function declaration.</p>
|
||||
|
||||
<p>This method of annotation is useful for controller declarations since it assigns the annotation
|
||||
information with the function.</p>
|
||||
|
||||
<h2>Inline Annotation</h2>
|
||||
|
||||
<p>Sometimes using the <code>$inject</code> annotation style is not convenient such as when annotating
|
||||
directives.</p>
|
||||
|
||||
<p>For example:
|
||||
<pre class="prettyprint linenums">
|
||||
someModule.factory('greeter', function($window) {
|
||||
...;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>Results in code bloat due to the need of temporary variable:
|
||||
<pre class="prettyprint linenums">
|
||||
var greeterFactory = function(renamed$window) {
|
||||
...;
|
||||
};
|
||||
|
||||
greeterFactory.$inject = ['$window'];
|
||||
|
||||
someModule.factory('greeter', greeterFactory);
|
||||
</pre>
|
||||
|
||||
<p>For this reason the third annotation style is provided as well.
|
||||
<pre class="prettyprint linenums">
|
||||
someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
...;
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
<p>Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
where injection is supported.</p>
|
||||
|
||||
<h2>Where can I use DI?</h2>
|
||||
|
||||
<p>DI is pervasive throughout Angular. It is typically used in controllers and factory methods. </p>
|
||||
|
||||
<h3>DI in controllers</h3>
|
||||
|
||||
<p>Controllers are classes which are responsible for application behavior. The recommended way of
|
||||
declaring controllers is:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var MyController = function($scope, dep1, dep2) {
|
||||
...
|
||||
$scope.aMethod = function() {
|
||||
...
|
||||
}
|
||||
}
|
||||
MyController.$inject = ['$scope', 'dep1', 'dep2'];
|
||||
</pre>
|
||||
|
||||
<h3>Factory methods</h3>
|
||||
|
||||
<p>Factory methods are responsible for creating most objects in Angular. Examples are directives,
|
||||
services, and filters. The factory methods are registered with the module, and the recommended way
|
||||
of declaring factories is:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
angualar.module('myModule', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
factory('serviceId', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
directive('directiveName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
filter('filterName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
run(['depService', function(depService) {
|
||||
...
|
||||
}]);
|
||||
</pre></div>
|
694
lib/angular/docs/partials/guide/directive.html
Normal file
694
lib/angular/docs/partials/guide/directive.html
Normal file
|
@ -0,0 +1,694 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>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.</p>
|
||||
|
||||
<p>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).</p>
|
||||
|
||||
<h2>Invoking directives from HTML</h2>
|
||||
|
||||
<p>Directives have camel cased names such as <code>ngBind</code>. The directive can be invoked by translating
|
||||
the camel case name into snake case with these special characters <code>:</code>, <code>-</code>, or <code>_</code>. Optionally the
|
||||
directive can be prefixed with <code>x-</code>, or <code>data-</code> to make it HTML validator compliant. Here is a
|
||||
list of some of the possible directive names: <code>ng:bind</code>, <code>ng-bind</code>, <code>ng_bind</code>, <code>x-ng-bind</code> and
|
||||
<code>data-ng-bind</code>.</p>
|
||||
|
||||
<p>The directives can be placed in element names, attributes, class names, as well as comments. Here
|
||||
are some equivalent examples of invoking <code>myDir</code>. (However, most directives are restricted to
|
||||
attribute only.)</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<span my-dir="exp"></span>
|
||||
<span class="my-dir: exp;"></span>
|
||||
<my-dir></my-dir>
|
||||
<!-- directive: my-dir exp -->
|
||||
</pre>
|
||||
|
||||
<p>Directives can be invoked in many different ways, but are equivalent in the end result as shown in
|
||||
the following example.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-42" source-edit-css="" source-edit-js="script.js-41" source-edit-unit="" source-edit-scenario="scenario.js-43"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-42" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-42">
|
||||
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-41"></pre>
|
||||
<script type="text/ng-template" id="script.js-41">
|
||||
function Ctrl1($scope) {
|
||||
$scope.name = 'angular';
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-43"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-43">
|
||||
it('should show off bindings', function() {
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text()).toBe('angular');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-42" ng-eval-javascript="script.js-41"></div>
|
||||
|
||||
<h2>String interpolation</h2>
|
||||
|
||||
<p>During the compilation process the <a href="api/ng.$compile"><code>compiler</code></a> matches text and
|
||||
attributes using the <a href="api/ng.$interpolate"><code>$interpolate</code></a> service to see if they
|
||||
contain embedded expressions. These expressions are registered as <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a> and will update as part of normal <a href="api/ng.$rootScope.Scope#$digest"><code>digest</code></a> cycle. An example of interpolation is shown
|
||||
here:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<a href="img/{{username}}.jpg">Hello {{username}}!</a>
|
||||
</pre>
|
||||
|
||||
<h2>Compilation process, and directive matching</h2>
|
||||
|
||||
<p>Compilation of HTML happens in three phases:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>First the HTML is parsed into DOM using the standard browser API. This is important to
|
||||
realize because the templates must be parsable HTML. This is in contrast to most templating
|
||||
systems that operate on strings, rather than on DOM elements.</p></li>
|
||||
<li><p>The compilation of the DOM is performed by the call to the <a href="api/ng.$compile"><code>$compile()</code></a> method. The method traverses the DOM and matches the directives. If a match is found
|
||||
it is added to the list of directives associated with the given DOM element. Once all directives
|
||||
for a given DOM element have been identified they are sorted by priority and their <code>compile()</code>
|
||||
functions are executed. The directive compile function has a chance to modify the DOM structure
|
||||
and is responsible for producing a <code>link()</code> function explained next. The <a href="api/ng.$compile"><code>$compile()</code></a> method returns a combined linking function, which is a
|
||||
collection of all of the linking functions returned from the individual directive compile
|
||||
functions.</p></li>
|
||||
<li><p>Link the template with scope by calling the linking function returned from the previous step.
|
||||
This in turn will call the linking function of the individual directives allowing them to
|
||||
register any listeners on the elements and set up any <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a> with the <a href="api/ng.$rootScope.Scope"><code>scope</code></a>. The result of this is a live binding between the
|
||||
scope and the DOM. A change in the scope is reflected in the DOM.</p></li>
|
||||
</ol>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
|
||||
var html = '<div ng-bind='exp'></div>';
|
||||
|
||||
// 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);
|
||||
</pre>
|
||||
|
||||
<h3>Reasons behind the compile/link separation</h3>
|
||||
|
||||
<p>At this point you may wonder why the compile process is broken down to a compile and link phase.
|
||||
To understand this, let's look at a real world example with a repeater:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
Hello {{user}}, you have these actions:
|
||||
<ul>
|
||||
<li ng-repeat="action in user.actions">
|
||||
{{action.description}}
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
<p>The short answer is that compile and link separation is needed any time a change in model causes
|
||||
a change in DOM structure such as in repeaters.</p>
|
||||
|
||||
<p>When the above example is compiled, the compiler visits every node and looks for directives. The
|
||||
<code>{{user}}</code> is an example of an <a href="api/ng.$interpolate"><code>interpolation</code></a> directive. <a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a> is another directive. But <a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a> has a dilemma. It needs to be
|
||||
able to quickly stamp out new <code>li</code>s for every <code>action</code> in <code>user.actions</code>. This means that it needs
|
||||
to save a clean copy of the <code>li</code> element for cloning purposes and as new <code>action</code>s are inserted,
|
||||
the template <code>li</code> element needs to be cloned and inserted into <code>ul</code>. But cloning the <code>li</code> element
|
||||
is not enough. It also needs to compile the <code>li</code> so that its directives such as
|
||||
<code>{{action.descriptions}}</code> evaluate against the right <a href="api/ng.$rootScope.Scope"><code>scope</code></a>. A naive method would be to simply insert a copy of the <code>li</code> element and then compile it.
|
||||
But compiling on every <code>li</code> element clone would be slow, since the compilation requires that we
|
||||
traverse the DOM tree and look for directives and execute them. If we put the compilation inside a
|
||||
repeater which needs to unroll 100 items we would quickly run into performance problems.</p>
|
||||
|
||||
<p>The solution is to break the compilation process into two phases; the compile phase where all of
|
||||
the directives are identified and sorted by priority, and a linking phase where any work which
|
||||
links a specific instance of the <a href="api/ng.$rootScope.Scope"><code>scope</code></a> and the specific
|
||||
instance of an <code>li</code> is performed.</p>
|
||||
|
||||
<p><a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a> works by preventing the
|
||||
compilation process from descending into the <code>li</code> element. Instead the <a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a> directive compiles <code>li</code>
|
||||
separately. The result of the <code>li</code> element compilation is a linking function which contains all
|
||||
of the directives contained in the <code>li</code> element, ready to be attached to a specific clone of the <code>li</code>
|
||||
element. At runtime the <a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a>
|
||||
watches the expression and as items are added to the array it clones the <code>li</code> element, creates a
|
||||
new <a href="api/ng.$rootScope.Scope"><code>scope</code></a> for the cloned <code>li</code> element and calls the
|
||||
link function on the cloned <code>li</code>.</p>
|
||||
|
||||
<p>Summary:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><em>compile function</em> - The compile function is relatively rare in directives, since most
|
||||
directives are concerned with working with a specific DOM element instance rather than
|
||||
transforming the template DOM element. Any operation which can be shared among the instance of
|
||||
directives should be moved to the compile function for performance reasons.</p></li>
|
||||
<li><p><em>link function</em> - It is rare for the directive not to 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.</p></li>
|
||||
</ul>
|
||||
|
||||
<h2>Writing directives (short version)</h2>
|
||||
|
||||
<p>In this example we will build a directive that displays the current time.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="time" source-edit-deps="angular.js script.js" source-edit-html="index.html-45" source-edit-css="" source-edit-js="script.js-44" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-45" ng-html-wrap="time angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-45">
|
||||
|
||||
<div ng-controller="Ctrl2">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-44"></pre>
|
||||
<script type="text/ng-template" id="script.js-44">
|
||||
function Ctrl2($scope) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
}
|
||||
|
||||
angular.module('time', [])
|
||||
// Register the 'myCurrentTime' directive factory method.
|
||||
// We inject $timeout and dateFilter service since the factory method is DI.
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
// return the directive link function. (compile function not needed)
|
||||
return function(scope, element, attrs) {
|
||||
var format, // date format
|
||||
timeoutId; // timeoutId, so that we can cancel the time updates
|
||||
|
||||
// used to update the UI
|
||||
function updateTime() {
|
||||
element.text(dateFilter(new Date(), format));
|
||||
}
|
||||
|
||||
// watch the expression, and update the UI on change.
|
||||
scope.$watch(attrs.myCurrentTime, function(value) {
|
||||
format = value;
|
||||
updateTime();
|
||||
});
|
||||
|
||||
// schedule update in one second
|
||||
function updateLater() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
updateLater(); // schedule another update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
updateLater(); // kick off the UI update process.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="time" ng-set-html="index.html-45" ng-eval-javascript="script.js-44"></div>
|
||||
|
||||
<h2>Writing directives (long version)</h2>
|
||||
|
||||
<p>An example skeleton of the directive is shown here, for the complete list see below.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
template: '<div></div>',
|
||||
templateUrl: 'directive.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
|
||||
post: function postLink(scope, iElement, iAttrs, controller) { ... }
|
||||
}
|
||||
},
|
||||
link: function postLink(scope, iElement, iAttrs) { ... }
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>In most cases you will not need such fine control and so the above can be simplified. All of the
|
||||
different parts of this skeleton are explained in following sections. In this section we are
|
||||
interested only in some of this skeleton.</p>
|
||||
|
||||
<p>The first step in simplyfing the code is to rely on the default values. Therefore the above can be
|
||||
simplified as:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
var directiveDefinitionObject = {
|
||||
compile: function compile(tElement, tAttrs) {
|
||||
return function postLink(scope, iElement, iAttrs) { ... }
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>Most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
return function postLink(scope, iElement, iAttrs) { ... }
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h3>Factory method</h3>
|
||||
|
||||
<p>The factory method is responsible for creating the directive. It is invoked only once, when the
|
||||
<a href="api/ng.$compile"><code>compiler</code></a> matches the directive for the first time. You can
|
||||
perform any initialization work here. The method is invoked using the <a href="api/AUTO.$injector#invoke"><code>$injector.invoke</code></a> which
|
||||
makes it injectable following all of the rules of injection annotation.</p>
|
||||
|
||||
<h3>Directive Definition Object</h3>
|
||||
|
||||
<p>The directive definition object provides instructions to the <a href="api/ng.$compile"><code>compiler</code></a>. The attributes are:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><code>name</code> - Name of the current scope. Optional and defaults to the name at registration.</p></li>
|
||||
<li><p><code>priority</code> - When there are multiple directives defined on a single DOM element, sometimes it
|
||||
is necessary to specify the order in which the directives are applied. The <code>priority</code> is used
|
||||
to sort the directives before their <code>compile</code> functions get called. Higher <code>priority</code> goes
|
||||
first. The order of directives within the same priority is undefined.</p></li>
|
||||
<li><p><code>terminal</code> - If set to true then the current <code>priority</code> will be the last set of directives
|
||||
which will execute (any directives at the current priority will still execute
|
||||
as the order of execution on same <code>priority</code> is undefined).</p></li>
|
||||
<li><p><code>scope</code> - If set to:</p>
|
||||
|
||||
<ul><li><p><code>true</code> - then a new scope will be created for this directive. If multiple directives on the
|
||||
same element request a new scope, only one new scope is created. The new scope rule does not
|
||||
apply for the root of the template since the root of the template always gets a new scope.</p></li>
|
||||
<li><p><code>{}</code> (object hash) - then a new 'isolate' scope is created. The 'isolate' scope differs from
|
||||
normal scope in that it does not prototypically inherit from the parent scope. This is useful
|
||||
when creating reusable components, which should not accidentally read or modify data in the
|
||||
parent scope. <br/>
|
||||
The 'isolate' scope takes an object hash which defines a set of local scope properties
|
||||
derived from the parent scope. These local properties are useful for aliasing values for
|
||||
templates. Locals definition is a hash of local scope property to its source:</p>
|
||||
|
||||
<ul><li><p><code>@</code> or <code>@attr</code> - bind a local scope property to the value of DOM attribute. The result is
|
||||
always a string since DOM attributes are strings. If no <code>attr</code> name is specified then the
|
||||
attribute name is assumed to be the same as the local name.
|
||||
Given <code><widget my-attr="hello {{name}}"></code> and widget definition
|
||||
of <code>scope: { localName:'@myAttr' }</code>, then widget scope property <code>localName</code> will reflect
|
||||
the interpolated value of <code>hello {{name}}</code>. As the <code>name</code> attribute changes so will the
|
||||
<code>localName</code> property on the widget scope. The <code>name</code> is read from the parent scope (not
|
||||
component scope).</p></li>
|
||||
<li><p><code>=</code> or <code>=attr</code> - set up bi-directional binding between a local scope property and the
|
||||
parent scope property of name defined via the value of the <code>attr</code> attribute. If no <code>attr</code>
|
||||
name is specified then the attribute name is assumed to be the same as the local name.
|
||||
Given <code><widget my-attr="parentModel"></code> and widget definition of
|
||||
<code>scope: { localModel:'=myAttr' }</code>, then widget scope property <code>localModel</code> will reflect the
|
||||
value of <code>parentModel</code> on the parent scope. Any changes to <code>parentModel</code> will be reflected
|
||||
in <code>localModel</code> and any changes in <code>localModel</code> will reflect in <code>parentModel</code>.</p></li>
|
||||
<li><p><code>&</code> or <code>&attr</code> - provides a way to execute an expression in the context of the parent scope.
|
||||
If no <code>attr</code> name is specified then the attribute name is assumed to be the same as the
|
||||
local name. Given <code><widget my-attr="count = count + value"></code> and widget definition of
|
||||
<code>scope: { localFn:'&myAttr' }</code>, then isolate scope property <code>localFn</code> will point to
|
||||
a function wrapper for the <code>count = count + value</code> expression. Often it's desirable to
|
||||
pass data from the isolated scope via an expression and to the parent scope, this can be
|
||||
done by passing a map of local variable names and values into the expression wrapper fn.
|
||||
For example, if the expression is <code>increment(amount)</code> then we can specify the amount value
|
||||
by calling the <code>localFn</code> as <code>localFn({amount: 22})</code>.</p></li></ul></li></ul></li>
|
||||
<li><p><code>controller</code> - Controller constructor function. The controller is instantiated before the
|
||||
pre-linking phase and it is shared with other directives if they request it by name (see
|
||||
<code>require</code> attribute). This allows the directives to communicate with each other and augment
|
||||
each other's behavior. The controller is injectable with the following locals:</p>
|
||||
|
||||
<ul><li><code>$scope</code> - Current scope associated with the element</li>
|
||||
<li><code>$element</code> - Current element</li>
|
||||
<li><code>$attrs</code> - Current attributes obeject for the element</li>
|
||||
<li><code>$transclude</code> - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
<code>function(cloneLinkingFn)</code>.</li></ul></li>
|
||||
<li><p><code>require</code> - Require another controller be passed into current directive linking function. The
|
||||
<code>require</code> takes a name of the directive controller to pass in. If no such controller can be
|
||||
found an error is raised. The name can be prefixed with:</p>
|
||||
|
||||
<ul><li><code>?</code> - Don't raise an error. This makes the require dependency optional.</li>
|
||||
<li><code>^</code> - Look for the controller on parent elements as well.</li></ul></li>
|
||||
<li><p><code>restrict</code> - String of subset of <code>EACM</code> which restricts the directive to a specific directive
|
||||
declaration style. If omitted directives are allowed on attributes only.</p>
|
||||
|
||||
<ul><li><code>E</code> - Element name: <code><my-directive></my-directive></code></li>
|
||||
<li><code>A</code> - Attribute: <code><div my-directive="exp">
|
||||
</div></code></li>
|
||||
<li><code>C</code> - Class: <code><div class="my-directive: exp;"></div></code></li>
|
||||
<li><code>M</code> - Comment: <code><!-- directive: my-directive exp --></code></li></ul></li>
|
||||
<li><p><code>template</code> - replace the current element with the contents of the HTML. The replacement process
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.</p></li>
|
||||
<li><p><code>templateUrl</code> - Same as <code>template</code> but the template is loaded from the specified URL. Because
|
||||
the template loading is asynchronous the compilation/linking is suspended until the template
|
||||
is loaded.</p></li>
|
||||
<li><p><code>replace</code> - if set to <code>true</code> then the template will replace the current element, rather than
|
||||
append the template to the element.</p></li>
|
||||
<li><p><code>transclude</code> - compile the content of the element and make it available to the directive.
|
||||
Typically used with <a href="api/ng.directive:ngTransclude"><code>ngTransclude</code></a>. The advantage of transclusion is that the linking function receives a
|
||||
transclusion function which is pre-bound to the correct scope. In a typical setup the widget
|
||||
creates an <code>isolate</code> scope, but the transclusion is not a child, but a sibling of the <code>isolate</code>
|
||||
scope. This makes it possible for the widget to have private state, and the transclusion to
|
||||
be bound to the parent (pre-<code>isolate</code>) scope.</p>
|
||||
|
||||
<ul><li><code>true</code> - transclude the content of the directive.</li>
|
||||
<li><code>'element'</code> - transclude the whole element including any directives defined at lower priority.</li></ul></li>
|
||||
<li><p><code>compile</code>: This is the compile function described in the section below.</p></li>
|
||||
<li><p><code>link</code>: This is the link function described in the section below. This property is used only
|
||||
if the <code>compile</code> property is not defined.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Compile function</h3>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function compile(tElement, tAttrs, transclude) { ... }
|
||||
</pre>
|
||||
|
||||
<p>The compile function deals with transforming the template DOM. Since most directives do not do
|
||||
template transformation, it is not used often. Examples that require compile functions are
|
||||
directives that transform template DOM, such as <a href="api/ng.directive:ngRepeat"><code>ngRepeat</code></a>, or load the contents
|
||||
asynchronously, such as <a href="api/ng.directive:ngView"><code>ngView</code></a>. The
|
||||
compile function takes the following arguments.</p>
|
||||
|
||||
<ul>
|
||||
<li><p><code>tElement</code> - template element - The element where the directive has been declared. It is
|
||||
safe to do template transformation on the element and child elements only.</p></li>
|
||||
<li><p><code>tAttrs</code> - template attributes - Normalized list of attributes declared on this element shared
|
||||
between all directive compile functions. See <a href="guide/directive#Attributes">Attributes</a>.</p></li>
|
||||
<li><p><code>transclude</code> - A transclude linking function: <code>function(scope, cloneLinkingFn)</code>.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>NOTE: The template instance and the link instance may not be the same objects if the template has
|
||||
been cloned. For this reason it is not safe in the compile function to do anything other than DOM
|
||||
transformation that applies to all DOM clones. Specifically, DOM listener registration should be
|
||||
done in a linking function rather than in a compile function.</p>
|
||||
|
||||
<p>A compile function can have a return value which can be either a function or an object.</p>
|
||||
|
||||
<ul>
|
||||
<li><p>returning a function - is equivalent to registering the linking function via the <code>link</code> property
|
||||
of the config object when the compile function is empty.</p></li>
|
||||
<li><p>returning an object with function(s) registered via <code>pre</code> and <code>post</code> properties - allows you to
|
||||
control when a linking function should be called during the linking phase. See info about
|
||||
pre-linking and post-linking functions below.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Linking function</h3>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function link(scope, iElement, iAttrs, controller) { ... }
|
||||
</pre>
|
||||
|
||||
<p>The link function is responsible for registering DOM listeners as well as updating the DOM. It is
|
||||
executed after the template has been cloned. This is where most of the directive logic will be
|
||||
put.</p>
|
||||
|
||||
<ul>
|
||||
<li><p><code>scope</code> - <a href="api/ng.$rootScope.Scope"><code>Scope</code></a> - The scope to be used by the
|
||||
directive for registering <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a>.</p></li>
|
||||
<li><p><code>iElement</code> - instance element - The element where the directive is to be used. It is safe to
|
||||
manipulate the children of the element only in <code>postLink</code> function since the children have
|
||||
already been linked.</p></li>
|
||||
<li><p><code>iAttrs</code> - instance attributes - Normalized list of attributes declared on this element shared
|
||||
between all directive linking functions. See <a href="guide/directive#Attributes">Attributes</a>.</p></li>
|
||||
<li><p><code>controller</code> - a controller instance - A controller instance if at least one directive on the
|
||||
element defines a controller. The controller is shared among all the directives, which allows
|
||||
the directives to use the controllers as a communication channel.</p></li>
|
||||
</ul>
|
||||
|
||||
<h4>Pre-linking function</h4>
|
||||
|
||||
<p>Executed before the child elements are linked. Not safe to do DOM transformation since the
|
||||
compiler linking function will fail to locate the correct elements for linking.</p>
|
||||
|
||||
<h4>Post-linking function</h4>
|
||||
|
||||
<p>Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.</p>
|
||||
|
||||
<p><a name="Attributes"></a></p>
|
||||
|
||||
<h3>Attributes</h3>
|
||||
|
||||
<p>The <a href="api/ng.$compile.directive.Attributes"><code>Attributes</code></a> object - passed as a parameter in the
|
||||
link() or compile() functions - is a way of accessing:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><em>normalized attribute names:</em> Since a directive such as 'ngBind' can be expressed in many ways
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
|
||||
the attributes.</p></li>
|
||||
<li><p><em>directive inter-communication:</em> All directives share the same instance of the attributes
|
||||
object which allows the directives to use the attributes object as inter directive
|
||||
communication.</p></li>
|
||||
<li><p><em>supports interpolation:</em> Interpolation attributes are assigned to the attribute object
|
||||
allowing other directives to read the interpolated value.</p></li>
|
||||
<li><p><em>observing interpolated attributes:</em> Use <code>$observe</code> to observe the value changes of attributes
|
||||
that contain interpolation (e.g. <code>src="{{bar}}"</code>). Not only is this very efficient but it's also
|
||||
the only way to easily get the actual value because during the linking phase the interpolation
|
||||
hasn't been evaluated yet and so the value is at this time set to <code>undefined</code>.</p></li>
|
||||
</ul>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function linkingFn(scope, elm, attrs, ctrl) {
|
||||
// get the attribute value
|
||||
console.log(attrs.ngModel);
|
||||
|
||||
// change the attribute
|
||||
attrs.$set('ngModel', 'new value');
|
||||
|
||||
// observe changes to interpolated attribute
|
||||
attrs.$observe('ngModel', function(value) {
|
||||
console.log('ngModel has changed value to ' + value);
|
||||
});
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Understanding Transclusion and Scopes</h2>
|
||||
|
||||
<p>It is often desirable to have reusable components. Below is a pseudo code showing how a simplified
|
||||
dialog component may work.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<div>
|
||||
<button ng-click="show=true">show</button>
|
||||
<dialog title="Hello {{username}}."
|
||||
visible="show"
|
||||
on-cancel="show = false"
|
||||
on-ok="show = false; doSomething()">
|
||||
Body goes here: {{username}} is {{title}}.
|
||||
</dialog>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
<p>Clicking on the "show" button will open the dialog. The dialog will have a title, which is
|
||||
data bound to <code>username</code>, and it will also have a body which we would like to transclude
|
||||
into the dialog.</p>
|
||||
|
||||
<p>Here is an example of what the template definition for the <code>dialog</code> widget may look like.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<div ng-show="visible">
|
||||
<h3>{{title}}</h3>
|
||||
<div class="body" ng-transclude></div>
|
||||
<div class="footer">
|
||||
<button ng-click="onOk()">Save changes</button>
|
||||
<button ng-click="onCancel()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
<p>This will not render properly, unless we do some scope magic.</p>
|
||||
|
||||
<p>The first issue we have to solve is that the dialog box template expects <code>title</code> to be defined, but
|
||||
the place of instantiation would like to bind to <code>username</code>. Furthermore the buttons expect the
|
||||
<code>onOk</code> and <code>onCancel</code> functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the <code>locals</code> to create local variables which the template
|
||||
expects as follows:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
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
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Creating local properties on widget scope creates two problems:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>isolation - if the user forgets to set <code>title</code> attribute of the dialog widget the dialog
|
||||
template will bind to parent scope property. This is unpredictable and undesirable.</p></li>
|
||||
<li><p>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 <code>title</code>
|
||||
property of the widget clobbers the <code>title</code> property of the transclusion.</p></li>
|
||||
</ol>
|
||||
|
||||
<p>To solve the issue of lack of isolation, the directive declares a new <code>isolated</code> 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.</p>
|
||||
|
||||
<p>However <code>isolated</code> 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.</p>
|
||||
|
||||
<p>This may seem to be unexpected complexity, but it gives the widget user and developer the least
|
||||
surprise.</p>
|
||||
|
||||
<p>Therefore the final directive definition looks something like this:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
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
|
||||
</pre>
|
||||
|
||||
<h2>Creating Components</h2>
|
||||
|
||||
<p>It is often desirable to replace a single directive with a more complex DOM structure. This
|
||||
allows the directives to become a short hand for reusable components from which applications
|
||||
can be built.</p>
|
||||
|
||||
<p>Following is an example of building a reusable widget.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="zippyModule" source-edit-deps="angular.js script.js" source-edit-html="index.html-48" source-edit-css="style.css-47" source-edit-js="script.js-46" source-edit-unit="" source-edit-scenario="scenario.js-49"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-48" ng-html-wrap="zippyModule angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-48">
|
||||
|
||||
|
||||
<div ng-controller="Ctrl3">
|
||||
Title: <input ng-model="title"> <br>
|
||||
Text: <textarea ng-model="text"></textarea>
|
||||
<hr>
|
||||
<div class="zippy" zippy-title="Details: {{title}}...">{{text}}</div>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="style.css">
|
||||
<pre class="prettyprint linenums" ng-set-text="style.css-47"></pre>
|
||||
<style type="text/css" id="style.css-47">
|
||||
.zippy {
|
||||
border: 1px solid black;
|
||||
display: inline-block;
|
||||
width: 250px;
|
||||
}
|
||||
.zippy.opened > .title:before { content: '▼ '; }
|
||||
.zippy.opened > .body { display: block; }
|
||||
.zippy.closed > .title:before { content: '► '; }
|
||||
.zippy.closed > .body { display: none; }
|
||||
.zippy > .title {
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: .1em .3em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.zippy > .body {
|
||||
padding: .1em .3em;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-46"></pre>
|
||||
<script type="text/ng-template" id="script.js-46">
|
||||
function Ctrl3($scope) {
|
||||
$scope.title = 'Lorem Ipsum';
|
||||
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
|
||||
}
|
||||
|
||||
angular.module('zippyModule', [])
|
||||
.directive('zippy', function(){
|
||||
return {
|
||||
restrict: 'C',
|
||||
// This HTML will replace the zippy directive.
|
||||
replace: true,
|
||||
transclude: true,
|
||||
scope: { title:'@zippyTitle' },
|
||||
template: '<div>' +
|
||||
'<div class="title">{{title}}</div>' +
|
||||
'<div class="body" ng-transclude></div>' +
|
||||
'</div>',
|
||||
// The linking function will add behavior to the template
|
||||
link: function(scope, element, attrs) {
|
||||
// Title element
|
||||
var title = angular.element(element.children()[0]),
|
||||
// Opened / closed state
|
||||
opened = true;
|
||||
|
||||
// Clicking on title should open/close the zippy
|
||||
title.bind('click', toggle);
|
||||
|
||||
// Toggle the closed/opened state
|
||||
function toggle() {
|
||||
opened = !opened;
|
||||
element.removeClass(opened ? 'closed' : 'opened');
|
||||
element.addClass(opened ? 'opened' : 'closed');
|
||||
}
|
||||
|
||||
// initialize the zippy
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-49"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-49">
|
||||
it('should bind and open / close', function() {
|
||||
input('title').enter('TITLE');
|
||||
input('text').enter('TEXT');
|
||||
expect(element('.title').text()).toEqual('Details: TITLE...');
|
||||
expect(binding('text')).toEqual('TEXT');
|
||||
|
||||
expect(element('.zippy').prop('className')).toMatch(/closed/);
|
||||
element('.zippy > .title').click();
|
||||
expect(element('.zippy').prop('className')).toMatch(/opened/);
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="zippyModule" ng-set-html="index.html-48" ng-eval-javascript="script.js-46"></div></div>
|
216
lib/angular/docs/partials/guide/expression.html
Normal file
216
lib/angular/docs/partials/guide/expression.html
Normal file
|
@ -0,0 +1,216 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Expressions are JavaScript-like code snippets that are usually placed in bindings such as <code>{{
|
||||
expression }}</code>. Expressions are processed by <a href="api/ng.$parse"><code>$parse</code></a>
|
||||
service.</p>
|
||||
|
||||
<p>For example, these are all valid expressions in angular:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>1+2</code></li>
|
||||
<li><code>3*10 | currency</code></li>
|
||||
<li><code>user.name</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Angular Expressions vs. JS Expressions</h3>
|
||||
|
||||
<p>It might be tempting to think of Angular view expressions as JavaScript expressions, but that is
|
||||
not entirely correct, since Angular does not use a JavaScript <code>eval()</code> to evaluate expressions.
|
||||
You can think of Angular expressions as JavaScript expressions with following differences:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><strong>Attribute Evaluation:</strong> evaluation of all properties are against the scope, doing the
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
<code>window</code>.</p></li>
|
||||
<li><p><strong>Forgiving:</strong> expression evaluation is forgiving to undefined and null, unlike in JavaScript,
|
||||
where such evaluations generate <code>NullPointerExceptions</code>.</p></li>
|
||||
<li><p><strong>No Control Flow Statements:</strong> you cannot do any of the following in angular expression:
|
||||
conditionals, loops, or throw.</p></li>
|
||||
<li><p><strong>Filters:</strong> you can pass result of expression evaluations through filter chains. For example
|
||||
to convert date object into a local specific human-readable format.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
|
||||
controller method and call the method. If you want to <code>eval()</code> an angular expression from
|
||||
JavaScript, use the <a href="api/ng.$rootScope.Scope#$eval"><code><code>$eval()</code></code></a> method.</p>
|
||||
|
||||
<h3>Example</h3>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js" source-edit-html="index.html-50" source-edit-css="" source-edit-js="" source-edit-unit="" source-edit-scenario="scenario.js-51"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-50" ng-html-wrap=" angular.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-50">
|
||||
1+2={{1+2}}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-51"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-51">
|
||||
it('should calculate expression in binding', function() {
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-50" ng-eval-javascript=""></div>
|
||||
|
||||
<p>You can try evaluating different expressions here:</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-53" source-edit-css="" source-edit-js="script.js-52" source-edit-unit="" source-edit-scenario="scenario.js-54"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-53" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-53">
|
||||
|
||||
<div ng-controller="Cntl2" class="expressions">
|
||||
Expression:
|
||||
<input type='text' ng-model="expr" size="80"/>
|
||||
<button ng-click="addExp(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng-repeat="expr in exprs">
|
||||
[ <a href="" ng-click="removeExp($index)">X</a> ]
|
||||
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-52"></pre>
|
||||
<script type="text/ng-template" id="script.js-52">
|
||||
function Cntl2($scope) {
|
||||
var exprs = $scope.exprs = [];
|
||||
$scope.expr = '3*10|currency';
|
||||
$scope.addExp = function(expr) {
|
||||
exprs.push(expr);
|
||||
};
|
||||
|
||||
$scope.removeExp = function(index) {
|
||||
exprs.splice(index, 1);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-54"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-54">
|
||||
it('should allow user expression testing', function() {
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-53" ng-eval-javascript="script.js-52"></div>
|
||||
|
||||
<h2>Property Evaluation</h2>
|
||||
|
||||
<p>Evaluation of all properties takes place against a scope. Unlike JavaScript, where names default
|
||||
to global window properties, Angular expressions have to use <a href="api/ng.$window"><code><code>$window</code></code></a> to refer to the global <code>window</code> object. For example, if you want to call <code>alert()</code>, which is
|
||||
defined on <code>window</code>, in an expression you must use <code>$window.alert()</code>. This is done intentionally to
|
||||
prevent accidental access to the global state (a common source of subtle bugs).</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-56" source-edit-css="" source-edit-js="script.js-55" source-edit-unit="" source-edit-scenario="scenario.js-57"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-56" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-56">
|
||||
|
||||
<div class="example2" ng-controller="Cntl1">
|
||||
Name: <input ng-model="name" type="text"/>
|
||||
<button ng-click="greet()">Greet</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-55"></pre>
|
||||
<script type="text/ng-template" id="script.js-55">
|
||||
function Cntl1($window, $scope){
|
||||
$scope.name = 'World';
|
||||
|
||||
$scope.greet = function() {
|
||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-57"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-57">
|
||||
it('should calculate expression in binding', function() {
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-56" ng-eval-javascript="script.js-55"></div>
|
||||
|
||||
<h3>Forgiving</h3>
|
||||
|
||||
<p>Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating <code>a.b.c</code> throws
|
||||
an exception if <code>a</code> is not an object. While this makes sense for a general purpose language, the
|
||||
expression evaluations are primarily used for data binding, which often look like this:</p>
|
||||
|
||||
<pre><code> {{a.b.c}}
|
||||
</code></pre>
|
||||
|
||||
<p>It makes more sense to show nothing than to throw an exception if <code>a</code> is undefined (perhaps we are
|
||||
waiting for the server response, and it will become defined soon). If expression evaluation wasn't
|
||||
forgiving we'd have to write bindings that clutter the code, for example: <code>{{((a||{}).b||{}).c}}</code></p>
|
||||
|
||||
<p>Similarly, invoking a function <code>a.b.c()</code> on undefined or null simply returns undefined.</p>
|
||||
|
||||
<h3>No Control Flow Statements</h3>
|
||||
|
||||
<p>You cannot write a control flow statement in an expression. The reason behind this is core to the
|
||||
Angular philosophy that application logic should be in controllers, not in the view. If you need a
|
||||
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.</p>
|
||||
|
||||
<h3>Filters</h3>
|
||||
|
||||
<p>When presenting data to the user, you might need to convert the data from its raw format to a
|
||||
user-friendly format. For example, you might have a data object that needs to be formatted
|
||||
according to the locale before displaying it to the user. You can pass expressions through a chain
|
||||
of filters like this:</p>
|
||||
|
||||
<pre><code> name | uppercase
|
||||
</code></pre>
|
||||
|
||||
<p>The expression evaluator simply passes the value of name to <a href="api/ng.filter:uppercase"><code><code>uppercase</code></code></a> filter.</p>
|
||||
|
||||
<p>Chain filters using this syntax:</p>
|
||||
|
||||
<pre><code> value | filter1 | filter2
|
||||
</code></pre>
|
||||
|
||||
<p>You can also pass colon-delimited arguments to filters, for example, to display the number 123
|
||||
with 2 decimal points:</p>
|
||||
|
||||
<pre><code> 123 | number:2
|
||||
</code></pre>
|
||||
|
||||
<h2>The $</h2>
|
||||
|
||||
<p>You might be wondering, what is the significance of the $ prefix? It is simply a prefix that
|
||||
angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating
|
||||
<code>a.length()</code> would return undefined because neither a nor angular define such a property.</p>
|
||||
|
||||
<p>Consider that in a future version of Angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you the developer could create a length
|
||||
property and then we would have a collision. This problem exists because Angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use Angular can develop in harmony without collisions.</p></div>
|
368
lib/angular/docs/partials/guide/forms.html
Normal file
368
lib/angular/docs/partials/guide/forms.html
Normal file
|
@ -0,0 +1,368 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Controls (<code>input</code>, <code>select</code>, <code>textarea</code>) are a way for user to enter data.
|
||||
Form is a collection of controls for the purpose of grouping related controls together.</p>
|
||||
|
||||
<p>Form and controls provide validation services, so that the user can be notified of invalid input.
|
||||
This provides a better user experience, because the user gets instant feedback on how to correct the error.
|
||||
Keep in mind that while client-side validation plays an important role in providing good user experience, it can easily be circumvented and thus can not be trusted.
|
||||
Server-side validation is still necessary for a secure application.</p>
|
||||
|
||||
<h2>Simple form</h2>
|
||||
|
||||
<p>The key directive in understanding two-way data-binding is <a href="api/ng.directive:ngModel"><code>ngModel</code></a>.
|
||||
The <code>ngModel</code> directive provides the two-way data-binding by synchronizing the model to the view, as well as view to the model.
|
||||
In addition it provides an <a href="api/ng.directive:ngModel.NgModelController"><code>API</code></a> for other directives to augment its behavior.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-59" source-edit-css="" source-edit-js="script.js-58" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-59" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-59">
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="simple-form">
|
||||
Name: <input type="text" ng-model="user.name" /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
<pre>form = {{user | json}}</pre>
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-58"></pre>
|
||||
<script type="text/ng-template" id="script.js-58">
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-59" ng-eval-javascript="script.js-58"></div>
|
||||
|
||||
<p>Note that <code>novalidate</code> is used to disable browser's native form validation.</p>
|
||||
|
||||
<h2>Using CSS classes</h2>
|
||||
|
||||
<p>To allow styling of form as well as controls, <code>ngModel</code> add these CSS classes:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>ng-valid</code></li>
|
||||
<li><code>ng-invalid</code></li>
|
||||
<li><code>ng-pristine</code></li>
|
||||
<li><code>ng-dirty</code></li>
|
||||
</ul>
|
||||
|
||||
<p>The following example uses the CSS to display validity of each form control.
|
||||
In the example both <code>user.name</code> and <code>user.email</code> are required, but are rendered with red background only when they are dirty.
|
||||
This ensures that the user is not distracted with an error until after interacting with the control, and failing to satisfy its validity.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-61" source-edit-css="" source-edit-js="script.js-60" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-61" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-61">
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="css-form">
|
||||
Name:
|
||||
<input type="text" ng-model="user.name" required /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" required /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.css-form input.ng-invalid.ng-dirty {
|
||||
background-color: #FA787E;
|
||||
}
|
||||
|
||||
.css-form input.ng-valid.ng-dirty {
|
||||
background-color: #78FA89;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-60"></pre>
|
||||
<script type="text/ng-template" id="script.js-60">
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-61" ng-eval-javascript="script.js-60"></div>
|
||||
|
||||
<h2>Binding to form and control state</h2>
|
||||
|
||||
<p>A form is in instance of <a href="api/ng.directive:form.FormController"><code>FormController</code></a>.
|
||||
The form instance can optionally be published into the scope using the <code>name</code> attribute.
|
||||
Similarly control is an instance of <a href="api/ng.directive:ngModel.NgModelController"><code>NgModelController</code></a>.
|
||||
The control instance can similarly be published into the form instance using the <code>name</code> attribute.
|
||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.</p>
|
||||
|
||||
<p>This allows us to extend the above example with these features:</p>
|
||||
|
||||
<ul>
|
||||
<li>RESET button is enabled only if form has some changes</li>
|
||||
<li>SAVE button is enabled only if form has some changes and is valid</li>
|
||||
<li>custom error messages for <code>user.email</code> and <code>user.agree</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-63" source-edit-css="" source-edit-js="script.js-62" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-63" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-63">
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
Name:
|
||||
<input type="text" ng-model="user.name" name="uName" required /><br />
|
||||
E-mail:
|
||||
<input type="email" ng-model="user.email" name="uEmail" required/><br />
|
||||
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
|
||||
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
|
||||
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
|
||||
</div>
|
||||
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
|
||||
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
|
||||
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
|
||||
required /><br />
|
||||
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
|
||||
|
||||
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
|
||||
<button ng-click="update(user)"
|
||||
ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-62"></pre>
|
||||
<script type="text/ng-template" id="script.js-62">
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.isUnchanged = function(user) {
|
||||
return angular.equals(user, $scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-63" ng-eval-javascript="script.js-62"></div>
|
||||
|
||||
<h2>Custom Validation</h2>
|
||||
|
||||
<p>Angular provides basic implementation for most common html5 <a href="api/ng.directive:input"><code>input</code></a>
|
||||
types: (<a href="api/ng.directive:input.text"><code>text</code></a>, <a href="api/ng.directive:input.number"><code>number</code></a>, <a href="api/ng.directive:input.url"><code>url</code></a>, <a href="api/ng.directive:input.email"><code>email</code></a>, <a href="api/ng.directive:input.radio"><code>radio</code></a>, <a href="api/ng.directive:input.checkbox"><code>checkbox</code></a>), as well as some directives for validation (<code>required</code>, <code>pattern</code>, <code>minlength</code>, <code>maxlength</code>, <code>min</code>, <code>max</code>).</p>
|
||||
|
||||
<p>Defining your own validator can be done by defining your own directive which adds a custom validation function to the <code>ngModel</code> <a href="api/ng.directive:ngModel.NgModelController"><code>controller</code></a>.
|
||||
To get a hold of the controller the directive specifies a dependency as shown in the example below.
|
||||
The validation can occur in two places:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><strong>Model to View update</strong> -
|
||||
Whenever the bound model changes, all functions in <a href="api/ng.directive:ngModel.NgModelController#$formatters"><code>NgModelController#$formatters</code></a> array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through <a href="api/ng.directive:ngModel.NgModelController#$setValidity"><code>NgModelController#$setValidity</code></a>.</p></li>
|
||||
<li><p><strong>View to Model update</strong> -
|
||||
In a similar way, whenever a user interacts with a control it calls <a href="api/ng.directive:ngModel.NgModelController#$setViewValue"><code>NgModelController#$setViewValue</code></a>.
|
||||
This in turn pipelines all functions in the <a href="api/ng.directive:ngModel.NgModelController#$parsers"><code>NgModelController#$parsers</code></a> array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through <a href="api/ng.directive:ngModel.NgModelController#$setValidity"><code>NgModelController#$setValidity</code></a>.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>In the following example we create two directives.</p>
|
||||
|
||||
<ul>
|
||||
<li><p>The first one is <code>integer</code> and it validates whether the input is a valid integer.
|
||||
For example <code>1.23</code> is an invalid value, since it contains a fraction.
|
||||
Note that we unshift the array instead of pushing.
|
||||
This is because we want to be first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.</p></li>
|
||||
<li><p>The second directive is a <code>smart-float</code>.
|
||||
It parses both <code>1.2</code> and <code>1,2</code> into a valid float number <code>1.2</code>.
|
||||
Note that we can't use input type <code>number</code> here as HTML5 browsers would not allow the user to type what it would consider an invalid number such as <code>1,2</code>.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="form-example1" source-edit-deps="angular.js script.js" source-edit-html="index.html-65" source-edit-css="" source-edit-js="script.js-64" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-65" ng-html-wrap="form-example1 angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-65">
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-64"></pre>
|
||||
<script type="text/ng-template" id="script.js-64">
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
app.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (INTEGER_REGEXP.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('integer', true);
|
||||
return viewValue;
|
||||
} else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('integer', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
|
||||
app.directive('smartFloat', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (FLOAT_REGEXP.test(viewValue)) {
|
||||
ctrl.$setValidity('float', true);
|
||||
return parseFloat(viewValue.replace(',', '.'));
|
||||
} else {
|
||||
ctrl.$setValidity('float', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="form-example1" ng-set-html="index.html-65" ng-eval-javascript="script.js-64"></div>
|
||||
|
||||
<h2>Implementing custom form controls (using <code>ngModel</code>)</h2>
|
||||
|
||||
<p>Angular implements all of the basic HTML form controls (<a href="api/ng.directive:input"><code>input</code></a>, <a href="api/ng.directive:select"><code>select</code></a>, <a href="api/ng.directive:textarea"><code>textarea</code></a>), which should be sufficient for most cases.
|
||||
However, if you need more flexibility, you can write your own form control as a directive.</p>
|
||||
|
||||
<p>In order for custom control to work with <code>ngModel</code> and to achieve two-way data-binding it needs to:</p>
|
||||
|
||||
<ul>
|
||||
<li>implement <code>render</code> method, which is responsible for rendering the data after it passed the <a href="api/ng.directive:ngModel.NgModelController#$formatters"><code>NgModelController#$formatters</code></a>,</li>
|
||||
<li>call <code>$setViewValue</code> method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.</li>
|
||||
</ul>
|
||||
|
||||
<p>See <a href="guide/directive">$compileProvider.directive</a> for more info.</p>
|
||||
|
||||
<p>The following example shows how to add two-way data-binding to contentEditable elements.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="form-example2" source-edit-deps="angular.js script.js" source-edit-html="index.html-67" source-edit-css="" source-edit-js="script.js-66" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-67" ng-html-wrap="form-example2 angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-67">
|
||||
|
||||
|
||||
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
|
||||
<pre>model = {{content}}</pre>
|
||||
|
||||
<style type="text/css">
|
||||
div[contentEditable] {
|
||||
cursor: pointer;
|
||||
background-color: #D0D0D0;
|
||||
}
|
||||
</style>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-66"></pre>
|
||||
<script type="text/ng-template" id="script.js-66">
|
||||
angular.module('form-example2', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.bind('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
});
|
||||
|
||||
// model -> view
|
||||
ctrl.$render = function() {
|
||||
elm.html(ctrl.$viewValue);
|
||||
};
|
||||
|
||||
// load init value from DOM
|
||||
ctrl.$setViewValue(elm.html());
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="form-example2" ng-set-html="index.html-67" ng-eval-javascript="script.js-66"></div></div>
|
113
lib/angular/docs/partials/guide/i18n.html
Normal file
113
lib/angular/docs/partials/guide/i18n.html
Normal file
|
@ -0,0 +1,113 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>I18n and L10n in AngularJS</h2>
|
||||
|
||||
<p><strong>What is i18n and l10n?</strong></p>
|
||||
|
||||
<p>Internationalization, abbreviated i18n, is the process of developing products in such a way that
|
||||
they can be localized for languages and cultures easily. Localization, abbreviated l10n, is the
|
||||
process of adapting applications and text to enable their usability in a particular cultural or
|
||||
linguistic market. For application developers, internationalizing an application means abstracting
|
||||
all of the strings and other locale-specific bits (such as date or currency formats) out of the
|
||||
application. Localizing an application means providing translations and localized formats for the
|
||||
abstracted bits.</p>
|
||||
|
||||
<p><strong>What level of support for i18n/l10n is currently in Angular?</strong></p>
|
||||
|
||||
<p>Currently, Angular supports i18n/l10n for <a href="http://docs.angularjs.org/#!/api/ng.filter:date">datetime</a>, <a href="http://docs.angularjs.org/#!/api/ng.filter:number">number</a> and <a href="http://docs.angularjs.org/#!/api/ng.filter:currency">currency</a> filters.</p>
|
||||
|
||||
<p>Additionally, Angular supports localizable pluralization support provided by the <a href="api/ng.directive:ngPluralize"><code>ngPluralize directive</code></a>.</p>
|
||||
|
||||
<p>All localizable Angular components depend on locale-specific rule sets managed by the <a href="api/ng.$locale"><code>$locale service</code></a>.</p>
|
||||
|
||||
<p>For readers who want to jump straight into examples, we have a few web pages that showcase how to
|
||||
use Angular filters with various locale rule sets. You can find these examples either on <a href="https://github.com/angular/angular.js/tree/master/i18n/e2e">Github</a> or in the i18n/e2e folder of
|
||||
Angular development package.</p>
|
||||
|
||||
<p><strong>What is a locale id?</strong></p>
|
||||
|
||||
<p>A locale is a specific geographical, political, or cultural region. The most commonly used locale
|
||||
ID consists of two parts: language code and country code. For example, en-US, en-AU, zh-CN are all
|
||||
valid locale IDs that have both language codes and country codes. Because specifying a country code
|
||||
in locale ID is optional, locale IDs such as en, zh, and sk are also valid. See the <a href="http://userguide.icu-project.org/locale">ICU</a> website for more information about using locale IDs.</p>
|
||||
|
||||
<p><strong>Supported locales in Angular</strong>
|
||||
Angular separates number and datetime format rule sets into different files, each file for a
|
||||
particular locale. You can find a list of currently supported locales <a href="https://github.com/angular/angular.js/tree/master/i18n/locale">here</a></p>
|
||||
|
||||
<h2>Providing locale rules to Angular</h2>
|
||||
|
||||
<p>There are two approaches to providing locale rules to Angular:</p>
|
||||
|
||||
<p><strong>1. Pre-bundled rule sets</strong></p>
|
||||
|
||||
<p>You can pre-bundle the desired locale file with Angular by concatenating the content of the
|
||||
locale-specific file to the end of <code>angular.js</code> or <code>angular.min.js</code> file.</p>
|
||||
|
||||
<p>For example on *nix, to create a an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:</p>
|
||||
|
||||
<p><code>cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js</code></p>
|
||||
|
||||
<p>When the application containing <code>angular_de-ge.js</code> script instead of the generic angular.js script
|
||||
starts, Angular is automatically pre-configured with localization rules for the german locale.</p>
|
||||
|
||||
<p><strong>2. Including locale js script in index.html page</strong></p>
|
||||
|
||||
<p>You can also include the locale specific js file in the index.html page. For example, if one client
|
||||
requires German locale, you would serve index_de-ge.html which will look something like this:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<html ng-app>
|
||||
<head>
|
||||
….
|
||||
<script src="angular.js"></script>
|
||||
<script src="i18n/angular-locale_de-ge.js"></script>
|
||||
….
|
||||
</head>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<p><strong>Comparison of the two approaches</strong>
|
||||
Both approaches described above requires you to prepare different index.html pages or js files for
|
||||
each locale that your app may be localized into. You also need to configure your server to serve
|
||||
the correct file that correspond to the desired locale.</p>
|
||||
|
||||
<p>However, the second approach (Including locale js script in index.html page) is likely to be slower
|
||||
because an extra script needs to be loaded.</p>
|
||||
|
||||
<h2>"Gotchas"</h2>
|
||||
|
||||
<p><strong>Currency symbol "gotcha"</strong></p>
|
||||
|
||||
<p>Angular's <a href="http://docs.angularjs.org/#!/api/ng.filter:currency">currency filter</a> allows
|
||||
you to use the default currency symbol from the <a href="api/ng.$locale"><code>locale service</code></a>,
|
||||
or you can provide the filter with a custom currency symbol. If your app will be used only in one
|
||||
locale, it is fine to rely on the default currency symbol. However, if you anticipate that viewers
|
||||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||
actual value is understood.</p>
|
||||
|
||||
<p>For example, if you want to display account balance of 1000 dollars with the following binding
|
||||
containing currency filter: <code>{{ 1000 | currency }}</code>, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.</p>
|
||||
|
||||
<p>In this case, you need to override the default currency symbol by providing the <a href="http://docs.angularjs.org/#!/api/ng.filter:currency">currency filter</a> with a currency symbol as
|
||||
a parameter when you configure the filter, for example, {{ 1000 | currency:"USD$"}}. This way,
|
||||
Angular will always show a balance of 'USD$1000' and disregard any locale changes.</p>
|
||||
|
||||
<p><strong>Translation length "gotcha"</strong></p>
|
||||
|
||||
<p>Keep in mind that translated strings/datetime formats can vary greatly in length. For example,
|
||||
<code>June 3, 1977</code> will be translated to Spanish as <code>3 de junio de 1977</code>. There are bound to be other
|
||||
more extreme cases. Hence, when internationalizing your apps, you need to apply CSS rules
|
||||
accordingly and do thorough testing to make sure UI components do not overlap.</p>
|
||||
|
||||
<p><strong>Timezones</strong></p>
|
||||
|
||||
<p>Keep in mind that Angular datetime filter uses the time zone settings of the browser. So the same
|
||||
application will show different time information depending on the time zone settings of the
|
||||
computer that the application is running on. Neither Javascript nor Angular currently supports
|
||||
displaying the date with a timezone specified by the developer.</p></div>
|
160
lib/angular/docs/partials/guide/ie.html
Normal file
160
lib/angular/docs/partials/guide/ie.html
Normal file
|
@ -0,0 +1,160 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>Overview</h2>
|
||||
|
||||
<p>This document describes the Internet Explorer (IE) idiosyncrasies when dealing with custom HTML
|
||||
attributes and tags. Read this document if you are planning on deploying your Angular application
|
||||
on IE v8.0 or earlier.</p>
|
||||
|
||||
<h2>Short Version</h2>
|
||||
|
||||
<p>To make your Angular application work on IE please make sure that:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
<a href="https://github.com/douglascrockford/JSON-js">JSON2</a> or
|
||||
<a href="http://bestiejs.github.com/json3/">JSON3</a> polyfills for this.</p></li>
|
||||
<li><p>you <strong>do not</strong> use custom element tags such as <code><ng:view></code> (use the attribute version
|
||||
<code><div ng-view></code> instead), or</p></li>
|
||||
<li><p>if you <strong>do use</strong> custom element tags, then you must take these steps to make IE happy:</p></li>
|
||||
</ol>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<p>The <strong>important</strong> parts are:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><code>xmlns:ng</code> - <em>namespace</em> - you need one namespace for each custom tag you are planning on
|
||||
using.</p></li>
|
||||
<li><p><code>document.createElement(yourTagName)</code> - <em>creation of custom tag names</em> - Since this is an
|
||||
issue only for older version of IE you need to load it conditionally. For each tag which does
|
||||
not have namespace and which is not defined in HTML you need to pre-declare it to make IE
|
||||
happy.</p></li>
|
||||
</ul>
|
||||
|
||||
<h2>Long Version</h2>
|
||||
|
||||
<p>IE has issues with element tag names which are not standard HTML tag names. These fall into two
|
||||
categories, and each category has its own fix.</p>
|
||||
|
||||
<ul>
|
||||
<li><p>If the tag name starts with <code>my:</code> prefix than it is considered an XML namespace and must
|
||||
have corresponding namespace declaration on <code><html xmlns:my="ignored"></code></p></li>
|
||||
<li><p>If the tag has no <code>:</code> but it is not a standard HTML tag, then it must be pre-created using
|
||||
<code>document.createElement('my-tag')</code></p></li>
|
||||
<li><p>If you are planning on styling the custom tag with CSS selectors, then it must be
|
||||
pre-created using <code>document.createElement('my-tag')</code> regardless of XML namespace.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>The Good News</h3>
|
||||
|
||||
<p>The good news is that these restrictions only apply to element tag names, and not to element
|
||||
attribute names. So this requires no special handling in IE: <code><div my-tag your:tag>
|
||||
</div></code>.</p>
|
||||
|
||||
<h3>What happens if I fail to do this?</h3>
|
||||
|
||||
<p>Suppose you have HTML with unknown tag <code>mytag</code> (this could also be <code>my:tag</code> or <code>my-tag</code> with same
|
||||
result):</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<html>
|
||||
<body>
|
||||
<mytag>some text</mytag>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<p>It should parse into the following DOM:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
#document
|
||||
+- HTML
|
||||
+- BODY
|
||||
+- mytag
|
||||
+- #text: some text
|
||||
</pre>
|
||||
|
||||
<p>The expected behavior is that the <code>BODY</code> element has a child element <code>mytag</code>, which in turn has
|
||||
the text <code>some text</code>.</p>
|
||||
|
||||
<p>But this is not what IE does (if the above fixes are not included):</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
#document
|
||||
+- HTML
|
||||
+- BODY
|
||||
+- mytag
|
||||
+- #text: some text
|
||||
+- /mytag
|
||||
</pre>
|
||||
|
||||
<p>In IE, the behavior is that the <code>BODY</code> element has three children:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>A self closing <code>mytag</code>. Example of self closing tag is <code><br/></code>. The trailing <code>/</code> is optional,
|
||||
but the <code><br></code> tag is not allowed to have any children, and browsers consider <code><br>some
|
||||
text</br></code> as three siblings not a <code><br></code> with <code>some text</code> as child.</p></li>
|
||||
<li><p>A text node with <code>some text</code>. This should have been a child of <code>mytag</code> above, not a sibling.</p></li>
|
||||
<li><p>A corrupt self closing <code>/mytag</code>. This is corrupt since element names are not allowed to have
|
||||
the <code>/</code> character. Furthermore this closing element should not be part of the DOM since it is
|
||||
only used to delineate the structure of the DOM.</p></li>
|
||||
</ol>
|
||||
|
||||
<h3>CSS Styling of Custom Tag Names</h3>
|
||||
|
||||
<p>To make CSS selectors work with custom elements, the custom element name must be pre-created with
|
||||
<code>document.createElement('my-tag')</code> regardless of XML namespace.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
<html xmlns:ng="needed for ng: namespace">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
// needed to make ng-include parse properly
|
||||
document.createElement('ng-include');
|
||||
|
||||
// needed to enable CSS reference
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
ng\\:view {
|
||||
display: block;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
ng-include {
|
||||
display: block;
|
||||
border: 1px solid blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ng:view></ng:view>
|
||||
<ng-include></ng-include>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre></div>
|
13
lib/angular/docs/partials/guide/index.html
Normal file
13
lib/angular/docs/partials/guide/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
|
||||
to develop web apps, you've come to the right place.</p>
|
||||
|
||||
<p>If you are completely or relatively unfamiliar with angular, you may want to check out one or both
|
||||
of the following documents before returning here to the Developer Guide:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="misc/started">Getting Started</a></li>
|
||||
<li><a href="tutorial/index">Angular Tutorial</a></li>
|
||||
</ul></div>
|
45
lib/angular/docs/partials/guide/introduction.html
Normal file
45
lib/angular/docs/partials/guide/introduction.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><p>Angular is pure client-side technology, written entirely in JavaScript. It works with the
|
||||
long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of
|
||||
web apps easier and faster than ever before.</p>
|
||||
|
||||
<p>One important way that Angular simplifies web development is by increasing the level of abstraction
|
||||
between the developer and most low-level web app development tasks. Angular automatically takes
|
||||
care of many of these tasks, including:</p>
|
||||
|
||||
<ul>
|
||||
<li>DOM Manipulation</li>
|
||||
<li>Setting Up Listeners and Notifiers</li>
|
||||
<li>Input Validation</li>
|
||||
</ul>
|
||||
|
||||
<p>Because Angular handles much of the work involved in these tasks, developers can concentrate more
|
||||
on application logic and less on repetitive, error-prone, lower-level coding.</p>
|
||||
|
||||
<p>At the same time that Angular simplifies the development of web apps, it brings relatively
|
||||
sophisticated techniques to the client-side, including:</p>
|
||||
|
||||
<ul>
|
||||
<li>Separation of data, application logic, and presentation components</li>
|
||||
<li>Data Binding between data and presentation components</li>
|
||||
<li>Services (common web app operations, implemented as substitutable objects)</li>
|
||||
<li>Dependency Injection (used primarily for wiring together services)</li>
|
||||
<li>An extensible HTML compiler (written entirely in JavaScript)</li>
|
||||
<li>Ease of Testing</li>
|
||||
</ul>
|
||||
|
||||
<p>These techniques have been for the most part absent from the client-side for far too long.</p>
|
||||
|
||||
<h3>Single-page / Round-trip Applications</h3>
|
||||
|
||||
<p>You can use Angular to develop both single-page and round-trip apps, but Angular is designed
|
||||
primarily for developing single-page apps. Angular supports browser history, forward and back
|
||||
buttons, and bookmarking in single-page apps.</p>
|
||||
|
||||
<p>You normally wouldn't want to load Angular with every page change, as would be the case with using
|
||||
Angular in a round-trip app. However, it would make sense to do so if you were adding a subset of
|
||||
Angular's features (for example, templates to leverage angular's data-binding feature) to an
|
||||
existing round-trip app. You might follow this course of action if you were migrating an older app
|
||||
to a single-page Angular app.</p></div>
|
276
lib/angular/docs/partials/guide/module.html
Normal file
276
lib/angular/docs/partials/guide/module.html
Normal file
|
@ -0,0 +1,276 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>What is a Module?</h2>
|
||||
|
||||
<p>Most applications have a main method which instantiates, wires, and bootstraps the application.
|
||||
Angular apps don't have a main method. Instead modules declaratively specify how an application
|
||||
should be bootstrapped. There are several advantages to this approach:</p>
|
||||
|
||||
<ul>
|
||||
<li>The process is more declarative which is easier to understand</li>
|
||||
<li>In unit-testing there is no need to load all modules, which may aid in writing unit-tests.</li>
|
||||
<li>Additional modules can be loaded in scenario tests, which can override some of the
|
||||
configuration and help end-to-end test the application</li>
|
||||
<li>Third party code can be packaged as reusable modules.</li>
|
||||
<li>The modules can be loaded in any/parallel order (due to delayed nature of module execution).</li>
|
||||
</ul>
|
||||
|
||||
<h2>The Basics</h2>
|
||||
|
||||
<p>Ok, I'm in a hurry. How do I get a Hello World module working?</p>
|
||||
|
||||
<p>Important things to notice:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/angular.Module"><code>Module</code></a> API</li>
|
||||
<li>Notice the reference to the <code>myApp</code> module in the <code><html ng-app="myApp"></code>, it is what
|
||||
bootstraps the app using your module.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="myApp" source-edit-deps="angular.js script.js" source-edit-html="index.html-69" source-edit-css="" source-edit-js="script.js-68" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-69" ng-html-wrap="myApp angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-69">
|
||||
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-68"></pre>
|
||||
<script type="text/ng-template" id="script.js-68">
|
||||
// declare a module
|
||||
var myAppModule = angular.module('myApp', []);
|
||||
|
||||
// configure the module.
|
||||
// in this example we will create a greeting filter
|
||||
myAppModule.filter('greet', function() {
|
||||
return function(name) {
|
||||
return 'Hello, ' + name + '!';
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="myApp" ng-set-html="index.html-69" ng-eval-javascript="script.js-68"></div>
|
||||
|
||||
<h2>Recommended Setup</h2>
|
||||
|
||||
<p>While the example above is simple, it will not scale to large applications. Instead we recommend
|
||||
that you break your application to multiple modules like this:</p>
|
||||
|
||||
<ul>
|
||||
<li>A service module, for service declaration</li>
|
||||
<li>A directive module, for directive declaration</li>
|
||||
<li>A filter module, for filter declaration</li>
|
||||
<li>And an application level module which depends on the above modules, and which has
|
||||
initialization code.</li>
|
||||
</ul>
|
||||
|
||||
<p>The reason for this breakup is that in your tests, it is often necessary to ignore the
|
||||
initialization code, which tends to be difficult to test. By putting it into a separate module it
|
||||
can be easily ignored in tests. The tests can also be more focused by only loading the modules
|
||||
that are relevant to tests.</p>
|
||||
|
||||
<p>The above is only a suggestion, so feel free to tailor it to your needs.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="xmpl" source-edit-deps="angular.js script.js" source-edit-html="index.html-71" source-edit-css="" source-edit-js="script.js-70" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-71" ng-html-wrap="xmpl angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-71">
|
||||
|
||||
<div ng-controller="XmplController">
|
||||
{{ greeting }}!
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-70"></pre>
|
||||
<script type="text/ng-template" id="script.js-70">
|
||||
angular.module('xmpl.service', []).
|
||||
value('greeter', {
|
||||
salutation: 'Hello',
|
||||
localize: function(localization) {
|
||||
this.salutation = localization.salutation;
|
||||
},
|
||||
greet: function(name) {
|
||||
return this.salutation + ' ' + name + '!';
|
||||
}
|
||||
}).
|
||||
value('user', {
|
||||
load: function(name) {
|
||||
this.name = name;
|
||||
}
|
||||
});
|
||||
|
||||
angular.module('xmpl.directive', []);
|
||||
|
||||
angular.module('xmpl.filter', []);
|
||||
|
||||
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
|
||||
run(function(greeter, user) {
|
||||
// This is effectively part of the main method initialization code
|
||||
greeter.localize({
|
||||
salutation: 'Bonjour'
|
||||
});
|
||||
user.load('World');
|
||||
})
|
||||
|
||||
|
||||
// A Controller for your app
|
||||
var XmplController = function($scope, greeter, user) {
|
||||
$scope.greeting = greeter.greet(user.name);
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="xmpl" ng-set-html="index.html-71" ng-eval-javascript="script.js-70"></div>
|
||||
|
||||
<h2>Module Loading & Dependencies</h2>
|
||||
|
||||
<p>A module is a collection of configuration and run blocks which get applied to the application
|
||||
during the bootstrap process. In its simplest form the module consist of collection of two kinds
|
||||
of blocks:</p>
|
||||
|
||||
<ol>
|
||||
<li><strong>Configuration blocks</strong> - get executed during the provider registrations and configuration
|
||||
phase. Only providers and constants can be injected into configuration blocks. This is to
|
||||
prevent accidental instantiation of services before they have been fully configured.</li>
|
||||
<li><strong>Run blocks</strong> - get executed after the injector is created and are used to kickstart the
|
||||
application. Only instances and constants can be injected into run blocks. This is to prevent
|
||||
further system configuration during application run time.</li>
|
||||
</ol>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
angular.module('myModule', []).
|
||||
config(function(injectables) { // provider-injector
|
||||
// This is an example of config block.
|
||||
// You can have as many of these as you want.
|
||||
// You can only inject Providers (not instances)
|
||||
// into the config blocks.
|
||||
}).
|
||||
run(function(injectables) { // instance-injector
|
||||
// This is an example of a run block.
|
||||
// You can have as many of these as you want.
|
||||
// You can only inject instances (not Providers)
|
||||
// into the run blocks
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h3>Configuration Blocks</h3>
|
||||
|
||||
<p>There are some convenience methods on the module which are equivalent to the config block. For
|
||||
example:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
angular.module('myModule', []).
|
||||
value('a', 123).
|
||||
factory('a', function() { return 123; }).
|
||||
directive('directiveName', ...).
|
||||
filter('filterName', ...);
|
||||
|
||||
// is same as
|
||||
|
||||
angular.module('myModule', []).
|
||||
config(function($provide, $compileProvider, $filterProvider) {
|
||||
$provide.value('a', 123)
|
||||
$provide.factory('a', function() { return 123; })
|
||||
$compileProvider.directive('directiveName', ...).
|
||||
$filterProvider.register('filterName', ...);
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>The configuration blocks get applied in the order in which they are registered. The only exception
|
||||
to it are constant definitions, which are placed at the beginning of all configuration blocks.</p>
|
||||
|
||||
<h3>Run Blocks</h3>
|
||||
|
||||
<p>Run blocks are the closest thing in Angular to the main method. A run block is the code which
|
||||
needs to run to kickstart the application. It is executed after all of the service have been
|
||||
configured and the injector has been created. Run blocks typically contain code which is hard
|
||||
to unit-test, and for this reason should be declared in isolated modules, so that they can be
|
||||
ignored in the unit-tests.</p>
|
||||
|
||||
<h3>Dependencies</h3>
|
||||
|
||||
<p>Modules can list other modules as their dependencies. Depending on a module implies that required
|
||||
module needs to be loaded before the requiring module is loaded. In other words the configuration
|
||||
blocks of the required modules execute before the configuration blocks or the requiring module.
|
||||
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
|
||||
modules require it.</p>
|
||||
|
||||
<h3>Asynchronous Loading</h3>
|
||||
|
||||
<p>Modules are a way of managing $injector configuration, and have nothing to do with loading of
|
||||
scripts into a VM. There are existing projects which deal with script loading, which may be used
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.</p>
|
||||
|
||||
<h2>Unit Testing</h2>
|
||||
|
||||
<p>In its simplest form a unit test is a way of instantiating a subset of the application in test and
|
||||
then applying a stimulus to it. It is important to realize that each module can only be loaded
|
||||
once per injector. Typically an app has only one injector. But in tests, each test has its own
|
||||
injector, which means that the modules are loaded multiple times per VM. Properly structured
|
||||
modules can help with unit testing, as in this example:</p>
|
||||
|
||||
<p>In all of these examples we are going to assume this module definition:
|
||||
<pre class="prettyprint linenums">
|
||||
angular.module('greetMod', []).
|
||||
|
||||
factory('alert', function($window) {
|
||||
return function(text) {
|
||||
$window.alert(text);
|
||||
}
|
||||
}).
|
||||
|
||||
value('salutation', 'Hello').
|
||||
|
||||
factory('greet', function(alert, salutation) {
|
||||
return function(name) {
|
||||
alert(salutation + ' ' + name + '!');
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>Let's write some tests:
|
||||
<pre class="prettyprint linenums">
|
||||
describe('myApp', function() {
|
||||
// load the relevant application modules then load a special
|
||||
// test module which overrides the $window with a mock version,
|
||||
// so that calling window.alert() will not block the test
|
||||
// runner with a real alert box. This is an example of overriding
|
||||
// configuration information in tests.
|
||||
beforeEach(module('greetMod', function($provide) {
|
||||
$provide.value('$window', {
|
||||
alert: jasmine.createSpy('alert')
|
||||
});
|
||||
}));
|
||||
|
||||
// The inject() will create the injector and inject the greet and
|
||||
// $window into the tests. The test need not concern itself with
|
||||
// wiring of the application, only with testing it.
|
||||
it('should alert on $window', inject(function(greet, $window) {
|
||||
greet('World');
|
||||
expect($window.alert).toHaveBeenCalledWith('Hello World!');
|
||||
}));
|
||||
|
||||
// this is another way of overriding configuration in the
|
||||
// tests using an inline module and inject methods.
|
||||
it('should alert using the alert service', function() {
|
||||
var alertSpy = jasmine.createSpy('alert');
|
||||
module(function($provide) {
|
||||
$provide.value('alert', alertSpy);
|
||||
});
|
||||
inject(function(greet) {
|
||||
greet('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre></div>
|
222
lib/angular/docs/partials/guide/overview.html
Normal file
222
lib/angular/docs/partials/guide/overview.html
Normal file
|
@ -0,0 +1,222 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>What Is Angular?</h2>
|
||||
|
||||
<p>AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser making it an
|
||||
ideal partner with any server technology.</p>
|
||||
|
||||
<p>Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in <em>what do I have to do, so
|
||||
that I trick the browser in to doing what I want.</em></p>
|
||||
|
||||
<p>The impedance mismatch between dynamic applications and static documents is often solved as:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>library</strong> - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., <code>jQuery</code>.</li>
|
||||
<li><strong>frameworks</strong> - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., <code>knockout</code>, <code>sproutcore</code>, etc.</li>
|
||||
</ul>
|
||||
|
||||
<p>Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:</p>
|
||||
|
||||
<ul>
|
||||
<li>Data binding as in <code>{{}}</code>.</li>
|
||||
<li>DOM control structures for repeating/hiding DOM fragments.</li>
|
||||
<li>Support for forms and form validation.</li>
|
||||
<li>Attaching code-behind to DOM elements.</li>
|
||||
<li>Grouping of HTML into reusable components.</li>
|
||||
</ul>
|
||||
|
||||
<h3>End-to-end solution</h3>
|
||||
|
||||
<p>Angular tries to be an end-to-end solution, when building a web application. This means it is
|
||||
not a single piece in an overall puzzle of building a web application, but an end-to-end solution.
|
||||
This makes Angular opinionated about how a CRUD application should be built. But while it is
|
||||
opinionated, it also tries to make sure that its opinion is just a starting point, which you can
|
||||
easily change. Angular comes with the following out-of-the-box:</p>
|
||||
|
||||
<ul>
|
||||
<li>Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.</li>
|
||||
<li>Testability story: unit-testing, end-to-end testing, mocks, test harnesses.</li>
|
||||
<li>Seed application with directory layout and test scripts as a starting point.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Angular Sweet Spot</h3>
|
||||
|
||||
<p>Angular simplifies application development by presenting a higher level of abstraction to the
|
||||
developer. Like any abstraction, it comes at a cost of flexibility. In other words not every app
|
||||
is a good fit for Angular. Angular was built for the CRUD application in mind. Luckily CRUD
|
||||
applications represent at least 90% of the web applications. But to understand what Angular is
|
||||
good at one also has to understand when an app is not a good fit for Angular.</p>
|
||||
|
||||
<p>Games, and GUI editors are examples of very intensive and tricky DOM manipulation. These kinds of
|
||||
apps are different from CRUD apps, and as a result are not a good fit for Angular. In these cases
|
||||
using something closer to bare metal such as <code>jQuery</code> may be a better fit.</p>
|
||||
|
||||
<h2>An Introductory Angular Example</h2>
|
||||
|
||||
<p>Below is a typical CRUD application which contains a form. The form values are validated, and
|
||||
are used to compute the total, which is formatted to a particular locale. These are some common
|
||||
concepts which the application developer may face:</p>
|
||||
|
||||
<ul>
|
||||
<li>attaching data-model to the UI.</li>
|
||||
<li>writing, reading and validating user input.</li>
|
||||
<li>computing new values based on the model.</li>
|
||||
<li>formatting output in a user specific locale.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-73" source-edit-css="" source-edit-js="script.js-72" source-edit-unit="" source-edit-scenario="scenario.js-74"></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-73" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-73">
|
||||
<div ng-controller="InvoiceCntl">
|
||||
<b>Invoice:</b>
|
||||
<br>
|
||||
<br>
|
||||
<table>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="integer" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-72"></pre>
|
||||
<script type="text/ng-template" id="script.js-72">
|
||||
function InvoiceCntl($scope) {
|
||||
$scope.qty = 1;
|
||||
$scope.cost = 19.95;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="End to end test">
|
||||
<pre class="prettyprint linenums" ng-set-text="scenario.js-74"></pre>
|
||||
<script type="text/ng-template" id="scenario.js-74">
|
||||
it('should show of angular binding', function() {
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-73" ng-eval-javascript="script.js-72"></div>
|
||||
|
||||
<p>Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.</p>
|
||||
|
||||
<p>In the <code><html></code> tag, we specify that it is an Angular
|
||||
application with the <code>ng-app</code> directive. The <code>ng-app</code> will cause Angular to <a href="guide/bootstrap">auto initialize</a> your application.</p>
|
||||
|
||||
<pre><code><html ng-app>
|
||||
</code></pre>
|
||||
|
||||
<p>We load Angular using the <code><script></code> tag:</p>
|
||||
|
||||
<pre><code><script src="https://ajax.googleapis.com/ajax/libs/angularjs/?.?.?/angular.min.js"></script>
|
||||
</code></pre>
|
||||
|
||||
<p>From the <code>ng-model</code> attribute of the <code><input></code> tags, Angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:</p>
|
||||
|
||||
<pre><code>Quantity: <input type="integer" min="0" ng-model="qty" required >
|
||||
Cost: <input type="number" ng-model="cost" required >
|
||||
</code></pre>
|
||||
|
||||
<p>These input widgets look normal enough, but consider these points:</p>
|
||||
|
||||
<ul>
|
||||
<li>When this page loaded, Angular bound the names of the input widgets (<code>qty</code> and <code>cost</code>) to
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
Model-View-Controller design pattern.</li>
|
||||
<li>Note that the HTML widget <a href="api/ng.directive:input"><code>input</code></a>
|
||||
has special powers. The input invalidates itself by turning red when you enter invalid data or
|
||||
leave the the input fields blank. These new widget behaviors make it easier to implement field
|
||||
validation common in CRUD applications.</li>
|
||||
</ul>
|
||||
|
||||
<p>And finally, the mysterious <code>{{ double curly braces }}</code>:</p>
|
||||
|
||||
<pre><code> Total: {{qty * cost | currency}}
|
||||
</code></pre>
|
||||
|
||||
<p>This notation, <code>{{ _expression_ }}</code>, is Angular markup for data-binding. The expression itself can
|
||||
be a combination of both an expression and a <a href="guide/dev_guide.templates.filters">filter</a>: <code>{{
|
||||
expression | filter }}</code>. Angular provides filters for formatting display data.</p>
|
||||
|
||||
<p>In the example above, the expression in double-curly braces directs Angular to "bind the data we
|
||||
got from the input widgets to the display, multiply them together, and format the resulting number
|
||||
into output that looks like money."</p>
|
||||
|
||||
<p>Notice that we achieved this application behavior not by calling Angular methods, nor by
|
||||
implementing application specific behavior as a framework. We achieved the behavior because the
|
||||
browser behaved more in line with what is needed for a dynamic web application rather then what is
|
||||
needed for a static document. Angular has lowered the impedance mismatch to the point where no
|
||||
library/framework calls are needed.</p>
|
||||
|
||||
<h2>The Zen of Angular</h2>
|
||||
|
||||
<p>Angular is built around the belief that declarative code is better than imperative when it comes
|
||||
to building UIs and wiring software components together, while imperative code is excellent for
|
||||
expressing business logic.</p>
|
||||
|
||||
<ul>
|
||||
<li>It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.</li>
|
||||
<li>It is a really, <em>really</em> good idea to regard app testing as equal in importance to app
|
||||
writing. Testing difficulty is dramatically affected by the way the code is structured.</li>
|
||||
<li>It is an excellent idea to decouple the client side of an app from the server side. This
|
||||
allows development work to progress in parallel, and allows for reuse of both sides.</li>
|
||||
<li>It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
building an app: from designing the UI, through writing the business logic, to testing.</li>
|
||||
<li>It is always good to make common tasks trivial and difficult tasks possible.</li>
|
||||
</ul>
|
||||
|
||||
<p>Angular frees you from the following pain:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Registering callbacks:</strong> Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It
|
||||
vastly reduces the amount of JavaScript coding <em>you</em> have to do, and it makes it easier to see
|
||||
what your application does.</li>
|
||||
<li><strong>Manipulating HTML DOM programmatically:</strong> Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low level DOM manipulation
|
||||
tasks. Most applications written with Angular never have to programmatically manipulate the
|
||||
DOM, although you can if you want to.</li>
|
||||
<li><strong>Marshaling data to and from the UI:</strong> CRUD operations make up the majority of AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to an HTML
|
||||
form, allowing users to modify the form, validating the form, displaying validation errors,
|
||||
returning to an internal model, and then back to the server, creates a lot of boilerplate
|
||||
code. Angular eliminates almost all of this boilerplate, leaving code that describes the
|
||||
overall flow of the application rather than all of the implementation details.</li>
|
||||
<li><strong>Writing tons of initialization code just to get started:</strong> Typically you need to write a lot
|
||||
of plumbing just to get a basic "Hello World" AJAX app working. With Angular you can bootstrap
|
||||
your app easily using services, which are auto-injected into your application in a <a href="http://code.google.com/p/google-guice/">Guice</a>-like dependency-injection style. This allows you
|
||||
to get started developing features quickly. As a bonus, you get full control over the
|
||||
initialization process in automated tests.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Watch a Presentation About Angular</h2>
|
||||
|
||||
<p>Here is a presentation on Angular from May 2012.</p>
|
||||
|
||||
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe></div>
|
328
lib/angular/docs/partials/guide/scope.html
Normal file
328
lib/angular/docs/partials/guide/scope.html
Normal file
|
@ -0,0 +1,328 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><h2>What are Scopes?</h2>
|
||||
|
||||
<p><a href="api/ng.$rootScope.Scope"><code>scope</code></a> is an object that refers to the application
|
||||
model. It is an execution context for <a href="guide/expression">expressions</a>. Scopes are
|
||||
arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can
|
||||
watch <a href="guide/expression">expressions</a> and propagate events.</p>
|
||||
|
||||
<h3>Scope characteristics</h3>
|
||||
|
||||
<ul>
|
||||
<li><p>Scopes provide APIs (<a href="api/ng.$rootScope.Scope#$watch"><code>$watch</code></a>) to observe
|
||||
model mutations.</p></li>
|
||||
<li><p>Scopes provide APIs (<a href="api/ng.$rootScope.Scope#$apply"><code>$apply</code></a>) to
|
||||
propagate any model changes through the system into the view from outside of the "Angular
|
||||
realm" (controllers, services, Angular event handlers).</p></li>
|
||||
<li><p>Scopes can be nested to isolate application components while providing access to shared model
|
||||
properties. A scope (prototypically) inherits properties from its parent scope.</p></li>
|
||||
<li><p>Scopes provide context against which <a href="guide/expression">expressions</a> are evaluated. For
|
||||
example <code>{{username}}</code> expression is meaningless, unless it is evaluated against a specific
|
||||
scope which defines the <code>username</code> property.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Scope as Data-Model</h3>
|
||||
|
||||
<p>Scope is the glue between application controller and the view. During the template <a href="guide/compiler">linking</a> phase the <a href="api/ng.$compileProvider#directive"><code>directives</code></a> set up
|
||||
<a href="api/ng.$rootScope.Scope#$watch"><code><code>$watch</code></code></a> expressions on the scope. The
|
||||
<code>$watch</code> allows the directives to be notified of property changes, which allows the directive to
|
||||
render the updated value to the DOM.</p>
|
||||
|
||||
<p>Both controllers and directives have reference to the scope, but not to each other. This
|
||||
arrangement isolates the controller from the directive as well as from DOM. This is an important
|
||||
point since it makes the controllers view agnostic, which greatly improves the testing story of
|
||||
the applications.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-76" source-edit-css="" source-edit-js="script.js-75" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-76" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-76">
|
||||
<div ng-controller="MyController">
|
||||
Your name:
|
||||
<input type="text" ng-model="username">
|
||||
<button ng-click='sayHello()'>greet</button>
|
||||
<hr>
|
||||
{{greeting}}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-75"></pre>
|
||||
<script type="text/ng-template" id="script.js-75">
|
||||
function MyController($scope) {
|
||||
$scope.username = 'World';
|
||||
|
||||
$scope.sayHello = function() {
|
||||
$scope.greeting = 'Hello ' + $scope.username + '!';
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-76" ng-eval-javascript="script.js-75"></div>
|
||||
|
||||
<p>In the above example notice that the <code>MyController</code> assigns <code>World</code> to the <code>username</code> property of
|
||||
the scope. The scope then notifies the <code>input</code> of the assignment, which then renders the input
|
||||
with username pre-filled. This demonstrates how a controller can write data into the scope.</p>
|
||||
|
||||
<p>Similarly the controller can assign behavior to scope as seen by the <code>sayHello</code> method, which is
|
||||
invoked when the user clicks on the 'greet' button. The <code>sayHello</code> method can read the <code>username</code>
|
||||
property and create a <code>greeting</code> property. This demonstrates that the properties on scope update
|
||||
automatically when they are bound to HTML input widgets.</p>
|
||||
|
||||
<p>Logically the rendering of <code>{{greeting}}</code> involves:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>retrieval of the scope associated with DOM node where <code>{{greeting}}</code> is defined in template.
|
||||
In this example this is the same scope as the scope which was passed into <code>MyController</code>. (We
|
||||
will discuss scope hierarchies later.)</p></li>
|
||||
<li><p>Evaluate the <code>greeting</code> <a href="guide/expression">expression</a> against the scope retrieved above,
|
||||
and assign the result to the text of the enclosing DOM element.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>You can think of the scope and its properties as the data which is used to render the view. The
|
||||
scope is the single source-of-truth for all things view related.</p>
|
||||
|
||||
<p>From a testability point of view, the separation of the controller and the view is desirable, because it allows us
|
||||
to test the behavior without being distracted by the rendering details.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
it('should say hello', function() {
|
||||
var scopeMock = {};
|
||||
var cntl = new MyController(scopeMock);
|
||||
|
||||
// Assert that username is pre-filled
|
||||
expect(scopeMock.username).toEqual('World');
|
||||
|
||||
// Assert that we read new username and greet
|
||||
scopeMock.username = 'angular';
|
||||
scopeMock.sayHello();
|
||||
expect(scopeMock.greeting).toEqual('Hello angular!');
|
||||
});
|
||||
</pre>
|
||||
|
||||
<h3>Scope Hierarchies</h3>
|
||||
|
||||
<p>Each Angular application has exactly one <a href="api/ng.$rootScope"><code>root scope</code></a>, but
|
||||
may have several child scopes.</p>
|
||||
|
||||
<p>The application can have multiple scopes, because some <a href="guide/directive">directives</a> create
|
||||
new child scopes (refer to directive documentation to see which directives create new scopes).
|
||||
When new scopes are created, they are added as children of their parent scope. This creates a tree
|
||||
structure which parallels the DOM where they're attached</p>
|
||||
|
||||
<p>When Angular evaluates <code>{{username}}</code>, it first looks at the scope associated with the given
|
||||
element for the <code>username</code> property. If no such property is found, it searches the parent scope
|
||||
and so on until the root scope is reached. In JavaScript this behavior is known as prototypical
|
||||
inheritance, and child scopes prototypically inherit from their parents.</p>
|
||||
|
||||
<p>This example illustrates scopes in application, and prototypical inheritance of properties.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-79" source-edit-css="style.css-77" source-edit-js="script.js-78" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-79" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-79">
|
||||
<div ng-controller="EmployeeController">
|
||||
Manager: {{employee.name}} [ {{department}} ]<br>
|
||||
Reports:
|
||||
<ul>
|
||||
<li ng-repeat="employee in employee.reports">
|
||||
{{employee.name}} [ {{department}} ]
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
{{greeting}}
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="style.css">
|
||||
<pre class="prettyprint linenums" ng-set-text="style.css-77"></pre>
|
||||
<style type="text/css" id="style.css-77">
|
||||
/* remove .doc-example-live in jsfiddle */
|
||||
.doc-example-live .ng-scope {
|
||||
border: 1px dashed red;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-78"></pre>
|
||||
<script type="text/ng-template" id="script.js-78">
|
||||
function EmployeeController($scope) {
|
||||
$scope.department = 'Engineering';
|
||||
$scope.employee = {
|
||||
name: 'Joe the Manager',
|
||||
reports: [
|
||||
{name: 'John Smith'},
|
||||
{name: 'Mary Run'}
|
||||
]
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-79" ng-eval-javascript="script.js-78"></div>
|
||||
|
||||
<p>Notice that Angular automatically places <code>ng-scope</code> class on elements where scopes are
|
||||
attached. The <code><style></code> definition in this example highlights in red the new scope locations. The
|
||||
child scopes are necessary because the repeater evaluates <code>{{employee.name}}</code> expression, but
|
||||
depending on which scope the expression is evaluated it produces different result. Similarly the
|
||||
evaluation of <code>{{department}}</code> prototypically inherits from root scope, as it is the only place
|
||||
where the <code>department</code> property is defined.</p>
|
||||
|
||||
<h3>Retrieving Scopes from the DOM.</h3>
|
||||
|
||||
<p>Scopes are attached to the DOM as <code>$scope</code> data property, and can be retrieved for debugging
|
||||
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
|
||||
application.) The location where the root scope is attached to the DOM is defined by the location
|
||||
of <a href="api/ng.directive:ngApp"><code><code>ng-app</code></code></a> directive. Typically
|
||||
<code>ng-app</code> is placed an the <code><html></code> element, but it can be placed on other elements as well, if,
|
||||
for example, only a portion of the view needs to be controlled by Angular.</p>
|
||||
|
||||
<p>To examine the scope in the debugger:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>right click on the element of interest in your browser and select 'inspect element'. You
|
||||
should see the browser debugger with the element you clicked on highlighted.</p></li>
|
||||
<li><p>The debugger allows you to access the currently selected element in the console as <code>$0</code>
|
||||
variable.</p></li>
|
||||
<li><p>To retrieve the associated scope in console execute: <code>angular.element($0).scope()</code></p></li>
|
||||
</ol>
|
||||
|
||||
<h3>Scope Events Propagation</h3>
|
||||
|
||||
<p>Scopes can propagate events in similar fashion to DOM events. The event can be <a href="api/ng.$rootScope.Scope#$broadcast"><code>broadcasted</code></a> to the scope children or <a href="api/ng.$rootScope.Scope#$emit"><code>emitted</code></a> to scope parents.</p>
|
||||
|
||||
<h3>Source</h3>
|
||||
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-81" source-edit-css="" source-edit-js="script.js-80" source-edit-unit="" source-edit-scenario=""></div>
|
||||
<div class="tabbable"><div class="tab-pane" title="index.html">
|
||||
<pre class="prettyprint linenums" ng-set-text="index.html-81" ng-html-wrap=" angular.js script.js"></pre>
|
||||
<script type="text/ng-template" id="index.html-81">
|
||||
<div ng-controller="EventController">
|
||||
Root scope <tt>MyEvent</tt> count: {{count}}
|
||||
<ul>
|
||||
<li ng-repeat="i in [1]" ng-controller="EventController">
|
||||
<button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
|
||||
<button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
|
||||
<br>
|
||||
Middle scope <tt>MyEvent</tt> count: {{count}}
|
||||
<ul>
|
||||
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
|
||||
Leaf scope <tt>MyEvent</tt> count: {{count}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
<div class="tab-pane" title="script.js">
|
||||
<pre class="prettyprint linenums" ng-set-text="script.js-80"></pre>
|
||||
<script type="text/ng-template" id="script.js-80">
|
||||
function EventController($scope) {
|
||||
$scope.count = 0;
|
||||
$scope.$on('MyEvent', function() {
|
||||
$scope.count++;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div><h3>Demo</h3>
|
||||
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-81" ng-eval-javascript="script.js-80"></div>
|
||||
|
||||
<h3>Scope Life Cycle</h3>
|
||||
|
||||
<p>The normal flow of a browser receiving an event is that it executes a corresponding JavaScript
|
||||
callback. Once the callback completes the browser re-renders the DOM and returns to waiting for
|
||||
more events.</p>
|
||||
|
||||
<p>When the browser calls into JavaScript the code executes outside the Angular execution context,
|
||||
which means that Angular is unaware of model modifications. To properly process model
|
||||
modifications the execution has to enter the Angular execution context using the <a href="api/ng.$rootScope.Scope#$apply"><code><code>$apply</code></code></a> method. Only model modifications which
|
||||
execute inside the <code>$apply</code> method will be properly accounted for by Angular. For example if a
|
||||
directive listens on DOM events, such as <a href="api/ng.directive:ngClick"><code><code>ng-click</code></code></a> it must evaluate the
|
||||
expression inside the <code>$apply</code> method.</p>
|
||||
|
||||
<p>After evaluating the expression, the <code>$apply</code> method performs a <a href="api/ng.$rootScope.Scope#$digest"><code><code>$digest</code></code></a>. In the $digest phase the scope examines all
|
||||
of the <code>$watch</code> expressions and compares them with the previous value. This dirty checking is done
|
||||
asynchronously. This means that assignment such as <code>$scope.username="angular"</code> will not
|
||||
immediately cause a <code>$watch</code> to be notified, instead the <code>$watch</code> notification is delayed until
|
||||
the <code>$digest</code> phase. This delay is desirable, since it coalesces multiple model updates into one
|
||||
<code>$watch</code> notification as well as it guarantees that during the <code>$watch</code> notification no other
|
||||
<code>$watch</code>es are running. If a <code>$watch</code> changes the value of the model, it will force additional
|
||||
<code>$digest</code> cycle.</p>
|
||||
|
||||
<ol>
|
||||
<li><p><strong>Creation</strong></p>
|
||||
|
||||
<p>The <a href="api/ng.$rootScope"><code>root scope</code></a> is created during the application
|
||||
bootstrap by the <a href="api/AUTO.$injector"><code>$injector</code></a>. During template
|
||||
linking, some directives create new child scopes.</p></li>
|
||||
<li><p><strong>Watcher registration</strong></p>
|
||||
|
||||
<p>During template linking directives register <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a> on the scope. These watches will be
|
||||
used to propagate model values to the DOM.</p></li>
|
||||
<li><p><strong>Model mutation</strong></p>
|
||||
|
||||
<p>For mutations to be properly observed, you should make them only within the <a href="api/ng.$rootScope.Scope#$apply"><code>scope.$apply()</code></a>. (Angular APIs do this
|
||||
implicitly, so no extra <code>$apply</code> call is needed when doing synchronous work in controllers,
|
||||
or asynchronous work with <a href="api/ng.$http"><code>$http</code></a> or <a href="api/ng.$timeout"><code>$timeout</code></a> services.</p></li>
|
||||
<li><p><strong>Mutation observation</strong></p>
|
||||
|
||||
<p>At the end <code>$apply</code>, Angular performs a <a href="api/ng.$rootScope.Scope#$digest"><code>$digest</code></a> cycle on the root scope, which then propagates throughout all child scopes. During
|
||||
the <code>$digest</code> cycle, all <code>$watch</code>ed expressions or functions are checked for model mutation
|
||||
and if a mutation is detected, the <code>$watch</code> listener is called.</p></li>
|
||||
<li><p><strong>Scope destruction</strong></p>
|
||||
|
||||
<p>When child scopes are no longer needed, it is the responsibility of the child scope creator
|
||||
to destroy them via <a href="api/ng.$rootScope.Scope#$destroy"><code>scope.$destroy()</code></a>
|
||||
API. This will stop propagation of <code>$digest</code> calls into the child scope and allow for memory
|
||||
used by the child scope models to be reclaimed by the garbage collector.</p></li>
|
||||
</ol>
|
||||
|
||||
<h4>Scopes and Directives</h4>
|
||||
|
||||
<p>During the compilation phase, the <a href="guide/compiler">compiler</a> matches <a href="api/ng.$compileProvider#directive"><code>directives</code></a> against the DOM template. The directives
|
||||
usually fall into one of two categories:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Observing <a href="api/ng.$compileProvider#directive"><code>directives</code></a>, such as
|
||||
double-curly expressions <code>{{expression}}</code>, register listeners using the <a href="api/ng.$rootScope.Scope#$watch"><code>$watch()</code></a> method. This type of directive needs
|
||||
to be notified whenever the expression changes so that it can update the view.</p></li>
|
||||
<li><p>Listener directives, such as <a href="api/ng.directive:ngClick"><code>ng-click</code></a>, register a listener with the DOM. When the DOM listener fires, the directive
|
||||
executes the associated expression and updates the view using the <a href="api/ng.$rootScope.Scope#$apply"><code>$apply()</code></a> method.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>When an external event (such as a user action, timer or XHR) is received, the associated <a href="guide/expression">expression</a> must be applied to the scope through the <a href="api/ng.$rootScope.Scope#$apply"><code>$apply()</code></a> method so that all listeners are updated
|
||||
correctly.</p>
|
||||
|
||||
<h4>Directives that Create Scopes</h4>
|
||||
|
||||
<p>In most cases, <a href="api/ng.$compileProvider#directive"><code>directives</code></a> and scopes interact
|
||||
but do not create new instances of scope. However, some directives, such as <a href="api/ng.directive:ngController"><code>ng-controller</code></a> and <a href="api/ng.directive:ngRepeat"><code>ng-repeat</code></a>, create new child scopes
|
||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||
element by using an <code>angular.element(aDomElement).scope()</code> method call.</p>
|
||||
|
||||
<h4>Controllers and Scopes</h4>
|
||||
|
||||
<p>Scopes and controllers interact with each other in the following situations:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Controllers use scopes to expose controller methods to templates (see <a href="api/ng.directive:ngController"><code>ng-controller</code></a>).</p></li>
|
||||
<li><p>Controllers define methods (behavior) that can mutate the model (properties on the scope).</p></li>
|
||||
<li><p>Controllers may register <a href="api/ng.$rootScope.Scope#$watch"><code>watches</code></a> on
|
||||
the model. These watches execute immediately after the controller behavior executes.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>See the <a href="api/ng.directive:ngController"><code>ng-controller</code></a> for more
|
||||
information.</p>
|
||||
|
||||
<h4>Scope <code>$watch</code> Performance Considerations</h4>
|
||||
|
||||
<p>Dirty checking the scope for property changes is a common operation in Angular and for this reason
|
||||
the dirty checking function must be efficient. Care should be taken that the dirty checking
|
||||
function does not do any DOM access, as DOM access is orders of magnitude slower then property
|
||||
access on JavaScript object.</p></div>
|
4
lib/angular/docs/partials/guide/type.html
Normal file
4
lib/angular/docs/partials/guide/type.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div></div>
|
Loading…
Add table
Add a link
Reference in a new issue