How to prefill a calculation form with initial values? - angularjs

I've a long calculation form and want to prefill some values for displaying initial results. These initial values should live in the template to have these easily editable. It seems, the angular way is to use a directive which reads the input value fields and initializes the app with these values.
Here is one way to to set the model value from the input field:
<input name="card[description]" value="Visa-4242" ng-model="card.description" ng-initial>
Coffeescript:
app = angular.module 'forms', []
app.directive 'ngInitial', ->
restrict: 'A'
controller: ['$scope', '$element', '$attrs', '$parse', ($scope, $element, $attrs, $parse) ->
val = $attrs.sbInitial || $attrs.value
getter = $parse($attrs.ngModel)
setter = getter.assign
setter($scope, val)
]
Source: Angular js init ng-model from default values
Unfortunately this doesn't work in my app. The values are displayed but the results aren't calculated. I have to manual fill out the fields in the browser to start the calculation.
The input fields are watched in my controller, like so:
$scope.$watch(
'inputValues.permeatCapacity',
function (newValue, oldValue) {
if (newValue === oldValue) {
return;
}
permeatCapacity = $scope.inputValues.permeatCapacity;
permeatPerDay = permeatFactory.getPermeatPerDay(permeatCapacity);
$scope.permeatPerDay = $filter('number')(permeatPerDay, 2);
}
);
What's the best directive for this problem or is there a better way in Angular.js?
UPDATE
I've just found a really dirty way in the controller to update the input fields after initialization that make use of the bound calculations. It not only feels like an bad idea, the initial values also don't live in the template:
$timeout(function () {
$scope.inputValues.overallRecovery = 40;
$scope.inputValues.permeatCapacity = 7.2;
}, 0);
In comparison, this initial object in my controller fills out the fields but doesn't trigger the watchers (important for the bound calculations):
$scope.inputValues = {
overallRecovery: 40,
permeatCapacity: 7.2
};
But there is a better way to do this, isn't it?

Ok, here's what I found out. The condition:
if (newValue === oldValue) {
return;
}
in my watch methods blocked the initial calculation. I can now use a really simple directive for my usecase:
An input field in the template:
<input type="text" id="overall-recovery" value="40" ng-model="inputValues.overallRecovery" initial-value>
The directive:
angular.module('ksbApp')
.directive('initialValue', function ($compile) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var initialValue = attrs.value;
ngModel.$setViewValue(initialValue);
}
};
});
In this case, the value "40" is pushed to the model during initialization.
Is there anything I could do better?

Not sure why you would need a directive for this, unless I dont understand the question fully. Spin up the model in scope, setting the values where on the controller, and then bind them using ngmodel.
in the controller:
scope.inputValues = {permeatCapacity: 1};
scope.calc = function(){
return permeatFactory(scope.inputValues.perMeatCapacity);
};
in the view:
<input ng-model="inputValues.permeatCapacity" type="text">
<button ng-click="calc()" />
In later versions of angular you can even have the calculation run AS the values in the model change using ng-change.

Related

In Angular, bind attribute from scope variable *before* ngModel binds the input’s `value`

