AngularJS 1.4 迁移 - ngOptions:是否已通过损坏的对象相等性检查进行跟踪?
AngularJS 1.4 migration - ngOptions: Has track by broken object equality checks?
我希望你们能帮助我阐明自从我们从 Angular 1.3 迁移到 Angular 1.4 以来我一直面临的问题。
我已经创建了一个支持 JSFiddle 演示来演示这个
说明
我有一个包含两个 MyElement
实例列表的控制器,用户可以 select 使用 <select>
.
还有一个按钮,用于比较 (===
) selected MyElement
与列表中的第一个元素。此比较的结果记录到控制台。
<select>
使用track by element.id
来区分MyElement
-实例。
奇怪的是 selected 项从不 等于列表的第一个元素(即使第一个元素是 select编)。绑定到 ctrl.selectedElement
的对象是不同的对象,但具有相同的属性:
NO!
Object {id: 1, description: "First"}
MyElement {id: 1, description: "First"}
当我删除 track by element.id
或 返回到 Angular 1.3 时,此行为消失(两个元素严格相等,正如我所期望的那样是)。
问题
这是一个错误吗?这是否与 fix(ngOptions) 有关,但我不知何故遗漏了其中的含义?这是怎么回事?
提前致谢!
JavaScript 中对象的默认相等运算符在它们引用内存中的相同位置时产生 true。更新必须以不遵守先前声明的方式更改对象。查看 migration docs 以查看更改是否记录在案。虽然,我可以看到 track by
确实在创建一个新对象——这将是虚假 ===
check
的症结所在
为此使用 angular.equals
,而不是 ===
。此检查有一些细节,如 angular.equals docs...
中所述
Two objects or values are considered equivalent if at least one of the
following is true:
- Both objects or values pass === comparison.
- Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
- Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
- Both values represent the same regular expression (In JavaScript, /abc/ == /abc/ => false. But we consider two regular expressions as
equal when their textual representation matches).
During a property comparison, properties of function type and
properties with names that begin with $ are ignored.
Scope and DOMWindow objects are being compared only by identify (===).
// -- 'YES!'
console.log(angular.equals(this.selectedElement, this.elements[0]) ? 'YES!' : 'NO!');
JSFiddle Link - 更新演示
在 尝试 中提供可能的最佳答案,我在 v1.4.0 中挖掘了 ngOptions 的源代码,track by
的以下代码似乎返回一个新对象,从而解释以上所有内容。
// Get the value by which we are going to track the option
// if we have a trackFn then use that (passing scope and locals)
// otherwise just hash the given viewValue
var getTrackByValueFn = trackBy ?
function(value, locals) { return trackByFn(scope, locals); } :
function getHashOfValue(value) { return hashKey(value); };
var getTrackByValue = function(value, key) {
return getTrackByValueFn(value, getLocals(value, key));
};
var locals = {}; // ---------- <<<<<<<<<<<<<<<< ---------- new object
var getLocals = keyName ? function(value, key) {
locals[keyName] = key;
locals[valueName] = value;
return locals;
} : function(value) {
locals[valueName] = value;
return locals;
};
使用JSON.stringify
然后比较。不知道用起来效率高不高
var MyElement = function (id, description) {
this.id = id;
this.description = description;
}
var MyController = function () {
this.elements = [
new MyElement(1, 'First'),
new MyElement(2, 'Second')
];
};
MyController.prototype.isFirst = function (element) {
console.log(JSON.stringify(this.selectedElement) === JSON.stringify(this.elements[0]) ? 'YES!' : 'NO!');
console.log(this.selectedElement);
console.log(this.elements[0]);
};
angular
.module('myApp', [])
.controller('MyController', MyController);
我希望你们能帮助我阐明自从我们从 Angular 1.3 迁移到 Angular 1.4 以来我一直面临的问题。
我已经创建了一个支持 JSFiddle 演示来演示这个
说明
我有一个包含两个 MyElement
实例列表的控制器,用户可以 select 使用 <select>
.
还有一个按钮,用于比较 (===
) selected MyElement
与列表中的第一个元素。此比较的结果记录到控制台。
<select>
使用track by element.id
来区分MyElement
-实例。
奇怪的是 selected 项从不 等于列表的第一个元素(即使第一个元素是 select编)。绑定到 ctrl.selectedElement
的对象是不同的对象,但具有相同的属性:
NO!
Object {id: 1, description: "First"}
MyElement {id: 1, description: "First"}
当我删除 track by element.id
或 返回到 Angular 1.3 时,此行为消失(两个元素严格相等,正如我所期望的那样是)。
问题
这是一个错误吗?这是否与 fix(ngOptions) 有关,但我不知何故遗漏了其中的含义?这是怎么回事?
提前致谢!
JavaScript 中对象的默认相等运算符在它们引用内存中的相同位置时产生 true。更新必须以不遵守先前声明的方式更改对象。查看 migration docs 以查看更改是否记录在案。虽然,我可以看到 track by
确实在创建一个新对象——这将是虚假 ===
check
为此使用 angular.equals
,而不是 ===
。此检查有一些细节,如 angular.equals docs...
Two objects or values are considered equivalent if at least one of the following is true:
- Both objects or values pass === comparison.
- Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
- Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
- Both values represent the same regular expression (In JavaScript, /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual representation matches).
During a property comparison, properties of function type and properties with names that begin with $ are ignored.
Scope and DOMWindow objects are being compared only by identify (===).
// -- 'YES!'
console.log(angular.equals(this.selectedElement, this.elements[0]) ? 'YES!' : 'NO!');
JSFiddle Link - 更新演示
在 尝试 中提供可能的最佳答案,我在 v1.4.0 中挖掘了 ngOptions 的源代码,track by
的以下代码似乎返回一个新对象,从而解释以上所有内容。
// Get the value by which we are going to track the option
// if we have a trackFn then use that (passing scope and locals)
// otherwise just hash the given viewValue
var getTrackByValueFn = trackBy ?
function(value, locals) { return trackByFn(scope, locals); } :
function getHashOfValue(value) { return hashKey(value); };
var getTrackByValue = function(value, key) {
return getTrackByValueFn(value, getLocals(value, key));
};
var locals = {}; // ---------- <<<<<<<<<<<<<<<< ---------- new object
var getLocals = keyName ? function(value, key) {
locals[keyName] = key;
locals[valueName] = value;
return locals;
} : function(value) {
locals[valueName] = value;
return locals;
};
使用JSON.stringify
然后比较。不知道用起来效率高不高
var MyElement = function (id, description) {
this.id = id;
this.description = description;
}
var MyController = function () {
this.elements = [
new MyElement(1, 'First'),
new MyElement(2, 'Second')
];
};
MyController.prototype.isFirst = function (element) {
console.log(JSON.stringify(this.selectedElement) === JSON.stringify(this.elements[0]) ? 'YES!' : 'NO!');
console.log(this.selectedElement);
console.log(this.elements[0]);
};
angular
.module('myApp', [])
.controller('MyController', MyController);