Skip to content

Commit c88aee6

Browse files
Merge pull request #171 from angular/master
Same entity always returned by readFragment
2 parents b460d4a + 09f013a commit c88aee6

File tree

14 files changed

+713
-92
lines changed

14 files changed

+713
-92
lines changed

docs/content/tutorial/step_03.ngdoc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ Let's see an example:
8888
});
8989
```
9090

91+
```html
92+
<body>
93+
<!-- The following line is how to use the `greetUser` component above in your html doc. -->
94+
<greet-user></greet-user>
95+
</body>
96+
```
97+
9198
Now, every time we include `<greet-user></greet-user>` in our view, AngularJS will expand it into a
9299
DOM sub-tree constructed using the provided `template` and managed by an instance of the specified
93100
controller.
@@ -127,7 +134,7 @@ acquired skill.
127134
<body>
128135

129136
<!-- Use a custom component to render a list of phones -->
130-
<phone-list></phone-list>
137+
<phone-list></phone-list> <!-- This tells AngularJS to instantiate a `phoneList` component here. -->
131138

132139
</body>
133140
</html>
@@ -148,7 +155,7 @@ angular.module('phonecatApp', []);
148155
// Register `phoneList` component, along with its associated controller and template
149156
angular.
150157
module('phonecatApp').
151-
component('phoneList', {
158+
component('phoneList', { // This name is what AngularJS uses to match to the `<phone-list>` element.
152159
template:
153160
'<ul>' +
154161
'<li ng-repeat="phone in $ctrl.phones">' +

docs/content/tutorial/step_06.ngdoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ You can now rerun `npm run protractor` to see the tests run.
230230

231231
* Reverse the sort order by adding a `-` symbol before the sorting value:
232232
`<option value="-age">Oldest</option>`
233+
After making this change, you'll notice that the drop-down list has a blank option selected and does not default to age anymore.
234+
Fix this by updating the `orderProp` value in `phone-list.component.js` to match the new value on the `<option>` element.
233235

234236

235237
## Summary

protractor-travis-conf.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ function capabilitiesForSauceLabs(capabilities) {
7979
'browserName': capabilities.browserName,
8080
'platform': capabilities.platform,
8181
'version': capabilities.version,
82-
'elementScrollBehavior': 1
82+
'elementScrollBehavior': 1,
83+
// Allow e2e test sessions to run for a maximum of 35 minutes, instead of the default 30 minutes.
84+
'maxDuration': 2100
8385
};
8486
}

src/ng/compile.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
22312231
this.$$element.removeAttr(attrName);
22322232
} else {
22332233
if (SIMPLE_ATTR_NAME.test(attrName)) {
2234-
this.$$element.attr(attrName, value);
2234+
// jQuery skips special boolean attrs treatment in XML nodes for
2235+
// historical reasons and hence AngularJS cannot freely call
2236+
// `.attr(attrName, false) with such attributes. To avoid issues
2237+
// in XHTML, call `removeAttr` in such cases instead.
2238+
// See https://github.com/jquery/jquery/issues/4249
2239+
if (booleanKey && value === false) {
2240+
this.$$element.removeAttr(attrName);
2241+
} else {
2242+
this.$$element.attr(attrName, value);
2243+
}
22352244
} else {
22362245
setSpecialAttr(this.$$element[0], attrName, value);
22372246
}

src/ng/directive/input.js

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,7 @@ function createDateParser(regexp, mapping) {
14971497
}
14981498

14991499
function createDateInputType(type, regexp, parseDate, format) {
1500-
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
1500+
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
15011501
badInputChecker(scope, element, attr, ctrl, type);
15021502
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
15031503

@@ -1540,24 +1540,34 @@ function createDateInputType(type, regexp, parseDate, format) {
15401540
});
15411541

15421542
if (isDefined(attr.min) || attr.ngMin) {
1543-
var minVal;
1543+
var minVal = attr.min || $parse(attr.ngMin)(scope);
1544+
var parsedMinVal = parseObservedDateValue(minVal);
1545+
15441546
ctrl.$validators.min = function(value) {
1545-
return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
1547+
return !isValidDate(value) || isUndefined(parsedMinVal) || parseDate(value) >= parsedMinVal;
15461548
};
15471549
attr.$observe('min', function(val) {
1548-
minVal = parseObservedDateValue(val);
1549-
ctrl.$validate();
1550+
if (val !== minVal) {
1551+
parsedMinVal = parseObservedDateValue(val);
1552+
minVal = val;
1553+
ctrl.$validate();
1554+
}
15501555
});
15511556
}
15521557

