Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f0904cf12e | |||
| 81b7e5ab0e | |||
| 1b1890274e | |||
| 6d418ef5e3 | |||
| 3fa1606c43 | |||
| 8661a9e3d4 | |||
| cf63292742 | |||
| fd420c4061 | |||
| 1382d4e88e | |||
| b9ddef2a49 | |||
| eafba9e2e5 | |||
| 6f1d9f8ca6 | |||
| bb9310974b | |||
| 30279d7b9b | |||
| 8df5f3259a | |||
| 14e797c1a1 | |||
| 82cd6b87f0 | |||
| 6d7cc572b5 | |||
| 2ebbe00eb5 | |||
| 8b86d363aa | |||
| 9b51067516 | |||
| a3208bf66e | |||
| 4e1fb82628 | |||
| ad466412c6 | |||
| 299a32740c | |||
| eb799bcb71 | |||
| 7314c1b69e | |||
| fcfe2b3793 | |||
| 5e140a99c7 | |||
| 4da169d15d | |||
| f37c6f9f73 | |||
| eae658fd96 | |||
| 286f269753 | |||
| 73640a6b7c | |||
| 2dc55ff5c2 | |||
| 98a2563ec4 | |||
| 8c02122837 | |||
| 1e069532fc | |||
| 6f6f7e82a4 | |||
| d852122442 | |||
| 66cb161221 | |||
| 6c14fb1eb6 | |||
| e906aafb0a |
@@ -80,9 +80,6 @@
|
||||
|
||||
## Features
|
||||
|
||||
- **injector:** "strict-DI" mode which disables "automatic" function annotation
|
||||
([f5a04f59](https://github.com/angular/angular.js/commit/f5a04f59cf8e8dd6d1806059e3d7fe440aa1613e),
|
||||
[#6719](https://github.com/angular/angular.js/issues/6719), [#6717](https://github.com/angular/angular.js/issues/6717), [#4504](https://github.com/angular/angular.js/issues/4504), [#6069](https://github.com/angular/angular.js/issues/6069), [#3611](https://github.com/angular/angular.js/issues/3611))
|
||||
- **ngMock:** add support of mocha tdd interface
|
||||
([6d1c6772](https://github.com/angular/angular.js/commit/6d1c67727ab872c44addc783ef1406952142d89e),
|
||||
[#7489](https://github.com/angular/angular.js/issues/7489))
|
||||
|
||||
+1
-1
@@ -269,6 +269,6 @@ You can find out more detailed information about contributing in the
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
[unit-testing]: https://docs.angularjs.org/guide/unit-testing
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ This process based on the idea of minimizing user pain
|
||||
* Check if there are comments that link to a dupe. If so verify that this is indeed a dupe, [close it][], and go to the last step.
|
||||
1. Bugs:
|
||||
* Label `Type: Bug`
|
||||
* Reproducible? - Steps to reproduce the bug are clear. If they are not,
|
||||
* Reproducible? - Steps to reproduce the bug are clear. If they are not, ask for a clarification. If there's no reply after a week, [close it][].
|
||||
* Reproducible on master? - <http://code.angularjs.org/snapshot/>
|
||||
|
||||
1. Non bugs:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs and testing mocks.
|
||||
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
@@ -212,7 +212,7 @@ Use ngTouch when developing for mobile browsers/devices.
|
||||
{@link ngTouch#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -252,7 +252,7 @@ Use ngSanitize to securely parse and manipulate HTML data in your application.
|
||||
|
||||
## {@link ngMock ngMock}
|
||||
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
|
||||
<div class="alert alert-info">Include the **angular-mocks.js** file into your test runner for this to work.</div>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
...
|
||||
scope: {
|
||||
'bind': '=localValue'
|
||||
localValue: '=bind'
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
@@ -37,3 +37,17 @@ elements. For example:
|
||||
```
|
||||
<b>Hello</b> World!
|
||||
```
|
||||
|
||||
Watch out for html comments at the beginning or end of templates, as these can cause this error as
|
||||
well. Consider the following template:
|
||||
|
||||
```
|
||||
<div class='container'>
|
||||
<div class='wrapper>
|
||||
...
|
||||
</div> <!-- wrapper -->
|
||||
</div> <!-- container -->
|
||||
```
|
||||
|
||||
The `<!-- container -->` comment is interpreted as a second root element and causes the template to
|
||||
be invalid.
|
||||
|
||||
@@ -3,72 +3,310 @@
|
||||
@fullName Action Already In Progress
|
||||
@description
|
||||
|
||||
At any point in time there can be only one `$digest` or $apply operation in progress.
|
||||
The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call.
|
||||
At any point in time there can be only one `$digest` or `$apply` operation in progress. This is to
|
||||
prevent very hard to detect bugs from entering your application. The stack trace of this error
|
||||
allows you to trace the origin of the currently executing `$apply` or `$digest` call, which caused
|
||||
the error.
|
||||
|
||||
`$digest` or `$apply` are processing operational states of the Scope - data-structure in Angular that provides context for models and enables model mutation observation.
|
||||
## Background
|
||||
|
||||
Trying to reenter a `$digest` or `$apply` while one of them is already in progress is typically a sign of programming error that needs to be fixed.
|
||||
Angular uses a dirty-checking digest mechanism to monitor and update values of the scope during
|
||||
the processing of your application. The digest works by checking all the values that are being
|
||||
watched against their previous value and running any watch handlers that have been defined for those
|
||||
values that have changed.
|
||||
|
||||
This digest mechanism is triggered by calling `$digest` on a scope object. Normally you do not need
|
||||
to trigger a digest manually, because every external action that can trigger changes in your
|
||||
application, such as mouse events, timeouts or server responses, wrap the Angular application code
|
||||
in a block of code that will run `$digest` when the code completes.
|
||||
|
||||
You wrap Angular code in a block that will be followed by a `$digest` by calling `$apply` on a scope
|
||||
object. So, in pseudo-code, the process looks like this:
|
||||
|
||||
```
|
||||
element.on('mouseup', function() {
|
||||
scope.$apply(function() {
|
||||
$scope.doStuff();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
where `$apply()` looks something like:
|
||||
|
||||
```
|
||||
$apply = function(fn) {
|
||||
try {
|
||||
fn();
|
||||
} finally() {
|
||||
$digest();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Digest Phases
|
||||
|
||||
Angular keeps track of what phase of processing we are in, the relevant ones being `$apply` and
|
||||
`$digest`. Trying to reenter a `$digest` or `$apply` while one of them is already in progress is
|
||||
typically a sign of programming error that needs to be fixed. So Angular will throw this error when
|
||||
that occurs.
|
||||
|
||||
In most situations it should be well defined whether a piece of code will be run inside an `$apply`,
|
||||
in which case you should not be calling `$apply` or `$digest`, or it will be run outside, in which
|
||||
case you should wrap any code that will be interacting with Angular scope or services, in a call to
|
||||
`$apply`.
|
||||
|
||||
As an example, all Controller code should expect to be run within Angular, so it should have no need
|
||||
to call `$apply` or `$digest`. Conversely, code that is being trigger directly as a call back to
|
||||
some external event, from the DOM or 3rd party library, should expect that it is never called from
|
||||
within Angular, and so any Angular application code that it calls should first be wrapped in a call
|
||||
to $apply.
|
||||
|
||||
## Common Causes
|
||||
|
||||
Apart from simply incorrect calls to `$apply` or `$digest` there are some cases when you may get
|
||||
this error through no fault of your own.
|
||||
|
||||
### Inconsistent API (Sync/Async)
|
||||
|
||||
This error is often seen when interacting with an API that is sometimes sync and sometimes async.
|
||||
|
||||
For example:
|
||||
For example, imagine a 3rd party library that has a method which will retrieve data for us. Since it
|
||||
may be making an asynchronous call to a server, it accepts a callback function, which will be called
|
||||
when the data arrives.
|
||||
|
||||
```
|
||||
function MyController() {
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
scope.$apply(function() {
|
||||
scope.someData = someData;
|
||||
$scope.$apply(function() {
|
||||
$scope.someData = someData;
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The controller constructor is always instantiated from within an $apply cycle, so if the third-party component called our callback synchronously, we'd be trying to enter the $apply again.
|
||||
We expect that our callback will be called asynchronously, and so from outside Angular. Therefore, we
|
||||
correctly wrap our application code that interacts with Angular in a call to `$apply`.
|
||||
|
||||
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or wrap the call to the api with setTimeout call to make it always asynchronous.
|
||||
The problem comes if `getData()` decides to call the callback handler synchronously; perhaps it has
|
||||
the data already cached in memory and so it immediately calls the callback to return the data,
|
||||
synchronously.
|
||||
|
||||
Since, the `MyController` constructor is always instantiated from within an `$apply` call, our
|
||||
handler is trying to enter a new `$apply` block from within one.
|
||||
|
||||
Other situation that leads to this error is when you are trying to reuse a function to by using it as a callback for code that is called by various apis inside and outside of $apply.
|
||||
This is not an ideal design choice on the part of the 3rd party library.
|
||||
|
||||
For example:
|
||||
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or force
|
||||
your callback handler to always run asynchronously by using the `$timeout` service.
|
||||
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
$timeout(function() {
|
||||
$scope.someData = someData;
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Here we have used `$timeout` to schedule the changes to the scope in a future call stack.
|
||||
By providing a timeout period of 0ms, this will occur as soon as possible and `$timeout` will ensure
|
||||
that the code will be called in a single `$apply` block.
|
||||
|
||||
### Triggering Events Programmatically
|
||||
|
||||
The other situation that often leads to this error is when you trigger code (such as a DOM event)
|
||||
programmatically (from within Angular), which is normally called by an external trigger.
|
||||
|
||||
For example, consider a directive that will set focus on an input control when a value in the scope
|
||||
is true:
|
||||
|
||||
```
|
||||
myApp.directive('setFocusIf', function() {
|
||||
return {
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
$scope.$apply(function() {
|
||||
// do work here, and update the model
|
||||
};
|
||||
}
|
||||
|
||||
$element.on('click', doSomeWork);
|
||||
doSomeWork(); // << this will throw an exception because templates are compiled within $apply
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
The fix for the example above looks like this:
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
// do work here, and update the model
|
||||
}
|
||||
|
||||
$element.on('click', function() {
|
||||
$scope.$apply(doSomeWork); // <<< the $apply call was moved to the callsite that doesn't execute in $apply call already
|
||||
link: function($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) { $element[0].focus(); }
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
doSomeWork();
|
||||
If we applied this directive to an input which also used the `ngFocus` directive to trigger some
|
||||
work when the element receives focus we will have a problem:
|
||||
|
||||
```
|
||||
<input set-focus-if="hasFocus" ng-focus="msg='has focus'">
|
||||
<button ng-click="hasFocus = true">Focus</button>
|
||||
```
|
||||
|
||||
In this setup, there are two ways to trigger ngFocus. First from a user interaction:
|
||||
|
||||
* Click on the input control
|
||||
* The input control gets focus
|
||||
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
|
||||
`$apply()`
|
||||
|
||||
Second programmatically:
|
||||
|
||||
* Click the button
|
||||
* The `ngClick` directive sets the value of `$scope.hasFocus` to true inside a call to `$apply`
|
||||
* The `$digest` runs, which triggers the watch inside the `setFocusIf` directive
|
||||
* The watch's handle runs, which gives the focus to the input
|
||||
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
|
||||
`$apply()`
|
||||
|
||||
In this second scenario, we are already inside a `$digest` when the ngFocus directive makes another
|
||||
call to `$apply()`, causing this error to be thrown.
|
||||
|
||||
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
|
||||
by using `$timeOut(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
|
||||
`$apply` block:
|
||||
|
||||
```
|
||||
myApp.directive('setFocusIf', function($timeout) {
|
||||
return {
|
||||
link: function($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) {
|
||||
$timeout(function() {
|
||||
// We must reevaluate the value in case it was changed by a subsequent
|
||||
// watch handler in the digest.
|
||||
if ( $scope.$eval($attr.setFocusIf) ) {
|
||||
$element[0].focus();
|
||||
}
|
||||
}, 0, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
To learn more about Angular processing model please check out the {@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
|
||||
## Diagnosing This Error
|
||||
|
||||
When you get this error it can be rather daunting to diagnose the cause of the issue. The best
|
||||
course of action is to investigate the stack trace from the error. You need to look for places
|
||||
where `$apply` or `$digest` have been called and find the context in which this occurred.
|
||||
|
||||
There should be two calls:
|
||||
|
||||
* The first call is the good `$apply`/`$digest` and would normally be triggered by some event near
|
||||
the top of the call stack.
|
||||
|
||||
* The second call is the bad `$apply`/`$digest` and this is the one to investigate.
|
||||
|
||||
Once you have identified this call you work your way up the stack to see what the problem is.
|
||||
|
||||
* If the second call was made in your application code then you should look at why this code has been
|
||||
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
sync/async scenario described earlier.
|
||||
|
||||
* If the second call was made inside an Angular directive then it is likely that it matches the second
|
||||
programmatic event trigger scenario described earlier. In this case you may need to look further up
|
||||
the tree to what triggered the event in the first place.
|
||||
|
||||
### Example Problem
|
||||
|
||||
Let's look at how to investigate this error using the `setFocusIf` example from above. This example
|
||||
defines a new `setFocusIf` directive that sets the focus on the element where it is defined when the
|
||||
value of its attribute becomes true.
|
||||
|
||||
<example name="error-$rootScope-inprog" module="app">
|
||||
<file name="index.html">
|
||||
<button ng-click="focusInput = true">Focus</button>
|
||||
<input ng-focus="count = count + 1" set-focus-if="focusInput" />
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('app', []).directive('setFocusIf', function() {
|
||||
return function link($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) { $element[0].focus(); }
|
||||
});
|
||||
};
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
When you click on the button to cause the focus to occur we get our `$rootScope:inprog` error. The
|
||||
stacktrace looks like this:
|
||||
|
||||
```
|
||||
Error: [$rootScope:inprog]
|
||||
at Error (native)
|
||||
at angular.min.js:6:467
|
||||
at n (angular.min.js:105:60)
|
||||
at g.$get.g.$apply (angular.min.js:113:195)
|
||||
at HTMLInputElement.<anonymous> (angular.min.js:198:401)
|
||||
at angular.min.js:32:32
|
||||
at Array.forEach (native)
|
||||
at q (angular.min.js:7:295)
|
||||
at HTMLInputElement.c (angular.min.js:32:14)
|
||||
at Object.fn (app.js:12:38) angular.js:10111
|
||||
(anonymous function) angular.js:10111
|
||||
$get angular.js:7412
|
||||
$get.g.$apply angular.js:12738 <--- $apply
|
||||
(anonymous function) angular.js:19833 <--- called here
|
||||
(anonymous function) angular.js:2890
|
||||
q angular.js:320
|
||||
c angular.js:2889
|
||||
(anonymous function) app.js:12
|
||||
$get.g.$digest angular.js:12469
|
||||
$get.g.$apply angular.js:12742 <--- $apply
|
||||
(anonymous function) angular.js:19833 <--- called here
|
||||
(anonymous function) angular.js:2890
|
||||
q angular.js:320
|
||||
```
|
||||
|
||||
We can see (even though the Angular code is minified) that there were two calls to `$apply`, first
|
||||
on line `19833`, then on line `12738` of `angular.js`.
|
||||
|
||||
It is this second call that caused the error. If we look at the angular.js code, we can see that
|
||||
this call is made by an Angular directive.
|
||||
|
||||
```
|
||||
var ngEventDirectives = {};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
return {
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr[directiveName]);
|
||||
return function(scope, element, attr) {
|
||||
element.on(lowercase(name), function(event) {
|
||||
scope.$apply(function() {
|
||||
fn(scope, {$event:event});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
It is not possible to tell which from the stack trace, but we happen to know in this case that it is
|
||||
the `ngFocus` directive.
|
||||
|
||||
Now look up the stack to see that our application code is only entered once in `app.js` at line `12`.
|
||||
This is where our problem is:
|
||||
|
||||
```
|
||||
10: link: function($scope, $element, $attr) {
|
||||
11: $scope.$watch($attr.setFocusIf, function(value) {
|
||||
12: if ( value ) { $element[0].focus(); } <---- This is the source of the problem
|
||||
13: });
|
||||
14: }
|
||||
```
|
||||
|
||||
We can now see that the second `$apply` was caused by us programmatically triggering a DOM event
|
||||
(i.e. focus) to occur. We must fix this by moving the code outside of the $apply block using
|
||||
`$timeout` as described above.
|
||||
|
||||
## Further Reading
|
||||
To learn more about Angular processing model please check out the
|
||||
{@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
|
||||
|
||||
@@ -13,3 +13,4 @@ Angular template from a URL requires that the URL is one considered safe for loa
|
||||
This helps prevent XSS and other security issues. Read more at {@link
|
||||
api/ng.$sce Strict Contextual Escaping (SCE)}
|
||||
|
||||
You may want to include the ngSanitize module to use the automatic sanitizing.
|
||||
|
||||
@@ -91,7 +91,7 @@ Here is an example of manually initializing Angular:
|
||||
<html>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
|
||||
|
||||
<script>
|
||||
angular.module('myApp', [])
|
||||
|
||||
@@ -9,7 +9,7 @@ When the model changes, the view reflects the change, and vice versa.
|
||||
|
||||
## Data Binding in Classical Template Systems
|
||||
|
||||
<img class="right" src="img/One_Way_Data_Binding.png"/>
|
||||
<img class="right" src="img/One_Way_Data_Binding.png"/><br />
|
||||
Most templating systems bind data in only one direction: they merge template and model components
|
||||
together into a view. After the merge occurs, changes to the model
|
||||
or related sections of the view are NOT automatically reflected in the view. Worse, any changes
|
||||
@@ -18,7 +18,7 @@ to write code that constantly syncs the view with the model and the model with t
|
||||
|
||||
## Data Binding in Angular Templates
|
||||
|
||||
<img class="right" src="img/Two_Way_Data_Binding.png"/>
|
||||
<img class="right" src="img/Two_Way_Data_Binding.png"/><br />
|
||||
Angular templates work differently. First the template (which is the uncompiled HTML along with
|
||||
any additional markup or directives) is compiled on the browser. The compilation step produces a
|
||||
live view. Any changes to the view are immediately reflected in the model, and any changes in
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
A filter formats the value of an expression for display to the user. They can be used in view templates,
|
||||
controllers or services and it is easy to define your own filter.
|
||||
|
||||
The underlying API is the {@link ng.$filterProvider filterProvider}.
|
||||
The underlying API is the {@link ng.$filterProvider `filterProvider`}.
|
||||
|
||||
## Using filters in view templates
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
|
||||
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
|
||||
* **Maps:** [UI-Map (Google Maps)](https://github.com/angular-ui/ui-map)
|
||||
|
||||
## Deployment
|
||||
|
||||
@@ -112,6 +113,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
|
||||
@@ -130,7 +130,7 @@ injection of `$window`, `$scope`, and our `notify` service:
|
||||
</example>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming)) your
|
||||
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming)) your
|
||||
code, your variable names will get renamed unless you use one of the annotation techniques above.
|
||||
</div>
|
||||
|
||||
@@ -299,5 +299,5 @@ it('should clear messages after alert', function() {
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link ./ng Angular Service API}
|
||||
* {@link ./api/ng/service Angular Service API}
|
||||
* {@link angular.injector Injector API}
|
||||
|
||||
@@ -105,9 +105,12 @@ Check the version of Node.js that you have installed by running the following co
|
||||
node --version
|
||||
```
|
||||
|
||||
Or in Debian based distributions:
|
||||
In Debian based distributions, there is a name clash with another utility called `node`. The
|
||||
suggested solution is to also install the `nodejs-legacy` apt package, which renames `node` to
|
||||
`nodejs`.
|
||||
|
||||
```
|
||||
apt-get install nodejs-legacy
|
||||
nodejs --version
|
||||
```
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ for this test run.
|
||||
Note that we call the helper function, `inject(function(checkmarkFilter) { ... })`, to get
|
||||
access to the filter that we want to test. See {@link angular.mock.inject angular.mock.inject()}.
|
||||
|
||||
Notice that the suffix 'Filter' is appended to your filter name when injected.
|
||||
See the {@link guide/filter#using-filters-in-controllers-services-and-directives Filter Guide}
|
||||
section where this is outlined.
|
||||
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
<pre>Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)</pre>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"uppercase": false,
|
||||
"manualLowercase": false,
|
||||
"manualUppercase": false,
|
||||
"nodeName_": false,
|
||||
"isArrayLike": false,
|
||||
"forEach": false,
|
||||
"sortedKeys": false,
|
||||
|
||||
+8
-4
@@ -510,10 +510,14 @@ function isDate(value) {
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Array`.
|
||||
*/
|
||||
function isArray(value) {
|
||||
return toString.call(value) === '[object Array]';
|
||||
}
|
||||
|
||||
var isArray = (function() {
|
||||
if (!isFunction(Array.isArray)) {
|
||||
return function(value) {
|
||||
return toString.call(value) === '[object Array]';
|
||||
};
|
||||
}
|
||||
return Array.isArray;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
|
||||
@@ -724,7 +724,8 @@ function createInjector(modulesToLoad) {
|
||||
function getService(serviceName) {
|
||||
if (cache.hasOwnProperty(serviceName)) {
|
||||
if (cache[serviceName] === INSTANTIATING) {
|
||||
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
|
||||
throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
|
||||
serviceName + ' <- ' + path.join(' <- '));
|
||||
}
|
||||
return cache[serviceName];
|
||||
} else {
|
||||
|
||||
+5
-3
@@ -652,6 +652,7 @@ forEach({
|
||||
*/
|
||||
JQLite.prototype[name] = function(arg1, arg2) {
|
||||
var i, key;
|
||||
var nodeCount = this.length;
|
||||
|
||||
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
|
||||
// in a way that survives minification.
|
||||
@@ -661,7 +662,7 @@ forEach({
|
||||
if (isObject(arg1)) {
|
||||
|
||||
// we are a write, but the object properties are the key/values
|
||||
for (i = 0; i < this.length; i++) {
|
||||
for (i = 0; i < nodeCount; i++) {
|
||||
if (fn === jqLiteData) {
|
||||
// data() takes the whole object in jQuery
|
||||
fn(this[i], arg1);
|
||||
@@ -675,9 +676,10 @@ forEach({
|
||||
return this;
|
||||
} else {
|
||||
// we are a read, so read the first child.
|
||||
// TODO: do we still need this?
|
||||
var value = fn.$dv;
|
||||
// Only if we have $dv do we iterate over all, otherwise it is just the first element.
|
||||
var jj = (value === undefined) ? Math.min(this.length, 1) : this.length;
|
||||
var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
|
||||
for (var j = 0; j < jj; j++) {
|
||||
var nodeValue = fn(this[j], arg1, arg2);
|
||||
value = value ? value + nodeValue : nodeValue;
|
||||
@@ -686,7 +688,7 @@ forEach({
|
||||
}
|
||||
} else {
|
||||
// we are a write, so apply to all children
|
||||
for (i = 0; i < this.length; i++) {
|
||||
for (i = 0; i < nodeCount; i++) {
|
||||
fn(this[i], arg1, arg2);
|
||||
}
|
||||
// return self for chaining
|
||||
|
||||
+4
-4
@@ -44,7 +44,7 @@ function setupModuleLoader(window) {
|
||||
*
|
||||
* # Module
|
||||
*
|
||||
* A module is a collection of services, directives, filters, and configuration information.
|
||||
* A module is a collection of services, directives, controllers, filters, and configuration information.
|
||||
* `angular.module` is used to configure the {@link auto.$injector $injector}.
|
||||
*
|
||||
* ```js
|
||||
@@ -72,9 +72,9 @@ function setupModuleLoader(window) {
|
||||
* {@link angular.bootstrap} to simplify this process for you.
|
||||
*
|
||||
* @param {!string} name The name of the module to create or retrieve.
|
||||
<<<<<* @param {!Array.<string>=} requires If specified then new module is being created. If
|
||||
>>>>>* unspecified then the module is being retrieved for further configuration.
|
||||
* @param {Function} configFn Optional configuration function for the module. Same as
|
||||
* @param {!Array.<string>=} requires If specified then new module is being created. If
|
||||
* unspecified then the module is being retrieved for further configuration.
|
||||
* @param {Function=} configFn Optional configuration function for the module. Same as
|
||||
* {@link angular.Module#config Module#config()}.
|
||||
* @returns {module} new module with the {@link angular.Module} api.
|
||||
*/
|
||||
|
||||
+69
-39
@@ -830,7 +830,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
safeAddClass($compileNodes, 'ng-scope');
|
||||
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
|
||||
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
// and sometimes changes the structure of the DOM.
|
||||
@@ -852,7 +852,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
|
||||
return $linkNode;
|
||||
};
|
||||
}
|
||||
@@ -907,7 +907,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
!childNodes.length)
|
||||
? null
|
||||
: compileNodes(childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
nodeLinkFn ? (
|
||||
(nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
|
||||
&& nodeLinkFn.transclude) : transcludeFn);
|
||||
|
||||
linkFns.push(nodeLinkFn, childLinkFn);
|
||||
linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
|
||||
@@ -918,8 +920,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// return a linking function if we have found anything, null otherwise
|
||||
return linkFnFound ? compositeLinkFn : null;
|
||||
|
||||
function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
|
||||
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
|
||||
function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
|
||||
var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n, childBoundTranscludeFn;
|
||||
|
||||
// copy nodeList so that linking doesn't break due to live list updates.
|
||||
var nodeListLength = nodeList.length,
|
||||
@@ -941,23 +943,32 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
} else {
|
||||
childScope = scope;
|
||||
}
|
||||
childTranscludeFn = nodeLinkFn.transclude;
|
||||
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
|
||||
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
|
||||
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
|
||||
);
|
||||
|
||||
if ( nodeLinkFn.transcludeOnThisElement ) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
|
||||
|
||||
} else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
|
||||
childBoundTranscludeFn = parentBoundTranscludeFn;
|
||||
|
||||
} else if (!parentBoundTranscludeFn && transcludeFn) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
|
||||
|
||||
} else {
|
||||
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
|
||||
childBoundTranscludeFn = null;
|
||||
}
|
||||
|
||||
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
|
||||
|
||||
} else if (childLinkFn) {
|
||||
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
|
||||
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn) {
|
||||
return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
|
||||
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers) {
|
||||
var scopeCreated = false;
|
||||
|
||||
if (!transcludedScope) {
|
||||
@@ -966,12 +977,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
scopeCreated = true;
|
||||
}
|
||||
|
||||
var clone = transcludeFn(transcludedScope, cloneFn, controllers);
|
||||
var clone = transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn);
|
||||
if (scopeCreated) {
|
||||
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
|
||||
clone.on('$destroy', function() { transcludedScope.$destroy(); });
|
||||
}
|
||||
return clone;
|
||||
};
|
||||
|
||||
return boundTranscludeFn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1149,6 +1162,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
templateDirective = previousCompileContext.templateDirective,
|
||||
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
|
||||
hasTranscludeDirective = false,
|
||||
hasTemplate = false,
|
||||
hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
|
||||
$compileNode = templateAttrs.$$element = jqLite(compileNode),
|
||||
directive,
|
||||
@@ -1239,6 +1253,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
if (directive.template) {
|
||||
hasTemplate = true;
|
||||
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
||||
templateDirective = directive;
|
||||
|
||||
@@ -1288,6 +1303,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
if (directive.templateUrl) {
|
||||
hasTemplate = true;
|
||||
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
||||
templateDirective = directive;
|
||||
|
||||
@@ -1296,7 +1312,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
|
||||
templateAttrs, jqCollection, childTranscludeFn, preLinkFns, postLinkFns, {
|
||||
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
|
||||
controllerDirectives: controllerDirectives,
|
||||
newIsolateScopeDirective: newIsolateScopeDirective,
|
||||
templateDirective: templateDirective,
|
||||
@@ -1324,7 +1340,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
||||
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
|
||||
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
|
||||
nodeLinkFn.templateOnThisElement = hasTemplate;
|
||||
nodeLinkFn.transclude = childTranscludeFn;
|
||||
|
||||
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
|
||||
|
||||
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
|
||||
@@ -1719,7 +1738,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
});
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
|
||||
|
||||
|
||||
while(linkQueue.length) {
|
||||
var scope = linkQueue.shift(),
|
||||
beforeTemplateLinkNode = linkQueue.shift(),
|
||||
@@ -1741,8 +1759,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// Copy in CSS classes from original node
|
||||
safeAddClass(jqLite(linkNode), oldClasses);
|
||||
}
|
||||
if (afterTemplateNodeLinkFn.transclude) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
|
||||
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
|
||||
} else {
|
||||
childBoundTranscludeFn = boundTranscludeFn;
|
||||
}
|
||||
@@ -1756,13 +1774,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
});
|
||||
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
||||
var childBoundTranscludeFn = boundTranscludeFn;
|
||||
if (linkQueue) {
|
||||
linkQueue.push(scope);
|
||||
linkQueue.push(node);
|
||||
linkQueue.push(rootElement);
|
||||
linkQueue.push(boundTranscludeFn);
|
||||
linkQueue.push(childBoundTranscludeFn);
|
||||
} else {
|
||||
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn);
|
||||
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
|
||||
}
|
||||
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1787,23 +1809,31 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
|
||||
function addTextInterpolateDirective(directives, text) {
|
||||
var interpolateFn = $interpolate(text, true);
|
||||
if (interpolateFn) {
|
||||
directives.push({
|
||||
priority: 0,
|
||||
compile: valueFn(function textInterpolateLinkFn(scope, node) {
|
||||
var parent = node.parent(),
|
||||
bindings = parent.data('$binding') || [];
|
||||
bindings.push(interpolateFn);
|
||||
safeAddClass(parent.data('$binding', bindings), 'ng-binding');
|
||||
scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
|
||||
node[0].nodeValue = value;
|
||||
});
|
||||
})
|
||||
});
|
||||
function addTextInterpolateDirective(directives, text) {
|
||||
var interpolateFn = $interpolate(text, true);
|
||||
if (interpolateFn) {
|
||||
directives.push({
|
||||
priority: 0,
|
||||
compile: function textInterpolateCompileFn(templateNode) {
|
||||
// when transcluding a template that has bindings in the root
|
||||
// then we don't have a parent and should do this in the linkFn
|
||||
var parent = templateNode.parent(), hasCompileParent = parent.length;
|
||||
if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding');
|
||||
|
||||
return function textInterpolateLinkFn(scope, node) {
|
||||
var parent = node.parent(),
|
||||
bindings = parent.data('$binding') || [];
|
||||
bindings.push(interpolateFn);
|
||||
parent.data('$binding', bindings);
|
||||
if (!hasCompileParent) safeAddClass(parent, 'ng-binding');
|
||||
scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
|
||||
node[0].nodeValue = value;
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getTrustedContext(node, attrNormalizedName) {
|
||||
|
||||
@@ -749,6 +749,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
* patterns defined as scope expressions.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -50,14 +50,19 @@
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngBindDirective = ngDirective(function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBind);
|
||||
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
|
||||
// We are purposefully using == here rather than === because we want to
|
||||
// catch when value is "null or undefined"
|
||||
// jshint -W041
|
||||
element.text(value == undefined ? '' : value);
|
||||
});
|
||||
var ngBindDirective = ngDirective({
|
||||
compile: function(templateElement) {
|
||||
templateElement.addClass('ng-binding');
|
||||
return function (scope, element, attr) {
|
||||
element.data('$binding', attr.ngBind);
|
||||
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
|
||||
// We are purposefully using == here rather than === because we want to
|
||||
// catch when value is "null or undefined"
|
||||
// jshint -W041
|
||||
element.text(value == undefined ? '' : value);
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* MVC components in angular:
|
||||
*
|
||||
* * Model — The Model is scope properties; scopes are attached to the DOM where scope properties
|
||||
* * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
|
||||
* are accessed through bindings.
|
||||
* * View — The template (HTML with data bindings) that is rendered into the View.
|
||||
* * Controller — The `ngController` directive specifies a Controller class; the class contains business
|
||||
|
||||
@@ -45,7 +45,7 @@ forEach(
|
||||
return {
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr[directiveName]);
|
||||
return function(scope, element, attr) {
|
||||
return function ngEventHandler(scope, element) {
|
||||
element.on(lowercase(name), function(event) {
|
||||
scope.$apply(function() {
|
||||
fn(scope, {$event:event});
|
||||
|
||||
+10
-9
@@ -674,14 +674,6 @@ function $HttpProvider() {
|
||||
config.headers = headers;
|
||||
config.method = uppercase(config.method);
|
||||
|
||||
var xsrfValue = urlIsSameOrigin(config.url)
|
||||
? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
|
||||
: undefined;
|
||||
if (xsrfValue) {
|
||||
headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
|
||||
}
|
||||
|
||||
|
||||
var serverRequest = function(config) {
|
||||
headers = config.headers;
|
||||
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
|
||||
@@ -957,8 +949,17 @@ function $HttpProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
// if we won't have the response in cache, send the request to the backend
|
||||
|
||||
// if we won't have the response in cache, set the xsrf headers and
|
||||
// send the request to the backend
|
||||
if (isUndefined(cachedResp)) {
|
||||
var xsrfValue = urlIsSameOrigin(config.url)
|
||||
? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
|
||||
: undefined;
|
||||
if (xsrfValue) {
|
||||
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
|
||||
}
|
||||
|
||||
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
|
||||
config.withCredentials, config.responseType);
|
||||
}
|
||||
|
||||
@@ -744,7 +744,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
* @kind function
|
||||
*
|
||||
* @param {boolean=} value If provided then set the animation on or off.
|
||||
* @param {DOMElement} element If provided then the element will be used to represent the enable/disable operation
|
||||
* @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation
|
||||
* @return {boolean} Current animation state.
|
||||
*
|
||||
* @description
|
||||
|
||||
@@ -522,23 +522,32 @@ angular.module('ngResource', ['ng']).
|
||||
extend({}, extractParams(data, action.params || {}), params),
|
||||
action.url);
|
||||
|
||||
var promise = $http(httpConfig).then(function(response) {
|
||||
var promise = $http(httpConfig).then(function (response) {
|
||||
var data = response.data,
|
||||
promise = value.$promise;
|
||||
promise = value.$promise;
|
||||
|
||||
if (data) {
|
||||
// Need to convert action.isArray to boolean in case it is undefined
|
||||
// jshint -W018
|
||||
if (angular.isArray(data) !== (!!action.isArray)) {
|
||||
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
|
||||
'response to contain an {0} but got an {1}',
|
||||
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
|
||||
throw $resourceMinErr('badcfg',
|
||||
'Error in resource configuration. Expected ' +
|
||||
'response to contain an {0} but got an {1}',
|
||||
action.isArray ? 'array' : 'object',
|
||||
angular.isArray(data) ? 'array' : 'object');
|
||||
}
|
||||
// jshint +W018
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
forEach(data, function (item) {
|
||||
if (typeof item === "object") {
|
||||
value.push(new Resource(item));
|
||||
} else {
|
||||
// Valid JSON values may be string literals, and these should not be converted
|
||||
// into objects. These items will not have access to the Resource prototype
|
||||
// methods, but unfortunately there
|
||||
value.push(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
shallowClearAndCopy(data, value);
|
||||
|
||||
@@ -630,7 +630,7 @@ describe('injector', function() {
|
||||
$provide.factory('service', function(service){});
|
||||
return function(service) {};
|
||||
}]);
|
||||
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service');
|
||||
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service <- service');
|
||||
});
|
||||
|
||||
|
||||
@@ -641,7 +641,7 @@ describe('injector', function() {
|
||||
$provide.factory('b', function(a){});
|
||||
return function(a) {};
|
||||
}]);
|
||||
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: b <- a');
|
||||
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: a <- b <- a');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -3764,6 +3764,88 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not pass transclusion into a template directive when the directive didn\'t request transclusion', function() {
|
||||
|
||||
module(function($compileProvider) {
|
||||
|
||||
$compileProvider.directive('transFoo', valueFn({
|
||||
template: '<div>' +
|
||||
'<div no-trans-bar></div>' +
|
||||
'<div ng-transclude>this one should get replaced with content</div>' +
|
||||
'<div class="foo" ng-transclude></div>' +
|
||||
'</div>',
|
||||
transclude: true
|
||||
|
||||
}));
|
||||
|
||||
$compileProvider.directive('noTransBar', valueFn({
|
||||
template: '<div>' +
|
||||
// This ng-transclude is invalid. It should throw an error.
|
||||
'<div class="bar" ng-transclude></div>' +
|
||||
'</div>',
|
||||
transclude: false
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
var message = 'Illegal use of ngTransclude directive in the template! No parent ' +
|
||||
'directive that requires a transclusion found. Element: <div class="bar" ' +
|
||||
'ng-transclude="">';
|
||||
if (msie <= 8) {
|
||||
// MSIE ヽ(`Д´)ノ
|
||||
message = 'Illegal use of ngTransclude directive in the template! No parent ' +
|
||||
'directive that requires a transclusion found. Element: <div class=bar ' +
|
||||
'ng-transclude>';
|
||||
}
|
||||
expect(function() {
|
||||
$compile('<div trans-foo>content</div>')($rootScope);
|
||||
}).toThrowMinErr('ngTransclude', 'orphan', message);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not pass transclusion into a templateUrl directive', function() {
|
||||
|
||||
module(function($compileProvider) {
|
||||
|
||||
$compileProvider.directive('transFoo', valueFn({
|
||||
template: '<div>' +
|
||||
'<div no-trans-bar></div>' +
|
||||
'<div ng-transclude>this one should get replaced with content</div>' +
|
||||
'<div class="foo" ng-transclude></div>' +
|
||||
'</div>',
|
||||
transclude: true
|
||||
|
||||
}));
|
||||
|
||||
$compileProvider.directive('noTransBar', valueFn({
|
||||
templateUrl: 'noTransBar.html',
|
||||
transclude: false
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('noTransBar.html',
|
||||
'<div>' +
|
||||
// This ng-transclude is invalid. It should throw an error.
|
||||
'<div class="bar" ng-transclude></div>' +
|
||||
'</div>');
|
||||
var message = 'Illegal use of ngTransclude directive in the template! No parent directive that '
|
||||
+ 'requires a transclusion found. Element: <div class="bar" ng-transclude="">';
|
||||
if (msie <= 8) {
|
||||
// MSIE ヽ(`Д´)ノ
|
||||
message = 'Illegal use of ngTransclude directive in the template! No parent directive that '
|
||||
+ 'requires a transclusion found. Element: <div class=bar ng-transclude>';
|
||||
}
|
||||
expect(function() {
|
||||
element = $compile('<div trans-foo>content</div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
}).toThrowMinErr('ngTransclude', 'orphan', message);
|
||||
});
|
||||
});
|
||||
|
||||
it('should make the result of a transclusion available to the parent directive in post-linking phase' +
|
||||
'(template)', function() {
|
||||
module(function() {
|
||||
@@ -3982,6 +4064,182 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('nested transcludes', function() {
|
||||
|
||||
beforeEach(module(function($compileProvider) {
|
||||
|
||||
$compileProvider.directive('noop', valueFn({}));
|
||||
|
||||
$compileProvider.directive('sync', valueFn({
|
||||
template: '<div ng-transclude></div>',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('async', valueFn({
|
||||
templateUrl: 'async',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('syncSync', valueFn({
|
||||
template: '<div noop><div sync><div ng-transclude></div></div></div>',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('syncAsync', valueFn({
|
||||
template: '<div noop><div async><div ng-transclude></div></div></div>',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('asyncSync', valueFn({
|
||||
templateUrl: 'asyncSync',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('asyncAsync', valueFn({
|
||||
templateUrl: 'asyncAsync',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($templateCache) {
|
||||
$templateCache.put('async', '<div ng-transclude></div>');
|
||||
$templateCache.put('asyncSync', '<div noop><div sync><div ng-transclude></div></div></div>');
|
||||
$templateCache.put('asyncAsync', '<div noop><div async><div ng-transclude></div></div></div>');
|
||||
}));
|
||||
|
||||
|
||||
it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) {
|
||||
element = $compile('<div sync-sync>transcluded content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) {
|
||||
element = $compile('<div sync-async>transcluded content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) {
|
||||
element = $compile('<div async-sync>transcluded content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) {
|
||||
element = $compile('<div async-async>transcluded content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('nested isolated scope transcludes', function() {
|
||||
beforeEach(module(function($compileProvider) {
|
||||
|
||||
$compileProvider.directive('trans', valueFn({
|
||||
restrict: 'E',
|
||||
template: '<div ng-transclude></div>',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('transAsync', valueFn({
|
||||
restrict: 'A',
|
||||
templateUrl: 'transAsync',
|
||||
transclude: true
|
||||
}));
|
||||
|
||||
$compileProvider.directive('iso', valueFn({
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
template: '<div trans><span ng-transclude></span></div>',
|
||||
scope: {}
|
||||
}));
|
||||
$compileProvider.directive('isoAsync1', valueFn({
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
template: '<div trans-async><span ng-transclude></span></div>',
|
||||
scope: {}
|
||||
}));
|
||||
$compileProvider.directive('isoAsync2', valueFn({
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
templateUrl: 'isoAsync',
|
||||
scope: {}
|
||||
}));
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($templateCache) {
|
||||
$templateCache.put('transAsync', '<div ng-transclude></div>');
|
||||
$templateCache.put('isoAsync', '<div trans-async><span ng-transclude></span></div>');
|
||||
}));
|
||||
|
||||
|
||||
it('should pass the outer scope to the transclude on the isolated template sync-sync', inject(function($compile, $rootScope) {
|
||||
|
||||
$rootScope.val = 'transcluded content';
|
||||
element = $compile('<div iso><span ng-bind="val"></span></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
it('should pass the outer scope to the transclude on the isolated template async-sync', inject(function($compile, $rootScope) {
|
||||
|
||||
$rootScope.val = 'transcluded content';
|
||||
element = $compile('<div iso-async1><span ng-bind="val"></span></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
it('should pass the outer scope to the transclude on the isolated template async-async', inject(function($compile, $rootScope) {
|
||||
|
||||
$rootScope.val = 'transcluded content';
|
||||
element = $compile('<div iso-async2><span ng-bind="val"></span></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('transcluded content');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('multiple siblings receiving transclusion', function() {
|
||||
|
||||
it("should only receive transclude from parent", function() {
|
||||
|
||||
module(function($compileProvider) {
|
||||
|
||||
$compileProvider.directive('myExample', valueFn({
|
||||
scope: {},
|
||||
link: function link(scope, element, attrs) {
|
||||
var foo = element[0].querySelector('.foo');
|
||||
scope.children = angular.element(foo).children().length;
|
||||
},
|
||||
template: '<div>' +
|
||||
'<div>myExample {{children}}!</div>' +
|
||||
'<div ng-if="children">has children</div>' +
|
||||
'<div class="foo" ng-transclude></div>' +
|
||||
'</div>',
|
||||
transclude: true
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
var element = $compile('<div my-example></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('myExample 0!');
|
||||
dealoc(element);
|
||||
|
||||
element = $compile('<div my-example><p></p></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('myExample 1!has children');
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4318,6 +4576,57 @@ describe('$compile', function() {
|
||||
|
||||
expect(element.text()).toBe('-->|x|');
|
||||
}));
|
||||
|
||||
|
||||
// See https://github.com/angular/angular.js/issues/7183
|
||||
it("should pass transclusion through to template of a 'replace' directive", function() {
|
||||
module(function() {
|
||||
directive('transSync', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
link: function(scope, element, attr, ctrl, transclude) {
|
||||
|
||||
expect(transclude).toEqual(jasmine.any(Function));
|
||||
|
||||
transclude(function(child) { element.append(child); });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
directive('trans', function($timeout) {
|
||||
return {
|
||||
transclude: true,
|
||||
link: function(scope, element, attrs, ctrl, transclude) {
|
||||
|
||||
// We use timeout here to simulate how ng-if works
|
||||
$timeout(function() {
|
||||
transclude(function(child) { element.append(child); });
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
directive('replaceWithTemplate', function() {
|
||||
return {
|
||||
templateUrl: "template.html",
|
||||
replace: true
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope, $templateCache, $timeout) {
|
||||
|
||||
$templateCache.put('template.html', '<div trans-sync>Content To Be Transcluded</div>');
|
||||
|
||||
expect(function() {
|
||||
element = $compile('<div><div trans><div replace-with-template></div></div></div>')($rootScope);
|
||||
$timeout.flush();
|
||||
}).not.toThrow();
|
||||
|
||||
expect(element.text()).toEqual('Content To Be Transcluded');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -199,6 +199,28 @@ describe('ngIf and transcludes', function() {
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should use the correct transcluded scope', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.directive('iso', valueFn({
|
||||
link: function(scope) {
|
||||
scope.val = 'value in iso scope';
|
||||
},
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
template: '<div ng-if="true">val={{val}}-<div ng-transclude></div></div>',
|
||||
scope: {}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
$rootScope.val = 'transcluded content';
|
||||
var element = $compile('<div iso><span ng-bind="val"></span></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(trim(element.text())).toEqual('val=value in iso scope-transcluded content');
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngIf animations', function () {
|
||||
|
||||
@@ -860,6 +860,25 @@ describe('$http', function() {
|
||||
|
||||
$httpBackend.flush();
|
||||
}));
|
||||
|
||||
it('should check the cache before checking the XSRF cookie', inject(function($browser, $cacheFactory) {
|
||||
var testCache = $cacheFactory('testCache'),
|
||||
executionOrder = [];
|
||||
|
||||
spyOn($browser, 'cookies').andCallFake(function() {
|
||||
executionOrder.push('cookies');
|
||||
return {'XSRF-TOKEN':'foo'};
|
||||
});
|
||||
spyOn(testCache, 'get').andCallFake(function() {
|
||||
executionOrder.push('cache');
|
||||
});
|
||||
|
||||
$httpBackend.expect('GET', '/url', undefined).respond('');
|
||||
$http({url: '/url', method: 'GET', cache: testCache});
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(executionOrder).toEqual(['cache', 'cookies']);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1044,6 +1044,27 @@ describe("resource", function() {
|
||||
$httpBackend.flush();
|
||||
expect(user).toEqualData([ {id: 1, name: 'user1'} ]);
|
||||
});
|
||||
|
||||
it('should not convert string literals in array into Resource objects', function() {
|
||||
$httpBackend.expect('GET', '/names.json').respond(["mary", "jane"]);
|
||||
var strings = $resource('/names.json').query();
|
||||
$httpBackend.flush();
|
||||
expect(strings).toEqualData(["mary", "jane"]);
|
||||
});
|
||||
|
||||
it('should not convert number literals in array into Resource objects', function() {
|
||||
$httpBackend.expect('GET', '/names.json').respond([213, 456]);
|
||||
var numbers = $resource('/names.json').query();
|
||||
$httpBackend.flush();
|
||||
expect(numbers).toEqualData([213, 456]);
|
||||
});
|
||||
|
||||
it('should not convert boolean literals in array into Resource objects', function() {
|
||||
$httpBackend.expect('GET', '/names.json').respond([true, false]);
|
||||
var bools = $resource('/names.json').query();
|
||||
$httpBackend.flush();
|
||||
expect(bools).toEqualData([true, false]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', function(){
|
||||
|
||||
Reference in New Issue
Block a user