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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20119998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 17:47:16  来源:igfitidea点击:

AngularJS: nested objects from dynamically set model names

javascriptangularjs

提问by Lorenzo Marcon

I have an array containing variable names, example:

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

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

I want to create input fields out of these, and I'm using AngularJS. No big deal:

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

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

The resulting $scope.dataobject is:

结果$scope.data对象是:

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

Which, by the way, is not exactly what I'm trying to achieve. Is there a syntax that could lead me to an object as the following one as result?

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

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

Please consider that I can have even more than one level of nesting, this is just an example.

请考虑我可以有不止一层的嵌套,这只是一个例子。

采纳答案by musically_ut

I do not think models should be accessed this way.

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

However, this was curious question and the solution is a bit fun.

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

The problem is that ng-modelrequires a referenceand thought Javascript sends modifiable copies of objects, it does not have pass-by-referencesemanticsand we cannot just pass a stringto ng-model.

问题是ng-model需要一个引用并且认为 Javascript 发送对象的可修改副本,它没有传递引用语义,我们不能只将字符串传递给ng-model.

However, arrays and objects do have this property. Hence, the solution is to return an array whose 0th element will be the referencefor ng-model. This is also the hackypart since all your objects are now arrays with '1' element.

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

The other solution would be to return an object for each case instead of 1 element array.

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

Solution using embedded objects

使用嵌入对象的解决方案

Here is the solution using an embeddedobject: http://plnkr.co/edit/MuC4LE2YG31RdU6J6FaD?p=previewwhich in my opinion looks nicer.

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

Hence, in your controller:

因此,在您的控制器中:

$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;
}

And in your template:

在你的模板中:

<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" />

Solution using single element array

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

Here is the shortest (albeit hacky) solution I could come up with: http://plnkr.co/edit/W92cHU6SQobot8xuElcG?p=preview

这是我能想出的最短(虽然很笨拙)的解决方案:http: //plnkr.co/edit/W92cHU6SQobot8xuElcG?p=preview

Hence, in your controller:

因此,在您的控制器中:

$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;
}

And in your template:

在你的模板中:

<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]" />

回答by roy650

The answer provided by @musically_ut is good but has one significant flaw: It will work great if you're creating a new model but if you have an pre-defined existing model that you can't refactor into the '.value' structure or the array structure, then you're stuck...

@musically_ut 提供的答案很好,但有一个重大缺陷:如果您正在创建一个新模型,它会很好用,但如果您有一个无法重构为“.value”结构的预定义现有模型,或者数组结构,然后你被卡住了......

Clearly that was the case for me... (and I assume that was the case for @LorenzoMarcon too, as he's implying that he'll have to "post-process" the result and transform it to a different format)

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

I ended up elaborating on @musically_ut's solution:

我最终详细说明了@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];
    };

(note the change in the while loop index)

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

Later on you access the dynamic field like this:

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

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

The idea is (as explained in @musically_ut's answer) that JS can't pass a string by reference, so the hack around it I pass the parent node (hence the while loop inside 'getModelParent' stops before the last index) and access the leaf node (from 'getModelLeaf') using an array like notation.

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

Hope this makes sense and helps.

希望这是有道理的并有所帮助。

回答by AlwaysALearner

If you can restructure your models, you can simply do like this:

如果你可以重组你的模型,你可以简单地这样做:

Controller

控制器

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

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

HTML

HTML

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

回答by Adrian H

For easier parsing of paths you can also check lodash's methods :

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

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

https://lodash.com/docs#get

https://lodash.com/docs#get