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()
}
Calvin Froedge
  • 16,135
  • 16
  • 55
  • 61

14 Answers14

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?

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • +1 for the angular way (of the less preferred practice). Example: http://jsfiddle.net/9ymB3/ – John Lehmann May 10 '13 at 21:25
  • 2
    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. ``. Bit ugly? Yes, but does the trick & solves an integration headache in the process. – GFoley83 Jul 29 '13 at 21:01
  • 13
    +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. – oma Oct 05 '13 at 15:26
  • Using ng-init is the best option for me because I want to have a form that also works (only server validation) without javascript turned on and is enhanced by angular for client validation...so I put the ng-init attribute next to my value attribute. – Darren Dec 02 '14 at 21:10
  • 1
    Actually I take that back, this works best for me, awesome: http://stackoverflow.com/a/20522955/329367 – Darren Dec 02 '14 at 21:18
  • 1
    FWIW: I dislike variable assignment in template expressions because it's not testable. – Ben Lesh Jan 09 '15 at 17:17
  • I agree that server-side data population does have its benefit, but having values in the templates does not strictly adhere to the MVC design. – plong0 Sep 10 '15 at 17:46
  • Plz dont use value ='something' => just for being careful.. dont spoil the concept plz – Sami Nov 24 '15 at 15:49
  • I'm using this as a placeholder on a non-input element. This should be the answer, since it's a 1:1 match on the question. – Niels Steenbeek Mar 16 '16 at 20:34
  • What if 'Visa-424' is a variable passed from controller? I use `ng-init="card.description='{{something}}'"` and it's not working. Please describe it. – Abaij Nov 22 '17 at 14:29
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;
});
starball
  • 20,030
  • 7
  • 43
  • 238
Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
  • 4
    Yea, it helps. I guess I'm just shocked that the Angular guys made this decision. – Calvin Froedge Dec 07 '12 at 21:40
  • 126
    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. – Ben Lesh Dec 07 '12 at 21:46
  • 3
    @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. – GFoley83 Apr 01 '13 at 21:21
  • Do I have links that support my claim that a rendering the HTML is moving from the server to the client? Technical docs? No. Examples: yes: http://twitter.com, http://mail.google.com, http://facebook.com, etc. There are cases where sites simply cannot go 100% in this direction, but it's usually due to a dependency on some tech that's a little behind. Like search engine bots, for example. e-commerce sites, and sites that have a requirement of being catalog by crawlers need to be rendered at the server because most crawlers don't handle JavaScript well. – Ben Lesh Apr 02 '13 at 00:12
  • @blesh: Great, thanks. Makes sense really. I'd just be a little concerned that doing the majority of rendering on the client side might be a touch slow on mobile devices. – GFoley83 Apr 02 '13 at 23:48
  • 1
    Well, actually Twitter switched to server side rendering because of the page loading speed issues. It all depends on your use case. I'm currently using both server-side and client-side rendering together, by first serving the populated html from server, and leaving the rest to angular. – Hakan Deryal Apr 22 '13 at 17:27
  • 1
    @GFoley83 - actually, it's not that slow at all. If you want to worry about mobile performance, don't do animations - these are far more taxing than anything you'll write in angular ;) – Oddman May 09 '13 at 02:33
  • 19
    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. – hunterloftis Jul 31 '13 at 18:39
  • @hunterloftis, can you provide an instance where server-side rendering is the best solution in an Angular application? – Ben Lesh Aug 01 '13 at 18:42
  • 8
    @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: https://blog.twitter.com/2012/improving-performance-twittercom – hunterloftis Aug 01 '13 at 19:49
  • "In an Angular application" <-- .. SPA development is going to be a bad choice for anything requiring SEO. And if you read my answer completely, you'd have seen I said "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." "**If you absolutely MUST render your values into your HTML from your server, you could ...**" – Ben Lesh Aug 02 '13 at 20:17
  • Seems like maybe you read just one sentence and downvoted because it offended you. I don't generally care about the downvote, but I do care that someone didn't understand what I was saying. I'll try to change up my tone in future answers. – Ben Lesh Aug 02 '13 at 20:19
  • blesh, how do you suggest to handle ASP.NET MVC action post rendering with angular? For example, I have "LogOn" action in which I validate the user and I determine that password is invalid. In that case I need to render the same form with previous email filled in and error displayed. Whats the proper way of doing this with angular? – Michael Logutov Aug 15 '13 at 07:39
  • "Proper?" well anything that works, frankly... but the "cleanest" way would be to have an angular service make an $http call that passes the username and password (preferrably over https) to your end point and gets back some sort of authentication token or other security information you can store locally and send with future requests. OAuth is ideal for a situation like this, but you could leverage ASP.Net's forms authentication cookie as well. – Ben Lesh Aug 15 '13 at 14:23
  • 2
    @BenLesh, I'm getting values into AngularJs from my web services using $http. But that's chicken and egg problem: to use $http I need to supply to Angular code a URL of my web service! I don't want to hard-code it in Angular, my .NET code gets this URL from its configuration and then needs to supply it to AngularJS. So, how to do that? Through global variables accessing them with $window, as you said? This looks really ugly. I'd like to have an elegant and a standard way of initializing Angular by variables supplied by server (ASP.NET C#) code. – vkelman Jan 08 '15 at 20:32
  • @BenLesh, Angular, and friends: in regards to avoiding server-side writing for bandwidth and a multitude of other perfectly sensible reasons, are we no longer concerning ourselves with clients that don't support Javascript as we move into this client-side rendering future we speak of? – Matt Borja Jun 18 '15 at 17:11
  • @MattBorja of course we should concern ourselves with those clients, but often that concern means some sort of sensible degradation of experience. Even if that experience just means a message saying "Requires JavaScript"... ideally you'll show whatever you *can* show to the user at that time. Things like Isomorphic JavaScript will help with those scenarios as time goes on. – Ben Lesh Jun 23 '15 at 17:40
  • 1
    @BenLesh In terms of server load, whats the difference between rendering the data into the HTML as its being served, vs responding to an HTTP request which comes after the page has loaded? Is answering requests "easier" than rendering an HTML string? Thanks – stackPusher Oct 15 '17 at 07:40
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);
      }
    ]
  };
});
Kevin Stone
  • 8,831
  • 41
  • 29
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);
                    }
                }
            }
        }
    });
