【问题标题】:Angular js init ng-model from default valuesAngular js 从默认值初始化 ng-model
【发布时间】:2012-11-26 00:34:01
【问题描述】:

假设您有一个从数据库加载值的表单。你如何初始化 ng-model?

例子:

<input name="card[description]" ng-model="card.description" value="Visa-4242">

在我的控制器中,$scope.card 最初是未定义的。除了做这样的事情还有什么办法吗?

$scope.card = {
  description: $('myinput').val()
}

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    这是新 Angular 应用程序中的常见错误。如果可以避免的话,您不想将值写入服务器上的 HTML。事实上,如果您可以避免让服务器完全呈现 HTML,那就更好了。

    理想情况下,您希望发送您的 Angular HTML 模板,然后通过 JSON 中的 $http 提取您的值并将它们放入您的范围。

    因此,如果可能,请这样做:

    app.controller('MyController', function($scope, $http) {
        $http.get('/getCardInfo.php', function(data) {
           $scope.card = data;
        });
    });
    
    <input type="text" ng-model="card.description" />
    

    如果您绝对必须将您的值从您的服务器呈现到您的 HTML 中,您可以将它们放在一个全局变量中并使用 $window 访问它们:

    在你的页面标题中你会写出:

    <head>
       <script>
           window.card = { description: 'foo' };
       </script>
    </head>
    

    然后在你的控制器中你会得到它:

    app.controller('MyController', function($scope, $window) {
       $scope.card = $window.card;
    });
    

    希望对你有帮助。

    【讨论】:

    • 是的,它有帮助。我想我只是对 Angular 的人做出这个决定感到震惊。
    • 不要感到震惊... HTML 的呈现正在从服务器转移到浏览器。如今,JavaScript 中有许多 MVC 框架,对于服务器来说,将 JSON/XML 数据托管到 JavaScript 应用程序比在服务器上渲染每个页面要高效得多。它抵消了客户端机器的大量工作,而不是让服务器承受打击。更不用说它节省了带宽。最重要的是,您可以拥有一个通过 HTTP 使用相同 JSON 的本机移动应用程序(或任何其他应用程序)。这就是未来。
    • @blesh:很好的答案。非常感谢。我同意这是前进的方向,并且我自己已经开始采用这种方法;你有任何支持这个说法的链接吗?我只是还担心将 html 的呈现从服务器移动到客户端可能会导致页面加载时间变慢,尤其是在您可以呈现大量 HTML 的移动设备中,例如导航树。
    • 我不同意这是一个“错误”。在某些情况下,客户端渲染是最好的解决方案。在其他情况下,服务器端渲染是要走的路。您可以将 Angular 与这两种技术一起使用,并且客户端渲染不应被视为“角度方式”,因为它不是。 Angular 比这更灵活。
    • @blesh,当然 - 任何 SEO 很重要但爬虫替代内容的成本高于使用嵌入数据实现 angular 的成本的任何实例 - 任何高度重视感知速度的应用程序或用户体验的渲染时间 - 许多内容网站和电子商务网站都属于这些类别之一。 twitter 的工程师尝试了客户端渲染,然后又回到服务器以提供更好的用户体验,他们也概述了他们的推理:blog.twitter.com/2012/improving-performance-twittercom
    【解决方案2】:

    如果您无法按照 @blesh 的建议重新设计您的应用程序(使用 $http 或 $resource 下拉 JSON 数据并填充 $scope),您可以改用 ng-init

    <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">
    

    另见AngularJS - Value attribute on an input text box is ignored when there is a ng-model used?

    【讨论】:

    • +1 表示角度方式(不太受欢迎的做法)。示例:jsfiddle.net/9ymB3
    • 我正在将 Angular 与 C# Web 表单一起使用,并且我发现在从代码隐藏/回发设置值时使用 ng-init 非常有用,例如&lt;input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '&lt;%: Model.Phone %&gt;'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required /&gt;。有点丑?是的,但确实可以解决这个过程中的集成难题。
    • +1,优雅!人们提倡快速和快速的行为,但 SO 的许多人说“全部在客户端做”,就像数以百万计的 http 调用对快速站点有好处。将数据服务器端植入模板可启用缓存策略。使用 Memcached 的静态或动态/部分。这就是推特所做的。有时它很有用,有时则没有。想强调这一点。只需要补充一点,不是你说的,@Mark。
    • 其实我收回了,这对我来说效果最好,太棒了:stackoverflow.com/a/20522955/329367
    • FWIW:我不喜欢模板表达式中的变量赋值,因为它不可测试。
    【解决方案3】:

    我尝试了@Mark Rajcok 的建议。它适用于字符串值(Visa-4242)。 请参考这个fiddle

    从小提琴:

    使用ng-repeat 可以完成在小提琴中完成的相同操作,每个人都可以推荐。但是在阅读了@Mark Rajcok 给出的答案后,我只是想尝试对具有配置文件数组的表单进行相同的操作。 在我拥有 $scope.profiles = [{},{}]; 之前一切正常控制器中的代码。如果我删除此代码,我会收到错误消息。 但在正常情况下,当我从服务器打印或回显 html 时,我无法打印 $scope.profiles = [{},{}];。 是否有可能以与@Mark Rajcok 对&lt;input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'"&gt; 等字符串值所做的类似方式执行上述操作,而无需从服务器回显JavaScript 部分。

    【讨论】:

    • 您可以使用 ng-init 初始化数组,然后使用 ng-repeat 输出表单行:jsfiddle.net/6tP6x/1
    【解决方案4】:

    这是 AngularJS 的一个明显缺乏但很容易添加的修复。只需编写一个快速指令即可从输入字段中设置模型值。

    <input name="card[description]" value="Visa-4242" ng-model="card.description" ng-initial>
    

    这是我的版本:

    var app = angular.module('forms', []);
    
    app.directive('ngInitial', function() {
      return {
        restrict: 'A',
        controller: [
          '$scope', '$element', '$attrs', '$parse', function($scope, $element, $attrs, $parse) {
            var getter, setter, val;
            val = $attrs.ngInitial || $attrs.value;
            getter = $parse($attrs.ngModel);
            setter = getter.assign;
            setter($scope, val);
          }
        ]
      };
    });
    

    【讨论】:

    • 不错的快速指令。是否有充分的理由不将其打包到 ng-model 中,以便 value= 和 ng-model= 可以像大多数人所期望的那样协同工作?
    • 相当不错!这很有用。
    • 很好的答案!我刚刚添加了一个“修复”以使其也适用于 textareas - gist.github.com/rmontgomery429/6191275
    • 为什么你为你的指令定义controller,为什么不使用link方法?我是 angularjs 的新手
    • 需要更改为 val = $attrs.ngInitial || $attrs.value || $element.val() 才能使用选择元素。
    【解决方案5】:

    刚刚向 Ryan Montgomery “修复”添加了对选择元素的支持

    <select class="input-control" ng-model="regCompModel.numberOfEmployeeId" ng-initial>
        <option value="1af38656-a752-4a98-a827-004a0767a52d"> More than 500</option>
        <option value="233a2783-db42-4fdb-b191-0f97d2d9fd43"> Between 250 and 500</option>
        <option value="2bab0669-550c-4555-ae9f-1fdafdb872e5"> Between 100 and 250</option>
        <option value="d471e43b-196c-46e0-9b32-21e24e5469b4"> Between 50 and 100</option>
        <option value="ccdad63f-69be-449f-8b2c-25f844dd19c1"> Between 20 and 50</option>
        <option value="e00637a2-e3e8-4883-9e11-94e58af6e4b7" selected> Less then 20</option>
    </select>
    
    app.directive('ngInitial', function () {
    return {
        restrict: 'A',
        controller: ['$scope', '$element', '$attrs', '$parse', function ($scope, $element, $attrs, $parse) {
            val = $attrs.sbInitial || $attrs.value || $element.val() || $element.text()
            getter = $parse($attrs.ngModel)
            setter = getter.assign
            setter($scope, val)
        }]
    }
    

    });

    【讨论】:

      【解决方案6】:

      恕我直言,最好的解决方案是 @Kevin Stone 指令,但我必须升级它才能在各种条件下工作(例如 select、textarea),而这个肯定可以工作:

          angular.module('app').directive('ngInitial', function($parse) {
              return {
                  restrict: "A",
                  compile: function($element, $attrs) {
                      var initialValue = $attrs.value || $element.val();
                      return {
                          pre: function($scope, $element, $attrs) {
                              $parse($attrs.ngModel).assign($scope, initialValue);
                          }
                      }
                  }
              }
          });
      

      【讨论】:

      • 那么选择呢?
      【解决方案7】:

      正如其他人指出的那样,在视图上初始化数据并不是一个好习惯。 但是,建议在控制器上初始化数据。 (见http://docs.angularjs.org/guide/controller

      所以你可以写

      <input name="card[description]" ng-model="card.description">
      

      $scope.card = { description: 'Visa-4242' };
      
      $http.get('/getCardInfo.php', function(data) {
         $scope.card = data;
      });
      

      这样视图不包含数据,并且控制器在加载实际值时初始化值。

      【讨论】:

        【解决方案8】:

        如果您喜欢 Kevin Stone 的上述方法https://stackoverflow.com/a/17823590/584761 通过为特定标签(例如“输入”)编写指令来考虑一种更简单的方法。

        app.directive('input', function ($parse) {
            return {
                restrict: 'E',
                require: '?ngModel',
                link: function (scope, element, attrs) {
                    if (attrs.ngModel) {
                        val = attrs.value || element.text();
                        $parse(attrs.ngModel).assign(scope, val);
                    }
                }
            }; });
        

        如果你走这条路,你就不必担心为每个标签添加 ng-initial。它自动将模型的值设置为标签的 value 属性。如果不设置 value 属性,它将默认为空字符串。

        【讨论】:

          【解决方案9】:

          如果你的 URL 中有 mypage/id 这样的初始化值,那么在 Angular JS 的控制器中你可以使用 location.pathname 找到 id 并将其分配给你想要的模型。

          【讨论】:

            【解决方案10】:

            这是一种以服务器为中心的方法:

            <html ng-app="project">
                <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
                <script>
                    // Create your module
                    var dependencies = [];
                    var app = angular.module('project', dependencies);
            
                    // Create a 'defaults' service
                    app.value("defaults", /* your server-side JSON here */);
            
                    // Create a controller that uses the service
                    app.controller('PageController', function(defaults, $scope) {
                        // Populate your model with the service
                        $scope.card = defaults;
                    });
                </script>
            
                <body>
                    <div ng-controller="PageController">
                        <!-- Bind with the standard ng-model approach -->
                        <input type="text" ng-model="card.description">
                    </div>
                </body>
            </html>
            

            除了$provide.value 注册了一个包含您的默认值的服务之外,它的基本思想与此问题上更流行的答案相同。

            所以,在服务器上,你可以有类似的东西:

            {
                description: "Visa-4242"
            }
            

            并通过您选择的服务器端技术将其放入您的页面。这是一个要点:https://gist.github.com/exclsr/c8c391d16319b2d31a43

            【讨论】:

            • 据我所知,如果无法进行初始 $http 请求,这是处理问题的最 Angular 方法。如果您想稍后切换到 $http,它还有一个好处是更容易修改。一种可能的增强是返回一个承诺,而不是使转换更加容易。我会非常回避在这里设置全局变量或使用 ng-initial。
            【解决方案11】:

            您还可以在 HTML 代码中使用: ng-init="card.description = 12345"

            Angular 不建议这样做,如上所述,您应该专门使用您的控制器。

            但它有效:)

            【讨论】:

              【解决方案12】:

              您可以使用自定义指令(支持 textarea、select、radio 和 checkbox),查看这篇博文 https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form-fields-attributes/

              【讨论】:

              【解决方案13】:

              这是上述想法的更通用版本... 它只是检查模型中是否有任何值,如果没有,则将值设置给模型。

              JS:

              function defaultValueDirective() {
                  return {
                      restrict: 'A',
                      controller: [
                          '$scope', '$attrs', '$parse',
                          function ($scope, $attrs, $parse) {
                              var getter = $parse($attrs.ngModel);
                              var setter = getter.assign;
                              var value = getter();
                              if (value === undefined || value === null) {
                                  var defaultValueGetter = $parse($attrs.defaultValue);
                                  setter($scope, defaultValueGetter());
                              }
                          }
                      ]
                  }
              }
              

              HTML(使用示例):

              <select class="form-control"
                      ng-options="v for (i, v) in compressionMethods"
                      ng-model="action.parameters.Method"
                      default-value="'LZMA2'"></select>
              

              【讨论】:

              • 这对我有用,但只有在将$scope 传递给getter() 的调用之后 - 希望这可以为其他尝试这个的人解决问题!
              【解决方案14】:

              我有一个简单的方法,因为我的表单中有一些繁重的验证和掩码。因此,我使用 jquery 再次获取我的值并将事件“更改”触发为验证:

              $('#myidelement').val('123');
              $('#myidelement').trigger( "change");
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-11-18
                • 1970-01-01
                • 2014-12-29
                • 1970-01-01
                • 2020-02-08
                • 1970-01-01
                • 2015-02-25
                • 1970-01-01
                相关资源
                最近更新 更多