15531558
if (isDefined(attr.max) || attr.ngMax) {
1554-
var maxVal;
1559+
var maxVal = attr.max || $parse(attr.ngMax)(scope);
1560+
var parsedMaxVal = parseObservedDateValue(maxVal);
1561+
15551562
ctrl.$validators.max = function(value) {
1556-
return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
1563+
return !isValidDate(value) || isUndefined(parsedMaxVal) || parseDate(value) <= parsedMaxVal;
15571564
};
15581565
attr.$observe('max', function(val) {
1559-
maxVal = parseObservedDateValue(val);
1560-
ctrl.$validate();
1566+
if (val !== maxVal) {
1567+
parsedMaxVal = parseObservedDateValue(val);
1568+
maxVal = val;
1569+
ctrl.$validate();
1570+
}
15611571
});
15621572
}
15631573

@@ -1709,50 +1719,68 @@ function isValidForStep(viewValue, stepBase, step) {
17091719
return (value - stepBase) % step === 0;
17101720
}
17111721

1712-
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
1722+
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
17131723
badInputChecker(scope, element, attr, ctrl, 'number');
17141724
numberFormatterParser(ctrl);
17151725
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
17161726

1717-
var minVal;
1718-
var maxVal;
1727+
var parsedMinVal;
17191728

17201729
if (isDefined(attr.min) || attr.ngMin) {
1730+
var minVal = attr.min || $parse(attr.ngMin)(scope);
1731+
parsedMinVal = parseNumberAttrVal(minVal);
1732+
17211733
ctrl.$validators.min = function(modelValue, viewValue) {
1722-
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
1734+
return ctrl.$isEmpty(viewValue) || isUndefined(parsedMinVal) || viewValue >= parsedMinVal;
17231735
};
17241736

17251737
attr.$observe('min', function(val) {
1726-
minVal = parseNumberAttrVal(val);
1727-
// TODO(matsko): implement validateLater to reduce number of validations
1728-
ctrl.$validate();
1738+
if (val !== minVal) {
1739+
parsedMinVal = parseNumberAttrVal(val);
1740+
minVal = val;
1741+
// TODO(matsko): implement validateLater to reduce number of validations
1742+
ctrl.$validate();
1743+
}
17291744
});
17301745
}
17311746

17321747
if (isDefined(attr.max) || attr.ngMax) {
1748+
var maxVal = attr.max || $parse(attr.ngMax)(scope);
1749+
var parsedMaxVal = parseNumberAttrVal(maxVal);
1750+
17331751
ctrl.$validators.max = function(modelValue, viewValue) {
1734-
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
1752+
return ctrl.$isEmpty(viewValue) || isUndefined(parsedMaxVal) || viewValue <= parsedMaxVal;
17351753
};
17361754

17371755
attr.$observe('max', function(val) {
1738-
maxVal = parseNumberAttrVal(val);
1739-
// TODO(matsko): implement validateLater to reduce number of validations
1740-
ctrl.$validate();
1756+
if (val !== maxVal) {
1757+
parsedMaxVal = parseNumberAttrVal(val);
1758+
maxVal = val;
1759+
// TODO(matsko): implement validateLater to reduce number of validations
1760+
ctrl.$validate();
1761+
}
17411762
});
17421763
}
17431764

17441765
if (isDefined(attr.step) || attr.ngStep) {
1745-
var stepVal;
1766+
var stepVal = attr.step || $parse(attr.ngStep)(scope);
1767+
var parsedStepVal = parseNumberAttrVal(stepVal);
1768+
17461769
ctrl.$validators.step = function(modelValue, viewValue) {
1747-
return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
1748-
isValidForStep(viewValue, minVal || 0, stepVal);
1770+
return ctrl.$isEmpty(viewValue) || isUndefined(parsedStepVal) ||
1771+
isValidForStep(viewValue, parsedMinVal || 0, parsedStepVal);
17491772
};
17501773

17511774
attr.$observe('step', function(val) {
1752-
stepVal = parseNumberAttrVal(val);
17531775
// TODO(matsko): implement validateLater to reduce number of validations
1754-
ctrl.$validate();
1776+
if (val !== stepVal) {
1777+
parsedStepVal = parseNumberAttrVal(val);
1778+
stepVal = val;
1779+
ctrl.$validate();
1780+
}
1781+
17551782
});
1783+
17561784
}
17571785
}
17581786

@@ -1782,6 +1810,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17821810
originalRender;
17831811

17841812
if (hasMinAttr) {
1813+
minVal = parseNumberAttrVal(attr.min);
1814+
17851815
ctrl.$validators.min = supportsRange ?
17861816
// Since all browsers set the input to a valid value, we don't need to check validity
17871817
function noopMinValidator() { return true; } :
@@ -1794,6 +1824,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17941824
}
17951825