In my Angular app, I defined a custom slider directive that wraps <input type="range">. My custom directive supports ngModel to bind the slider’s value to a variable. The custom directive also requires a fraction-size attribute. It does a calculation on the value and then uses the result to set the step value of the wrapped <input>.
I am seeing a bug when I combine these two features – ngModel and my bound attribute value. They are run in the wrong order.
Here is a demonstration:
angular.module('HelloApp', []);
angular.module('HelloApp').directive('customSlider', function() {
var tpl = "2 <input type='range' min='2' max='3' step='{{stepSize}}' ng-model='theNum' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '='
},
link: function(scope, element, attrs, ngModelCtrl) {
scope.stepSize = 1 / scope.fractionSize;
scope.$watch('theNum', function(newValue, oldValue) {
ngModelCtrl.$setViewValue(newValue);
});
ngModelCtrl.$render = function() {
scope.theNum = ngModelCtrl.$viewValue;
};
}
};
});
angular.module('HelloApp').controller('HelloController', function($scope) {
$scope.someNumber = 2.5;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<div ng-app="HelloApp" ng-controller="HelloController">
<h3>Custom slider</h3>
<custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
<h3>View/edit the slider’s value</h3>
<input ng-model="someNumber"></input>
</div>
The above slider should start at the middle, which represents 2.5. But it actually starts all the way at the right (representing 3). The slider fixes itself and allows the value 2.5 if you drag it, or if you change its bound value by editing the text field.
I have figured out why this is happening in the demonstration – I just don’t know how to fix it. Currently, when a new custom slider is dynamically added to the page, the wrapped <input>’s step is undefined, and defaults to 1. Then ngModel sets the value to 2.5 – but since step is 1, the value in the input is rounded to 3. Finally, step is set to 0.1 – too late for it to matter.
How can I ensure that the step attribute’s value is bound before ngModel sets the input’s value?
In the example above, the slider is on the page at page load. In my real code, multiple new sliders are added dynamically. They should all bind their step and value in the correct order whenever they are added.
A workaround I don’t like
A workaround is to hard-code the step in the template instead of setting it dynamically. If I were to do this, my custom slider directive would have no use, and I would remove it and just use an <input> directly:
<input type="range" min="2" max="3" step="0.1" ng-model="someNumber">
If I use that, the slider’s value is set correctly, without rounding. But I want to to keep my custom directive, customSlider, so that the calculation of step is abstracted.
Things I tried that didn’t work
Changing the order of the step and ng-model attributes in the template doesn’t have any effect.
I started trying to add a compile function, instead of just having a link function. But I can’t set stepSize in compile because scope isn’t available during the compile phase.
I tried splitting my directive’s link function into pre-link and post-link functions. But whether I set scope.stepSize in pre or in post, the page works as before.
Manually calling scope.$digest() right after setting scope.stepSize just throws Error: [$rootScope:inprog] $digest already in progress.
I don’t think custom directive priorities are a good fit for this, because there is only one custom directive involved. My binding of step’s value isn’t a custom directive, it is just raw {{}}-binding. And I think binding step is a simple enough task that it shouldn’t be wrapped in its own directive.
Taking #pixelbits' answer of direct DOM manipulation further, I wouldn't have another ng-model on the inner input at all, and instead always set/get the properties on the input directly
You have one abstraction level to think about when setting/getting values from the input. Raw DOM events and elements.
As such you are not limited to what Angular allows on such elements (indeed: what it does seems to not be able to handle your use case without work-around). If the browser allows it on the range input, you can do it.
Have 2 ngModelControllers in play on the one UI widget that sets one value can get a bit confusing, at least for me!
You still have access to the outer ngModelController pipeline and all its functionality regarding parsers and validators, if you need/want to use it.
You save from having an extra watcher (but this could be a micro/premature optimization).
See an example below.
angular.module('HelloApp', []);
angular.module('HelloApp').directive('customSlider', function() {
var tpl = "2 <input type='range' min='2' max='3' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '='
},
link: function(scope, element, attrs, ngModelCtrl) {
var input = element.find('input');
input.prop('step', 1 / scope.fractionSize);
input.on('input', function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(input.prop('value'));
});
});
ngModelCtrl.$render = function(value) {
input.prop('value', ngModelCtrl.$viewValue);
};
}
};
});
angular.module('HelloApp').controller('HelloController', function($scope) {
$scope.someNumber = 2.5;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<div ng-app="HelloApp" ng-controller="HelloController">
<h3>Custom slider</h3>
<custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
<h3>View/edit the slider’s value</h3>
<input ng-model="someNumber"></input>
</div>
Also available at http://plnkr.co/edit/pMtmNSy6MVuXV5DbE1HI?p=preview
In your link function, manipulate the DOM by adding the step attribute.
You can also simplify your binding with the outer ngModel by putting theNum: '=ngModel' in scope.
var app = angular.module('HelloApp', []);
app.directive('customSlider', function () {
var tpl = "2 <input type='range' min='2' max='3' ng-model='theNum' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '=',
theNum: '=ngModel'
},
link: function (scope, element, attrs, ngModelCtrl) {
var e = element.find('input')[0];
var step = 1 / scope.fractionSize;
e.setAttribute('step', step);
scope.$watch('fractionSize', function (newVal) {
if (newVal) {
var step = 1 / newVal;
e.setAttribute('step', step);
}
});
}
};
});
angular.module('HelloApp').controller('HelloController', function($scope) {
$scope.someNumber = 2.5;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<div ng-app="HelloApp" ng-controller="HelloController">
<h3>Custom slider</h3>
<custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
<h3>View/edit the slider’s value</h3>
<input ng-model="someNumber"></input> {{ someNumber }}
</div>
I'm not sure if this is the correct way to do it, but adding a timeout to your main controller solves the issue.
$timeout(function() {
$scope.someNumber = 2.5;
});
Edit: While it seems like a dirty hack at first, but note that it is common for (final) $scope variables to be assigned later than templates, because of additional ajax calls to retrieve the values.

ngChange fires before value makes it out of isolate scope

//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function () {
return {
scope: {
searchModel: '=ngModel',
searchChange: '&ngChange',
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel" ng-change="searchChange()"/>',
restrict: 'E'
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
Here is a simplified directive to illustrate. When I type into the input, I expect the console.log in loadResults to log out exactly what I have already typed. It actually logs one character behind because loadResults is running just before the searchFilter var in the main controller is receiving the new value from the directive. Logging inside the directive however, everything works as expected. Why is this happening?
My Solution
After getting an understanding of what was happening with ngChange in my simple example, I realized my actual problem was complicated a bit more by the fact that the ngModel I am actually passing in is an object, whose properties i am changing, and also that I am using form validation with this directive as one of the inputs. I found that using $timeout and $eval inside the directive solved all of my problems:
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function ($timeout) {
return {
scope: {
searchModel: '=ngModel'
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel.subProp" ng-change="valueChange()"/>',
restrict: 'E',
link: function ($scope, $element, $attrs, ngModel)
{
$scope.valueChange = function()
{
$timeout(function()
{
if ($attrs.ngChange) $scope.$parent.$eval($attrs.ngChange);
}, 0);
};
}
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
The reason for the behavior, as rightly pointed out in another answer, is because the two-way binding hasn't had a chance to change the outer searchFilter by the time searchChange(), and consequently, loadResults() was invoked.
The solution, however, is very hacky for two reasons.
One, the caller (the user of the directive), should not need to know about these workarounds with $timeout. If nothing else, the $timeout should have been done in the directive rather than in the View controller.
And two - a mistake also made by the OP - is that using ng-model comes with other "expectations" by users of such directives. Having ng-model means that other directives, like validators, parsers, formatters and view-change-listeners (like ng-change) could be used alongside it. To support it properly, one needs to require: "ngModel", rather than bind to its expression via scope: {}. Otherwise, things would not work as expected.
Here's how it's done - for another example, see the official documentation for creating a custom input control.
scope: true, // could also be {}, but I would avoid scope: false here
template: '<input ng-model="innerModel" ng-change="onChange()">',
require: "ngModel",
link: function(scope, element, attrs, ctrls){
var ngModel = ctrls; // ngModelController
// from model -> view
ngModel.$render = function(){
scope.innerModel = ngModel.$viewValue;
}
// from view -> model
scope.onChange = function(){
ngModel.$setViewValue(scope.innerModel);
}
}
Then, ng-change just automatically works, and so do other directives that support ngModel, like ng-required.
You answered your own question in the title! '=' is watched while '&' is not
Somewhere outside angular:
input view value changes
next digest cycle:
ng-model value changes and fires ng-change()
ng-change adds a $viewChangeListener and is called this same cycle.
See:
ngModel.js#L714 and ngChange.js implementation.
At that time $scope.searchFilter hasn't been updated. Console.log's old value
next digest cycle:
searchFilter is updated by data binding.
UPDATE: Only as a POC that you need 1 extra cycle for the value to propagate you can do the following. See the other anwser (#NewDev for a cleaner approach).
.controller('mainCtrl', function ($scope, $timeout){
$scope.loadResults = function (){
$timeout(function(){
console.log($scope.searchFilter);
});
};
});

AngularJS - how to focus on an element via a controller

I know the question has been asked multiple time, but I can't seem to find anywhere how to focus to an element from within a controller. What is the best approach? Would it be better to do a directive? But if so, then how would I call it within my controller? Or is it better to create a service then?
What I already have and works properly from within HTML code is a directive:
.directive('ngxFocus', ['$timeout', function($timeout) {
return function(scope, element, attr) {
$timeout(function () {
element.focus();
}, 10);
};
}])
Can I call directive within controller? I'm still learning AngularJS and I'm a bit confused on what the best approach is in this case. I really want to do it via the controller, at the moment I use a simple 1 line of jQuery to focus, but yeah it's not the Angular way and so I'd like to go with the correct way.
Note
To be more specific with an example, let say I have 10 inputs in the HTML and let say that inside the execution of a function (defined in the controller), I want to focus on 1 of the multiple inputs directly from the function (again this is all declared inside the controller). I would rather not write anything inside the HTML code, if possible, but instead call a focus function or something that will focus to the input I chose. I know I could write it simply in jQuery with $('input12').focus(); but I want to know how to do it the AngularJS way. All the answers I get so far are based on writing a Directive, that also equals to writing something inside the HTML, isn't there any other way???
Example
For more explicit example of my form... I have a first input connected to a Yahoo web service (stock market), this input filled by the user will hold a stock quotes symbol that can be anywhere in the world and then the user will choose (from a dropdown) his Bank account... now from there, my controller will check that the stock quotes market is in the same currency as the user's bank account (ex.: GOOG is US currency, if user's account is in $CAD, it will fail because GOOG is in $USD). If currency isn't the same, I want to advise my user and to do so I would seriously prefer to focus on the field so he could change his symbol if he made an error.
I you're trying to work with elements in controller, be sure you're going wrong, the controller's target in to bind data received from services to view, not to manipulate view.
If you want to focus on an element with route change:
app.directive('focuser', ['$location', function ($location) {
return {
restrict: 'A',
link: function ($scope, $element) {
$scope.$watch(function () {
//simply focus
$element.focus();
//or more specific
if ($location.$$url == '/specific/path') {
$element.focus();
}
});
}
};
}]);
I've made this directive:
app.directive('rfocus',function(){
return {
restrict: 'A',
controller: function($scope, $element, $attrs){
var fooName = 'setFocus' + $attrs.rfocus;
$scope[fooName] = function(){
$element.focus();
}
},
}
});
It adds to controller's $scope function to set focus on element. Name of the function is based on value given in attribute.
Using: <input type="text" rfocus="Input1"/> will create function setFocusInput1() which you can use in your controller.
Here is the fiddle: http://jsfiddle.net/aartek/2PJMQ/
I've recently started to learn Angular, too, but hopefully I can provide you a different way of approaching this.
I've been using some basic jQuery to focus, too, so in that regard, I can't really help you. However, with regard to calling a directive within a controller, I can't find any articles that say "yes, you can", or "no, you can't". I know that you can declare a controller within a directive, though, so you miiiiight be able to do something like this:
.directive('ngxFocus', function() {
return {
restrict: 'A',
controller: //do your controller stuff here
}])
I know it's an old question, but here's an other approach, where you set to true a variable in the controller, and that's this action that set the focus to your element.
Try this:
myAngularModule.directive('goFocus', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(attrs.goFocus, function (newValue) {
if (newValue) {
$timeout(function () {
element[0].focus();
}, 100);
}
});
element.bind("blur", function (e) {
$timeout(function () {
scope.$apply(attrs.goFocus + "=false");
}, 10);
});
element.bind("focus", function (e) {
$timeout(function () {
scope.$apply(attrs.goFocus + "=true");
}, 10);
});
}
}
}]);
In HTML template:
<input go-focus="focusvar1" type="text" ng-model="mytext1" />
<input go-focus="focusvar2" type="text" ng-model="mytext2" />
<input go-focus="focusvar3" type="text" ng-model="mytext3" />
<button ng-click="doFocus()">OK</button>
In javascript angular controller:
myAngularModule.controller('myController', function () {
var self = this;
self.doFocus = function () {
// do some logic and focus your field
self.focusvar2 = true;
};
});

Fill ng-model default value from html in AngularJS [duplicate]

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()
}
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?
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;
});
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);
}
]
};
});
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);
}
}
}
}
});
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/.
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 :)
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");
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.
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.
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
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>
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.
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)
}]
}
});
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.

Angular js init ng-model from default values

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()
}
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?
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;
});
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);
}
]
};
});
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);
}
}
}
}
});
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/.
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 :)
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");
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.
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.
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
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>
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.
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)
}]
}
});
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.

Resources