AngularJS:动态设置模型名称的嵌套对象

时间:2013-11-21 11:40:25

标签: javascript angularjs

我有一个包含变量名的数组,例如:

var names = ['address.street','address.city'];

我想从这些中创建输入字段,我正在使用AngularJS。没什么大不了的:

<div ng-repeat="n in names">
    <input type="text" ng-model="data[n]" />
</div>

生成的$scope.data对象是:

{
    "address.street" : ...,
    "address.city" : ...
}

顺便说一句,这并不是我想要实现的目标。 是否有一种语法可以将我引导为一个对象,如下所示?

{
    "address" : {
        "street" : ...,
        "city" : ...
    }
}

请考虑我可以拥有多个嵌套级别,这只是一个例子。

4 个答案:

答案 0 :(得分:9)

我不认为应该以这种方式访问​​模型。

然而,这是一个奇怪的问题,解决方案有点有趣。

问题是ng-model需要引用,并认为Javascript发送可修改的对象副本does not have pass-by-reference semantics,我们不能只传递字符串ng-model

但是,数组和对象确实具有此属性。因此,解决方案是返回一个数组,其0元素将是ng-model引用。这也是 hacky 部分,因为所有对象现在都是带有'1'元素的数组。

另一种解决方案是为每个案例而不是1个元素数组返回一个对象。

使用嵌入对象的解决方案

以下是使用嵌入式对象的解决方案:http://plnkr.co/edit/MuC4LE2YG31RdU6J6FaD?p=preview在我看来看起来更好。

因此,在您的控制器中:

$scope.getModel = function(path) {
  var segs = path.split('.');
  var root = $scope.data;

  while (segs.length > 0) {
    var pathStep = segs.shift();
    if (typeof root[pathStep] === 'undefined') {
      root[pathStep] = segs.length === 0 ? { value:  '' } : {};
    }
    root = root[pathStep];
  }
  return root;
}

在你的模板中:

<p>Hello {{data.person.name.value}}!</p>
<p>Address: {{data.address.value}}</p>
<input ng-model="getModel('person.name').value" />
<input ng-model="getModel('address').value" />

使用单个元素数组的解决方案

以下是我能提出的最短(尽管是hacky)解决方案:http://plnkr.co/edit/W92cHU6SQobot8xuElcG?p=preview

因此,在您的控制器中:

$scope.getModel = function(path) {
  var segs = path.split('.');
  var root = $scope.data;

  while (segs.length > 0) {
    var pathStep = segs.shift();
    if (typeof root[pathStep] === 'undefined') {
      root[pathStep] = segs.length === 0 ? [ '' ] : {};
    }
    root = root[pathStep];
  }
  return root;
}

在你的模板中:

<p>Hello {{data.person.name[0]}}!</p>
<p>Address: {{data.address[0]}}</p>
<input ng-model="getModel('person.name')[0]" />
<input ng-model="getModel('address')[0]" />

答案 1 :(得分:7)

@musically_ut提供的答案很好,但有一个重大缺陷: 如果你正在创建一个新模型,它会很有效,但如果你有一个预先定义的现有模型,你不能重构为'.value'结构或数组结构,那么你就被卡住......

显然,对我来说就是这种情况......(我认为@LorenzoMarcon的情况也是如此,因为他暗示他必须“后处理”结果并将其转换为不同的格式)

我最后详细阐述了@ musically_ut的解决方案:

    $scope.getModelParent = function(path) {
      var segs = path.split('.');
      var root = $scope.data;

      while (segs.length > 1) {
        var pathStep = segs.shift();
        if (typeof root[pathStep] === 'undefined') {
          root[pathStep] = {};
        }
        root = root[pathStep];
      }
      return root;
    };

    $scope.getModelLeaf = function(path) {
      var segs = path.split('.');
      return segs[segs.length-1];
    };

(注意while循环索引的变化)

稍后您可以像这样访问动态字段:

<input ng-model="getModelParent(fieldPath)[ getModelLeaf(fieldPath) ]"/>

这个想法是(如@ musically_ut的答案中所解释的)JS不能通过引用传递一个字符串,所以围绕它的hack我传递父节点(因此'getModelParent'中的while循环在最后一个索引之前停止)并使用符号表示数组访问叶节点(来自'getModelLeaf')。

希望这是有道理和有帮助的。

答案 2 :(得分:1)

如果您可以重新构建模型,您可以这样做:

控制器

$scope.names = {
    "address":[
        "street",
        "city"
    ]
};

$scope.data = {
    address:{
        street:"",
        city:""
    }
};

HTML

<div ng-repeat="(key, values) in names">
    <div ng-repeat="value in values">
        <input type="text" ng-model="data[key][value]" />
    </div>
</div>

答案 3 :(得分:0)

为了更容易解析路径,您还可以检查lodash的方法:

_.get($scope, 'model.nested.property', 'default');   
_.set($scope, 'model.nested.property', 'default');   
_.has($scope, 'model.nested.property');

https://lodash.com/docs#get