17961826
if (hasMaxAttr) {
1827+
maxVal = parseNumberAttrVal(attr.max);
1828+
17971829
ctrl.$validators.max = supportsRange ?
17981830
// Since all browsers set the input to a valid value, we don't need to check validity
17991831
function noopMaxValidator() { return true; } :
@@ -1806,6 +1838,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
18061838
}
18071839

18081840
if (hasStepAttr) {
1841+
stepVal = parseNumberAttrVal(attr.step);
1842+
18091843
ctrl.$validators.step = supportsRange ?
18101844
function nativeStepValidator() {
18111845
// Currently, only FF implements the spec on step change correctly (i.e. adjusting the
@@ -1827,7 +1861,13 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
18271861
// attribute value when the input is first rendered, so that the browser can adjust the
18281862
// input value based on the min/max value
18291863
element.attr(htmlAttrName, attr[htmlAttrName]);
1830-
attr.$observe(htmlAttrName, changeFn);
1864+
var oldVal = attr[htmlAttrName];
1865+
attr.$observe(htmlAttrName, function wrappedObserver(val) {
1866+
if (val !== oldVal) {
1867+
oldVal = val;
1868+
changeFn(val);
1869+
}
1870+
});
18311871
}
18321872

18331873
function minChange(val) {
@@ -1881,11 +1921,11 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
18811921
}
18821922

18831923
// Some browsers don't adjust the input value correctly, but set the stepMismatch error
1884-
if (supportsRange && ctrl.$viewValue !== element.val()) {
1885-
ctrl.$setViewValue(element.val());
1886-
} else {
1924+
if (!supportsRange) {
18871925
// TODO(matsko): implement validateLater to reduce number of validations
18881926
ctrl.$validate();
1927+
} else if (ctrl.$viewValue !== element.val()) {
1928+
ctrl.$setViewValue(element.val());
18891929
}
18901930
}
18911931
}

src/ng/directive/ngModel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ NgModelController.prototype = {
562562
* `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
563563
*/
564564
$validate: function() {
565+
565566
// ignore $validate before model is initialized
566567
if (isNumberNaN(this.$modelValue)) {
567568
return;

src/ng/directive/ngRepeat.js

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
454454
return block.clone[block.clone.length - 1];
455455
};
456456

457+
var trackByIdArrayFn = function($scope, key, value) {
458+
return hashKey(value);
459+
};
460+
461+
var trackByIdObjFn = function($scope, key) {
462+
return key;
463+
};
457464

458465
return {
459466
restrict: 'A',
@@ -493,32 +500,23 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
493500
aliasAs);
494501
}
495502

496-
var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
497-
var hashFnLocals = {$id: hashKey};
503+
var trackByIdExpFn;
498504

499505
if (trackByExp) {
500-
trackByExpGetter = $parse(trackByExp);
501-
} else {
502-
trackByIdArrayFn = function(key, value) {
503-
return hashKey(value);
504-
};
505-
trackByIdObjFn = function(key) {
506-
return key;
506+
var hashFnLocals = {$id: hashKey};
507+
var trackByExpGetter = $parse(trackByExp);
508+
509+
trackByIdExpFn = function($scope, key, value, index) {
510+
// assign key, value, and $index to the locals so that they can be used in hash functions
511+
if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
512+
hashFnLocals[valueIdentifier] = value;
513+
hashFnLocals.$index = index;
514+
return trackByExpGetter($scope, hashFnLocals);
507515
};
508516
}
509517

510518
return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
511519

512-
if (trackByExpGetter) {
513-
trackByIdExpFn = function(key, value, index) {
514-
// assign key, value, and $index to the locals so that they can be used in hash functions
515-
if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
516-
hashFnLocals[valueIdentifier] = value;
517-
hashFnLocals.$index = index;
518-
return trackByExpGetter($scope, hashFnLocals);
519-
};
520-
}
521-
522520
// Store a list of elements from previous run. This is a hash where key is the item from the
523521
// iterator, and the value is objects with following properties.
524522
// - scope: bound scope
@@ -572,7 +570,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
572570
for (index = 0; index < collectionLength; index++) {
573571
key = (collection === collectionKeys) ? index : collectionKeys[index];
574572
value = collection[key];
575-
trackById = trackByIdFn(key, value, index);
573+
trackById = trackByIdFn($scope, key, value, index);
576574
if (lastBlockMap[trackById]) {
577575
// found previously seen block
578576
block = lastBlockMap[trackById];
@@ -594,6 +592,12 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
594592
}
595593
}
596594

595+
// Clear the value property from the hashFnLocals object to prevent a reference to the last value
596+
// being leaked into the ngRepeatCompile function scope
597+
if (hashFnLocals) {
598+
hashFnLocals[valueIdentifier] = undefined;
599+
}
600+
597601
// remove leftover items
598602
for (var blockKey in lastBlockMap) {
599603
block = lastBlockMap[blockKey];

0 commit comments

Comments
 (0)