【问题标题】:AngularJS Two-way Data Binding in Nested Directives嵌套指令中的 AngularJS 双向数据绑定
【发布时间】:2013-06-15 10:28:54
【问题描述】:

如果您需要更多信息或希望我澄清任何事情,请告诉我。我尝试了很多不同的方法来解决这个问题,但没有找到解决方案。

我对 angularJS 比较陌生,我正在尝试构建一个包含多层数据的应用程序。我有一些基本的用户信息存储在控制器 PageController 的主体范围内。然后我有一个设置表单,它使用 $routeParams(带有控制器 SettingsController)加载,其中包括几个用于模板目的的自定义指令。由于指令是嵌套的,我使用嵌入将第二个加载到第一个内部。这一切似乎都正常。

我的问题是我试图从最里面的指令中引用字段user.firstname,并希望使用双向数据绑定来允许对文本框所做的更改导致 PageController 范围内的值也发生更改。我知道很多这类问题是由在 ng-model 中使用原语引起的,但是我尝试将所有内容都放在一个额外的对象中,以便触发原型继承无济于事。我在这里做错了什么?

这是我的代码的JSFiddle,已尽可能精简以隔离问题。在此示例中,如果我直接在 PageController 范围内输入外部文本框,它将修改内部文本框,直到该文本框被修改,然后连接断开。这似乎就像其他问题中描述的使用原语的问题,但我无法弄清楚问题出在哪里。

HTML:

<body class="event-listing" ng-app="app" ng-controller="PageController">
    <div class="listing-event-wrap">
        <input type="text" ng-model="user.firstname" />
        <div ng-controller="SettingsController">
            <section block title="{{data.updateInfo.title}}" description="{{data.updateInfo.description}}">
                <div formrow label="{{data.updateInfo.labels.firstname}}" type="textInput" value="user.firstname"></div>
            </section>
        </div>
    </div>
</body>

Angular 指令:

app.directive('formrow', function() {
return {
    scope: {
            label: "@label",
            type: "@type",
            value: "=value" 
    },
    replace: true,
    template: '<div class="form-row">' + 
            '<div class="form-label" data-ng-show="label">{{label}}</div>' + 
            '<div class="form-entry" ng-switch on="type">' + 
                '<input type="text" ng-model="value" data-ng-switch-when="textInput" />' + 
            '</div>' + 
        '</div>'
}
});
app.directive('block', function() {
return {
    scope: {
            title: "@title",
            description: "@description" 
    },
    transclude: true,
    replace: true,
    template: '<div class="page-block">' +
            '<h2 data-ng-show="title">{{title}}</h2>' + 
            '<p class="form-description" data-ng-show="description">{{description}}</p>' + 
            '<div class="block-inside" data-ng-transclude></div>' + 
            '</div>'
}
});

角度控制器:

app.controller("PageController", function($scope) {
    $scope.user = {
        firstname: "John"
    };
});
app.controller("SettingsController", function($scope) {
    $scope.data = {
        updateInfo: {
            title: "Update Your Information",
            description: "A description here",
            labels: {
                firstname: "First Name"
            }
        }
    }
});

【问题讨论】:

    标签: javascript angularjs data-binding web-applications angularjs-scope


    【解决方案1】:

    问题是由ng-switch引起的,来自git的文档Understanding scope

    ng-switch 范围继承就像 ng-include 一样工作。所以如果你需要 2 路数据绑定到父范围内的原语,使用 $parent,或 将模型更改为对象,然后绑定到该对象的属性 目的。这将避免子范围隐藏/隐藏父范围 属性。

    因此,如果您在文本框中键入一些文本。 以下代码将针对ng-switch 范围执行。

    $scope.value="the text you typed"

    因此它不会参考原型链来搜索value。这将为ng-switch 范围创建一个新属性。

    如何证明?

    如果您将value 更改为$parent.value。一切都会好起来的。因为在原始类型的ng-switch 中(如果没有点,angularjs 会将value 识别为原始类型)$parent 将引用formrow 指令范围。

    尝试删除ng-switch 或按照文档说明进行操作。问题将消失。

    更重要的是,文档建议我们在应用双向绑定时始终使用点 . 来引用模型。

    如果我说错了。请纠正我并改正。谢谢。

    【讨论】:

      【解决方案2】:

      由于指令中的文本框对其模型使用原语而不是对象(ng-model="value" 而不是ng-model="someobj.somevalue"),因此它的模型仅在本地范围内创建,并且父级无权访问它。

      解决方法是使用 dot rule 作为对象属性来定义指令文本框模型:

      ng-model="value.firstname"
      

      然后将整个 user 对象传递给指令,而不仅仅是原始属性:

      <div formrow ... value="user"></div>
      

      Here is a demo

      【讨论】:

      • 嘿,我已经简化了你的演示,只是为了看得更清楚。它似乎适用于复杂的对象,但不适用于简单的属性...Here is updated version。你明白为什么吗?
      • @Dmitry 您的简单属性示例重新引入了 OP 的错误。简而言之,Angular 范围继承需要对象而不是简单的原始变量。这是 JavaScript 中原型继承的直接后果。您可以查看[this answer] 我在其他地方提供的另一个示例,或者查看[this Q&A] 以获得更详细的说明。
      • 显然我过于乐观地认为字符串可以绑定。但是你是对的,这样嵌套指令中的绑定可以完美地工作。以前,我只获得单向(初始)绑定,并且正在为让它工作而发疯。 +10
      【解决方案3】:

      我很抱歉之前的代码。试试这个:http://jsfiddle.net/CxNc2/2/

      我现在不是传递实际值,而是传递对象 + 指向内部正确值的指针。我在这里添加了“refobject”:

      <body class="event-listing" ng-app="app" ng-controller="PageController">
          <div class="listing-event-wrap">
              <input type="text" ng-model="user.firstname" />
              <div ng-controller="SettingsController">
                  <section block title="{{data.updateInfo.title}}" description="{{data.updateInfo.description}}">
                      <div formrow label="{{data.updateInfo.labels.firstname}}" type="textInput" refobj='user' value="firstname"></div>
                  </section>
              </div>
          </div>
      </body>
      

      我在这里添加了 refobj + value:

      app.directive('formrow', function() {
          return {
              scope: {
                  label: "@label",
                  type: "@type",
                  value: "@value",
                  refobj: "="
              },
              replace: true,
              template: '<div class="form-row">' + 
                  '<div class="form-label" data-ng-show="label">{{label}}</div>' + 
                  '<div class="form-entry" ng-switch on="type">' + 
              '<input type="text" ng-model="refobj[value]" data-ng-switch-when="textInput" />' + 
                  '</div>' + 
              '</div>'
          }
      

      【讨论】:

      • 感谢您的快速回复!我尝试了小提琴,但它似乎和我发布的一样。将其更改为函数隔离范围的目标是什么?
      • 我添加了 refobj,因此您不需要在指令中调用“名字”,因为我确定您希望这是通用的。
      • 真是太聪明了!没有考虑过使用数组表示法而不是点。这将满足我的需求。谢谢!
      • 如果可以的话,我会给这 10 个赞。这救了我的培根。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-22
      • 2019-07-23
      • 1970-01-01
      相关资源
      最近更新 更多