默认值的Angular js init ng-model


126

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

例:

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

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

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

Answers:


136

这是新的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;
});

希望对您有所帮助。


4
是的,它有帮助。我想我对Angular的这个决定感到震惊。
Calvin Froedge

125
别震惊... HTML的渲染正在从服务器移到浏览器。如今,JavaScript中有数十种MVC框架,对于服务器而言,仅将JSON / XML数据托管到JavaScript应用程序中,比在服务器上呈现每个页面要有效得多。它使许多工作转移到客户端的机器上,而不是让服务器承担重任。更不用说它节省带宽。最重要的是,您可以拥有一个通过HTTP使用相同JSON的本地移动应用程序(或其他任何东西)。这就是未来。
Ben Lesh 2012年

3
@blesh:好答案。非常感谢。我同意这是前进的道路,我本人已开始采用这种方法。您是否有任何支持此声明的链接?我也只是担心将html的渲染从服务器移动到客户端会导致页面加载时间变慢,尤其是在移动设备中,您可能会渲染很多HTML,例如导航树。
GFoley83

19
我不同意这是一个“错误”。在某些情况下,客户端渲染是最好的解决方案。在其他情况下,服务器端渲染是必经之路。您可以同时使用角度和角度两种技术,并且不应将客户端渲染视为“角度方式”,因为事实并非如此。Angular比这更灵活。
hunterloftis

8
@blesh,当然-在搜索引擎优化很重要但爬虫替代内容的成本高于实现嵌入数据角度的成本的任何情况下-任何在感知速度或渲染时间上都具有很高价值的应用程序用户体验-许多内容网站和电子商务网站将属于这些类别之一。twitter上的工程师尝试了客户端渲染,然后又移回到服务器上以提供更好的用户体验,他们也概述了其推理: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis 2013年

236

如果您无法重做应用程序以执行@blesh的建议(使用$ http或$ resource下拉JSON数据并填充$ scope),则可以使用ng-init代替:

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

另请参见AngularJS-使用ng模型时,输入文本框中的Value属性会被忽略吗?


角度方式为+1(较不受欢迎的做法)。示例:jsfiddle.net/9ymB3
John Lehmann

2
我在C#Web窗体中使用Angular,并且发现ng-init从代码隐藏/回发Eg中设置值时,使用非常有用<input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />。有点丑吗?是的,但是可以解决问题,并解决了该过程中的集成难题。
GFoley83

13
+1,优雅!人们提倡快速而敏捷的行为,但是SO上的很多人都说“在客户端做所有事情。”就像数百万的http调用对快速站点很有用。将数据服务器端植入模板可以启用缓存策略。带有Memcached的静态或动态/部分。这就是Twitter所做的。有时它很有用,而其他时候则没有。想强调一下。只需添加,而不是您否则说@Mark。
OMA

1
实际上,我将其收回,这对我而言最有效,非常棒:stackoverflow.com/a/20522955/329367
Darren 2014年

1
FWIW:我不喜欢模板表达式中的变量赋值,因为它是不可测试的。
Ben Lesh 2015年

60

这显然是缺少的,但很容易添加到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 =可以像大多数人期望的那样协同工作?
hunterloftis

挺棒的!这很有用。
santiagobasulto

3
好答案!我刚刚添加了一个“修复程序”,使其也可以与textareas一起使用-gist.github.com/rmontgomery429/6191275
Ryan Montgomery

为什么controller为指令定义,为什么不使用该link方法?我是angularjs的新手
ebram khalil 2014年

1
需要更改val = $attrs.ngInitial || $attrs.value || $element.val()为使其能够与选择元素一起使用。
fjsj 2014年

12

恕我直言,最好的解决方案是@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);
                    }
                }
            }
        }
    });

那选择呢?
jwize 2014年


6

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

Angular不建议这样做,并且如上所述,您应仅使用控制器。

但这有效:)


4

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

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

3

正如其他人指出的那样,在视图上初始化数据不是一个好习惯。但是,建议初始化Controller上的数据。(请参阅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;
});

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


3

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

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属性,则默认为空字符串。


3

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

<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


1
据我所知,如果不能发出初始$ http请求,这是解决问题的最有效的方法。如果以后要交换到$ http,它的好处是易于修改。一种可能的增强功能是返回承诺而不是使转换更加容易。我会非常避开设置全局变量或在此处使用ng-initial。
里查德2014年

1

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

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()-希望这可以为其他尝试此操作的人清除一切!
zesda

0

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

从小提琴:

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


1
您可以使用ng-init初始化数组,然后使用ng-repeat输出表格行:jsfiddle.net/6tP6x/1
Karen Zilles

0

刚刚在Ryan Montgomery“修复”中添加了对select元素的支持

<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)
    }]
}

});


0

如果您在URL中具有init值(如)mypage/id,那么在有角JS的控制器中,您可以使用location.pathname该ID并将其分配给所需的模型。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.