Angular js 从默认值初始化 ng-model

Posted

技术标签:

【中文标题】Angular js 从默认值初始化 ng-model【英文标题】:Angular js init ng-model from default values 【发布时间】: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()

【问题讨论】:

【参考方案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。 其实我收回了,这对我来说效果最好,太棒了:***.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://***.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/。

【讨论】:

这篇文章很棒。我最喜欢的答案是因为问题是根据初始值设置输入的值。 我创建了一个 Plnkr 来测试这段代码,效果很好:plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview【参考方案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");

【讨论】:

以上是关于Angular js 从默认值初始化 ng-model的主要内容,如果未能解决你的问题,请参考以下文章

2017年前端开发Angular.JS从入门到上手企业开发视频

angular 下拉框null默认值

从 typescript angular 6 中选择默认选项值

angular js 指令

从 Angular Universal 应用程序中的 Express JS 获取值

如何使用 Angular JS 从外部范围访问数组值