125

Say you have a form that has values loaded from database. How do you initialize ng-model?

Example:

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

In my controller, $scope.card is undefined initially. Is there a way besides doing something like this?

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

14 Answers 14

237

If you can't rework your app to do what @blesh suggests (pull JSON data down with $http or $resource and populate $scope), you can use ng-init instead:

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

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

Sign up to request clarification or add additional context in comments.

10 Comments

+1 for the angular way (of the less preferred practice). Example: jsfiddle.net/9ymB3
I'm using Angular with C# web forms & I find that using ng-init is quite useful when setting values from code-behind/postback E.g. <input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />. Bit ugly? Yes, but does the trick & solves an integration headache in the process.
+1, elegant! People advocate snappy and speedy behavior, yet so many here at SO says "do it all client side.", like millions of http calls is good for snappy sites. Seeding data server side into templates enables caching strategies. Static, or dynamic/partial with Memcached. This is what twitter does. Sometimes it's useful, other times not. Wanted to stress that. Just had to add that, not that you said otherwise, @Mark.
Actually I take that back, this works best for me, awesome: stackoverflow.com/a/20522955/329367
FWIW: I dislike variable assignment in template expressions because it's not testable.
|
135

This is a common mistake in new Angular applications. You don't want to write your values into your HTML on the server if you can avoid it. If fact, if you can get away from having your server render HTML entirely, all the better.

Ideally, you want to send out your Angular HTML templates, then pull down your values via $http in JSON and put them in your scope.

So if at all possible, do this:

app.controller('MyController', function($scope, $http) {
    $http.get('/getCardInfo.php', function(data) {
       $scope.card = data;
    });
});

<input type="text" ng-model="card.description" />

If you absolutely MUST render your values into your HTML from your server, you could put them in a global variable and access them with $window:

In the header of your page you'd write out:

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

And then in your controller you'd get it like so:

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

18 Comments

Yea, it helps. I guess I'm just shocked that the Angular guys made this decision.
Don't be shocked... The rendering of HTML is moving off of servers and to the browser. There are dozens of MVC frameworks in JavaScript these days, and it's much more efficient for a server just to host JSON/XML data to JavaScript apps than it is to render every single page on the server. It offsets a lot of the work to the client's machine rather than having the server take the hit. Not to mention it saves on bandwidth. To top it all off, you could have a native mobile app (or anything really) that consumes the same JSON over HTTP. This is the future.
@blesh: Great answer. Thanks a lot. I agree that this is the way forward and have started adopting this approach myself; do you have any links that support this claim? I'm just also worried that moving the rendering of the html from the server to the client could result in slower page load times, especially in mobile devices where you could be rendering a lot of HTML e.g. for a navigation tree.
I disagree that this is a 'mistake.' In some cases, client-side rendering is the best solution. In others, server-side rendering is the way to go. You can use angular with both techniques, and client-side rendering shouldn't be held up as the "angular way," because it isn't. Angular is more flexible than that.
@blesh, certainly - any instance where SEO is important but the cost of alternative content for crawlers is higher than the cost of implementing angular with embedded data - any application that puts a very high value on perceived speed or time-to-render for the user experience - many content sites and ecommerce sites would fall into one of these categories. The engineers at twitter, who tried client rendering then moved back to the server to deliver a better user experience, have outlined their reasoning as well: blog.twitter.com/2012/improving-performance-twittercom
|
60

This is an obviously lacking, but easily added fix for AngularJS. Just write a quick directive to set the model value from the input field.

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

Here's my version:

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

8 Comments

Nice quick directive. Any good reason not to package this into ng-model so that value= and ng-model= could work together as most people would expect?
Pretty nice! This is useful.
Great answer! I just added a "fix" to make this also work with textareas as well - gist.github.com/rmontgomery429/6191275
why you defined controller for your directive, and why didn't use the link method?. I'm newbie to angularjs
needed to change to val = $attrs.ngInitial || $attrs.value || $element.val() for having it to work with select elements.
|
12

IMHO the best solution is the @Kevin Stone directive, but I had to upgrade it to work in every conditions (f.e. select, textarea), and this one is working for sure:

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

1 Comment

And what about select ?
7

You can use a custom directive (with support to textarea, select, radio and checkbox), check out this blog post https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form-fields-attributes/.

2 Comments

This is a great post. My favorite answer since the question was to set the value of an input according to the initials values.
I created a Plnkr to test this code and it works great : plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
6

You can also use within your HTML code: ng-init="card.description = 12345"

It is not recommended by Angular, and as mentioned above you should use exclusively your controller.

But it works :)

Comments

4

I have a simple approach, because i have some heavy validations and masks in my forms. So, i used jquery to get my value again and fire the event "change" to validations:

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

Comments

3

As others pointed out, it is not good practice to initialize data on views. Initializing data on Controllers, however, is recommended. (see http://docs.angularjs.org/guide/controller)

So you can write

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

and

$scope.card = { description: 'Visa-4242' };

$http.get('/getCardInfo.php', function(data) {
   $scope.card = data;
});

This way the views do not contain data, and the controller initializes the value while the real values are being loaded.

Comments

3

If you like Kevin Stone's approach above https://stackoverflow.com/a/17823590/584761 consider an easier approach by writing directives for specific tags such as 'input'.

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

If you go this route you won't have to worry about adding ng-initial to every tag. It automatically sets the value of the model to the tag's value attribute. If you do not set the value attribute it will default to an empty string.

Comments

3

Here is a server-centric approach:

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

It's the same basic idea as the more popular answers on this question, except $provide.value registers a service that contains your default values.

So, on the server, you could have something like:

{
    description: "Visa-4242"
}

And put it into your page via the server-side tech of your choice. Here's a Gist: https://gist.github.com/exclsr/c8c391d16319b2d31a43

1 Comment

As far as I can tell, this is the most Angular method to handle the problem if making an initial $http request is not an option. It also has the benefit of being much easier to modify if you want to swap to $http later. One possible enhancement is that of returning a promise instead to make the changeover even easier. I would very much shy away from setting global vars or using ng-initial here.
1

This one is a more generic version of the ideas mentioned above... It simply checks whether there is any value in the model, and if not, it sets the value to the model.

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 (usage example):

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

1 Comment

This worked for me, but only after passing $scope to the call to getter() - hope this clears things up for anyone else trying this!
0

I tried what @Mark Rajcok suggested. Its working for String values (Visa-4242). Please refer this fiddle.

From the fiddle:

The same thing that is done in the fiddle can be done using ng-repeat, which everybody could recommend. But after reading the answer given by @Mark Rajcok, i just wanted to try the same for a form with array of profiles. Things work well untill i have the $scope.profiles = [{},{}]; code in the controller. If i remove this code, im getting errors. But in normal scenarios i cant print $scope.profiles = [{},{}]; as i print or echo html from the server. Will it be possible to execute the above, in a similar fashion as @Mark Rajcok did for the string values like <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">, without having to echo the JavaScript part from the server.

1 Comment

You can use ng-init to initialize the array and then use ng-repeat to output the form lines: jsfiddle.net/6tP6x/1
0

Just added support for select element to Ryan Montgomery "fix"

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

});

Comments

0

If you have the init value in the URL like mypage/id, then in the controller of the angular JS you can use location.pathname to find the id and assign it to the model you want.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.