基于规则绑定到对象的任意深度属性
(如果我的问题标题不是很好,我很抱歉,我想不出更好的选择。请随时提出更好的选择。)
我正在尝试在Angular中创建一个可重用的“属性网格”,其中可以将对象绑定到网格,但以这种方式可以对对象的呈现进行一些定制。
这就是指令模板的样子( form-element
对我的问题并不重要,所以我会放弃它):
<div ng-repeat="prop in propertyData({object: propertyObject})">
<div ng-switch on="prop.type">
<div ng-switch-when="text">
<form-element type="text"
label-translation-key="{{prop.key}}"
label="{{prop.key}}"
name="{{prop.key}}"
model="propertyObject[prop.key]"
focus-events-enabled="false">
</form-element>
</div>
</div>
</div>
并且,指令代码:
angular.module("app.shared").directive('propertyGrid', ['$log', function($log) {
return {
restrict: 'E',
scope: {
propertyObject: '=',
propertyData: '&'
}
templateUrl: 'views/propertyGrid.html'
};
}]);
以下是一个示例用法:
<property-grid edit-mode="true"
property-object="selectedSite"
property-data="getSitePropertyData(object)">
</property-grid>
和它一起使用的getSitePropertyData()
函数:
var lastSite;
var lastSitePropertyData;
$scope.getSitePropertyData = function (site) {
if (site == undefined) return null;
if (site == lastSite)
return lastSitePropertyData;
lastSite = site;
lastSitePropertyData = [
{key:"SiteName", value:site.SiteName, editable: true, type:"text"},
//{key:"Company.CompanyName", value:site.Company.CompanyName, editable: false, type:"text"},
{key:"Address1", value:site.Address1, editable: true, type:"text"},
{key:"Address2", value:site.Address2, editable: true, type:"text"},
{key:"PostalCode", value:site.PostalCode, editable: true, type:"text"},
{key:"City", value:site.City, editable: true, type:"text"},
{key:"Country", value:site.Country, editable: true, type:"text"},
{key:"ContactName", value:site.ContactName, editable: true, type:"text"},
{key: "ContactEmail", value: site.ContactEmail, editable: true, type:"email"},
{key: "ContactPhone", value: site.ContactPhone, editable: true, type:"text"},
{key: "Info", value: site.Info, editable: true, type:"text"}
];
return lastSitePropertyData;
};
我经历这样一个“属性数据”函数,而不仅仅是直接绑定到对象的属性的原因是,我需要控制属性的顺序,以及它们是否应该显示给用户,以及为了演示的目的,它是什么样的属性(文本,电子邮件,数字,日期等)。
起初,你可以从getSitePropertyData()
函数中的value
属性getSitePropertyData()
,我首先尝试直接从这个函数中提供值,但不会绑定到对象,所以要么改变对象,要么改变属性的属性电网没有来回同步。 接下来是使用key
思想,它让我做到这一点: propertyObject[prop.key]
- 这对于直接属性非常propertyObject[prop.key]
,但正如您所看到的,我必须将“Company”字段注释掉,因为它是属性的属性和propertyObject["ab"]
不起作用。
我正在努力弄清楚在这里做什么。 我需要绑定才能工作,并且我需要能够在绑定中使用任意深度的属性。 我知道这种事情在理论上是可能的, 我已经看到它在UI Grid中完成,但是这样的项目有很多代码,我可能会花几天的时间找出它们是如何实现的。
我正在接近,还是我对这一切都做错了?
你想在一个对象上运行一个任意的Angular表达式。 这正是$parse
(ref)的目的。 这个服务可以......解析一个Angular表达式并返回一个getter和setter。 以下示例是formElement
指令的简化实现,演示了如何使用$parse
:
app.directive('formElement', ['$parse', function($parse) {
return {
restrict: 'E',
scope: {
label: '@',
name: '@',
rootObj: '=',
path: '@'
},
template:
'<label>{{ label }}</label>' +
'<input type="text" ng-model="data.model" />',
link: function(scope) {
var getModel = $parse(scope.path);
var setModel = getModel.assign;
scope.data = {};
Object.defineProperty(scope.data, 'model', {
get: function() {
return getModel(scope.rootObj);
},
set: function(value) {
setModel(scope.rootObj, value);
}
});
}
};
}]);
我稍微改变了指令的使用方式,希望不改变语义:
<form-element type="text"
label-translation-key="{{prop.key}}"
label="{{prop.key}}"
name="{{prop.key}}"
root-obj="propertyObject"
path="{{prop.key}}"
focus-events-enabled="false">
其中root-obj
是模型的顶部, path
是达到实际数据的表达式。
如您所见, $parse
为给定表达式创建getter和setter函数,用于任何根对象。 在model.data
属性中,您将由$parse
创建的访问器函数应用于根对象。 整个Object.defineProperty
结构可以被手表取代,但这只会增加摘要循环的开销。
这是一个工作小提琴:https://jsfiddle.net/zb6cfk6y/
顺便说一句,另一种(更简洁和习惯的)写get / set的方法是:
Object.defineProperty(scope.data, 'model', {
get: getModel.bind(null, scope.rootObj),
set: setModel.bind(null, scope.rootObj)
});
如果您使用lodash
,则可以使用_.get
函数来实现此目的。
您可以将_.get
存储在property-grid
的控制器中,然后使用
model="get(propertyObject,prop.key)"
在您的模板中。 如果你在你的应用程序的多个地方需要这个功能(而不仅仅是property-grid
),你可以为此写一个filter
。
这样做的问题是您无法以这种方式绑定您的模型,因此您无法编辑这些值。 你可以使用_.set
函数和一个带有getter和setter的对象来完成这个工作。
vm.modelize = function(obj, path) {
return {
get value(){return _.get(obj, path)},
set value(v){_.set(obj, path,v)}
};
}
然后,您可以在模板中使用该功能:
<div ng-repeat="prop in propertyData({object: propertyObject})">
<input type="text"
ng-model="ctrl.modelize(propertyObject,prop.key).value"
ng-model-options="{ getterSetter: true }"></input>
</div>
对于一个简化的例子,请参阅这个Plunker。
如果你不使用lodash
你可以使用我从lodash
提取的_.get
函数的简化版本。
function getPath(object, path) {
path = path.split('.')
var index = 0
var length = path.length;
while (object != null && index < length) {
object = object[path[index++]];
}
return (index && index == length) ? object : undefined;
}
这个函数确保你不会得到任何Cannot read property 'foo' of undefined
错误的Cannot read property 'foo' of undefined
。 如果您有可能存在未定义值的长链属性,这非常有用。 如果你希望能够使用更先进的路径(如foo.bar[0]
你必须使用完整的_.get
功能从lodash
。
这里是一个简化版本_.set
还提取形式lodash
:
function setPath(object, path, value) {
path = path.split(".")
var index = -1,
length = path.length,
lastIndex = length - 1,
nested = object;
while (nested != null && ++index < length) {
var key = path[index]
if (typeof nested === 'object') {
var newValue = value;
if (index != lastIndex) {
var objValue = nested[key];
newValue = objValue == null ?
((typeof path[index + 1] === 'number') ? [] : {}) :
objValue;
}
if (!(hasOwnProperty.call(nested, key) && (nested[key] === value)) ||
(value === undefined && !(key in nested))) {
nested[key] = newValue;
}
}
nested = nested[key];
}
return object;
}
请记住,这些提取函数忽略一些边缘案件lodash
手柄。 但他们应该在大多数情况下工作。
当你创建lastSitePropertyData时,你可以用这种方式创建对象来不对它进行硬编码
function createObject (){
for(var key in site){
lastSitePropertyData.push({key:key, value:site[key], editable: true, type:"text"});
} }
后来用函数来获取这样的数据
function getKey(prop){
if(typeof prop.value === 'object'){
return prop.value.key; //can run loop create a go deep reccursive method - thats upto u
}
else return prop.key;
}
function getValue(prop){
if(typeof prop === 'object'){
return prop.value.value; //have tp run loop get value from deep reccursive method - thats upto u
}
else return prop.value;
}
这种方式可以在html {{getKey(prop)}}
和{{getValue(prop}}
对于工作演示请看这个链接 - https://jsfiddle.net/718px9c2/4/
注意:它只是更好地访问json数据的想法,我没有在演示中使用angular。
链接地址: http://www.djcxy.com/p/34513.html上一篇: Binding to arbitrarily deep properties on an object based on rules