jtompl
  • 1,034
  • 13
  • 16
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/.

user1519240
  • 2,186
  • 1
  • 22
  • 20
  • 1
    This is a great post. My favorite answer since the question was to set the value of an input according to the initials values. – RPDeshaies Dec 02 '14 at 13:11
  • I created a Plnkr to test this code and it works great : http://plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview – RPDeshaies Dec 02 '14 at 14:14
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 :)

koxon
  • 818
  • 1
  • 10
  • 12
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");
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.

Jkarttunen
  • 6,764
  • 4
  • 27
  • 29
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.

Community
  • 1
  • 1
coder
  • 1,274
  • 1
  • 13
  • 19
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

exclsr
  • 3,329
  • 23
  • 27
  • 1
    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. – richard Oct 02 '14 at 01:00
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>
Eitan H.S.
  • 430
  • 3
  • 15
  • This worked for me, but only after passing `$scope` to the call to `getter()` - hope this clears things up for anyone else trying this! – zesda Jul 13 '17 at 16:09
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.

Rajkamal Subramanian
  • 6,884
  • 4
  • 52
  • 69
  • 1
    You can use ng-init to initialize the array and then use ng-repeat to output the form lines: http://jsfiddle.net/6tP6x/1/ – Karen Zilles Mar 22 '13 at 17:05
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)
    }]
}

});

kolesso
  • 336
  • 3
  • 14
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.

xav
  • 5,452
  • 7
  • 48
  • 57
Neon
  • 13
  • 1
  • 3