BEAKING CHANGE:
Lazy-binding now happens on the scope watcher level.
What this means is that given `parseFn = $parse('::foo')`,
bind-once will only kick in when `parseFn` is being watched by a scope
(i.e. `scope.$watch(parseFn)`)
Bind-once will have no effect when directily invoking `parseFn` (i.e. `parseFn()`)
If an element contains two "element" transcludes then the initial clone
consists of only comment nodes. The concern was that this meant that
the transclude scopes would not be cleaned up.
But it turns out that in the case that there are only comments then the
scope is never attached to anything so we don't need to worry about cleaning
it up.
Later if a concrete element is created as part of the transclude then these
elements will have destroy handlers.
Previously, <element ng-attr-foo="{{binding}}" foo="bar"></element>'s "foo" attribute would always
equal "bar", because the bound version was overwritten. This CL corrects this behaviour and ensures
that the ordering of attributes does not have an effect on whether or not ng-attr-bound attributes
do their work.
Closes#7739
In apps that create lots of scopes (apps with large tables) the uid generation
shows up in the profiler and adds a few milliseconds. Using simple counter
doesn't have this overhead.
I think the initial fear of overflowing and thus using string alphanum sequence
is unjustified because even if an app was to create lots of scopes non-stop,
you could create about 28.6 million scopes per seconds for 10 years before
you would reach a number that can't be accurately represented in JS
BREAKING CHANGE: Scope#$id is now of time number rather than string. Since the
id is primarily being used for debugging purposes this change should not affect
anyone.
If a "replace" directive has an async template, which contains a transclusion
directive at its root node, then outer transclusions were failing to be
passed to this directive. An example would be uses of `ngIf` inside and
outside the template.
Collaborated with @caitp
Closes#7183Closes#7772
Previously, the compiler would throw an error if a directive requested new non-isolate scope
after a directive had requested isolate scope. But it would not error if a directive
requested an isolate scope after a directive had requested a new non-isolate scope.
Since it is invalid to have more than one directive request any kind of scope if one of
them has requested isolate scope, then the compiler should error whatever order the
directives are applied.
This fix addresses this situation by throwing error regardless of order of directives.
BREAKING CHANGE:
Requesting isolate scope and any other scope on a single element is an error.
Before this change, the compiler let two directives request a child scope
and an isolate scope if the compiler applied them in the order of non-isolate
scope directive followed by isolate scope directive.
Now the compiler will error regardless of the order.
If you find that your code is now throwing a `$compile:multidir` error,
check that you do not have directives on the same element that are trying
to request both an isolate and a non-isolate scope and fix your code.
Closes#4402Closes#4421
If a directive provides a template but is not explicitly requesting transclusion
then the compiler should not pass a transclusion function to the directives
within the template.
If you have two directives that both expect to receive transcluded content
the outer directive works but the inner directive never receives a
transclusion function. This only failed if the first transclude directive
was not the first directive found in compilation.
Handles the regression identified in e994259739Fixes#7240Closes#7387
Expressions that start with `::` will be binded once. The rule
that binding follows is that the binding will take the first
not-undefined value at the end of a $digest cycle.
Watchers from $watch, $watchCollection and $watchGroup will
automatically stop watching when the expression(s) are bind-once
and fulfill.
Watchers from text and attributes interpolations will
automatically stop watching when the expressions are fulfill.
All directives that use $parse for expressions will automatically
work with bind-once expressions. E.g.
<div ng-bind="::foo"></div>
<li ng-repeat="item in ::items">{{::item.name}};</li>
Paired with: Caitlin and Igor
Design doc: https://docs.google.com/document/d/1fTqaaQYD2QE1rz-OywvRKFSpZirbWUPsnfaZaMq8fWI/edit#Closes#7486Closes#5408
If a directives specifies `replace:true` and the template of the directive contains
a root element with an attribute which already exists at the place
where the directive is used with the same value, don't duplicate the value.
Closes#7463
If you have two directives that both expect to receive transcluded content
the outer directive works but the inner directive never receives a
transclusion function. This only failed if the first transclude directive
was not the first directive found in compilation.
Fixes#7240Closes#7387
All isolated scope directives that do not have `templateUrl` were marked
as `$isolateScopeNoTemplate` even if they did have a `template` attribute.
This caused `jqLite#scope()` to return the wrong value for child elements
within the directive's template.
Closes#6942
Previously, templates would always be assumed to be valid HTML nodes. In some cases, it is
desirable to use SVG or MathML or some other language.
For the time being, this change is only truly meaningful for SVG elements, as MathML has
very limited browser support. But in the future, who knows?
Closes#7265
Previously, ctreq would possibly reference the incorrect directive name,
due to relying on a directiveName living outside of the closure which
throws the exception, which can change before the call is ever made.
This change saves the current value of directiveName as a property of
the link function, which prevents this from occurring.
Closes#7062Closes#7067
In order to make the behavior compatible with $rootScope.$watch and $rootScope.$on methods, and
make it possible to deregister an attribute observer, Attributes.$observe method now returns a
deregistration function instead of the observer itself.
BREAKING CHANGE: calling attr.$observe no longer returns the observer function, but a
deregistration function instead.
To migrate the code follow the example below:
Before:
```
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = attr.$observe('someAttr', function(value) {
console.log(value);
});
}
};
});
```
After:
```
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = function(value) {
console.log(value);
};
attr.$observe('someAttr', observer);
}
};
});
```
Closes#5609
BREAKING CHANGE: ngClass and {{ class }} will now call the `setClass`
animation callback instead of addClass / removeClass when both a
addClass/removeClass operation is being executed on the element during the animation.
Please include the setClass animation callback as well as addClass and removeClass within
your JS animations to work with ngClass and {{ class }} directives.
Closes#6019
If the first element in a template is a <tr>, <th>, <td>, or <tbody> tag,
the HTML compiler will ensure that the template is wrapped in a <table>
element so that the table content is not discarded.
Closes#2848Closes#1459Closes#3647Closes#3241
This corrects a complicated compiler issue, described in detail below:
Previously, if an element transclusion directive contained an asynchronous directive whose template
contained another element transclusion directive, the inner element transclusion directive would be
linked with the element, rather than the expected comment node.
An example manifestation of this bug would look like so:
```html
<div ng-repeat="i in [1,2,3,4,5]">
<div my-directive>
</div>
</div>
```
`my-directive` would be a replace directive, and its template would contain another element
transclusion directive, like so:
```html
<div ng-if="true">{{i}}</div>
```
ngIf would be linked with this template content, rather than the comment node, and the template element
would be attached to the DOM, rather than the comment. As a result, this caused ng-if to duplicate the
template when its expression evaluated to true.
Closes#6006Closes#6101
The flushNext method of testing is difficult and highly coupled with the behavior
of ngAnimate's $animate workflow. It is much better instead to just queue all
$animate animation calls into a queue collection which is available on the $animate
service when mock.animate is included as a module within test code.
Previously, classes added to asynchronous directive elements during the clone
attach function would not persist after the node is merged with the template, prior
to linking. This change corrects this behaviour and brings it in line with synchronous
directives.
Closes#5439Closes#5617
FF 26.0 now throws:
"TypeError: NodeList doesn't have an indexed property setter."
when we try to assign to `childNodes[1]`, since this test still works properly
on Chrome and the issue being tested is not a cross-browser issues, I'm
just making the patchability check more robust instead of trying to figure
out how to make this test fully pass on FF.
It appears that this exceptional case was only valid for IE<8 and that for IE>=8 it
was actually causing a bug with the `ng-href-attr` directive on `<a>` elements.
Closes#5479
jQuery's elem.html('') is way slower than elem.empty(). As clearing
element contents happens quite often in certain scenarios, switching
to using .empty() provides a significant performance boost when using
Angular with jQuery.
Closes#4457
When a component uses an isolate scope reference
and the the component is used with an object literal
a new object is created on every evaluation.
Therefore the compiler needs to compare
the values of the parent and the isolate scope
using object equality and not object reference
equality.
Fixes#5296.
If an element has a directive whose content is loaded using `templateUrl`,
and the element is cloned using a linking function before the template arrives,
the clone needs to be updated as well.
This also updates `ngIf` and `ngRepeat` to keep the connection to the clone
of a tranclude function, so that they know about the changes a directive with
`templateUrl` does to the element in the future.
Fixes to #4930.
When using two-way binding with isolate scope, under some circumstances
the lastValue variable captured in the parentValueWatch function can get
out of sync.
Specifically, if both the value in the origin scope as well as the value
in the isolate scope get independently updated to the same value within
one digest cycle, the lastValue is never updated. This potentially causes
the watch to make the wrong decision as to which side to update on subsequent
passes.
This fixes things by ensuring lastValue is always set to the last seen
value even if the watch's logic was short circuited because there was no
difference between the values in the original and isolate scopes.
Closes#5182
`$sanitize` now uses the same mechanism as `$compile` to validate uris.
By this, the validation in `$sanitize` is more general and can be
configured in the same way as the one in `$compile`.
Changes
- Creates the new private service `$$sanitizeUri`.
- Moves related specs from `compileSpec.js` into `sanitizeUriSpec.js`.
- Refactors the `linky` filter to be less dependent on `$sanitize`
internal functions.
Fixes#3748.
When $compile interpolates a CSS class attribute expression it will
do so by comparing the CSS class value already present on the element.
This may lead to unexpected results when dealing with ngClass values being
added and removed therefore it is best that both compile and ngClass delegate
addClass/removeClass operations to the same block of code.
Additional API (backwards compatible)
- Injects `$transclude` (see directive controllers) as 5th argument to directive link functions.
- `$transclude` takes an optional scope as first parameter that overrides the
bound scope.
Deprecations:
- `transclude` parameter of directive compile functions (use the new parameter for link functions instead).
Refactorings:
- Don't use comment node to temporarily store controllers
- `ngIf`, `ngRepeat`, ... now all use `$transclude`
Closes#4935.