How can I make ng-init to trigger ng-change? - angularjs

I just started learning Angular. There is a <select> element that should fill the input elements below with one of some presents.
This is my view.
<select
ng-model="selection"
ng-options="preset.name for preset in presets"
ng-init="selection=presets[0]"
ng-change="select()"></select>
<input ng-model="name" type="text"/>
<textarea ng-model="description"></textarea>
This is my controller.
$scope.presets = [
{ name: 'Lorem ipsum', description: 'Dolor sit amet.' },
{ name: 'Consectetur', description: 'Adipisicing elit.' },
{ name: 'Sed do', description: 'Eiusmod tempor.' },
];
$scope.select = function() {
$scope.name = $scope.selection.name;
$scope.description = $scope.selection.description;
};
When I load the page, I can see the first option selected. But the input fields remain blank until I manually change the selection. Why doesn't Angular set the input fields automatically in the first place and what can I do about it?

The reason it does not set the text fields initially is because there is no binding setup for $scope.name and $scope.description until select() is called. You could set it up initially in your controller function:
$scope.init = function(selected) {
$scope.selection = selected;
$scope.name = $scope.selection.name;
$scope.description = $scope.selection.description;
}
$scope.select = function() {
$scope.name = $scope.selection.name;
$scope.description = $scope.selection.description;
};
And in your HTML:
<select
ng-model="selection"
ng-options="preset.name for preset in presets"
ng-init="init(presets[0])"
ng-change="select()"></select>
Alternative Method:
Alternatively, you can have the input fields bind to the same selection model. That way,
when the selection model is updated on the scope, it will update all the associated views.
Change the model to 'selection.name' and 'selection.description':
<input ng-model="selection.name" type="text"/>
<textarea ng-model="selection.description"></textarea>
There is no need for the select() function anymore because there is already a two-way binding setup between the selected model and the input fields.

Related

$scope.formName.$setPristine() does not work ; How to reset form in angular?

create a form in angular 1.5
< form name="movieForm" ng-submit="addMovie(movie) ../>
and when I try to reset the form after form submission using
$scope.movieForm.$setPristine()
BUT it gives error in console : Cannot read property '$setPristine' of undefined
form.html
<form name="movieForm" novalidate role="form" ng-submit="movieForm.$valid && addMovie(movie)">
<input type="text" ng-model="movie.name" name="mName" required />
<input type="text" ng-model="movie.star" name="mStar" />
<input type="number" ng-model="movie.year" name="mYear" required />
<input type="submit" value="Save" />
<input type="button" value="Reset" ng-disabled="movieForm.$pristine" ng-click="reset()" />
</form>
app.js
(function() {
var app = angular.module('app', []);
app.controller('movieController', function($scope) {
$scope.title = "Home Page";
$scope.movieData = [{
name: 'PiKu',
star: 'Irrfan Khan',
releaseYear: '2015'
}];
$scope.addMovie = function(movie) {
if(movie && movie.name) {
$scope.movieData.push({
name : movie.name,
star : movie.star,
releaseYear : movie.year
});
}
};
$scope.reset = function() {
$scope.movieForm.$setPristine();
$scope.movieForm.$setUntouched();
};
// **un-commenting below line gives error in console**
//$scope.reset();
});
}());
also checked $scope.movieForm() in console but its undefined ?
What is the issue?
see the DEMO
You're trying to access $scope.movieForm before bindings actually initialized.
Edit
To be clear: $setPristine - not clears form fields. For clearing form fields you can just reset the object that form controls are bound to:
$scope.movie = {};
here's working demo
I think you are using $setPristine wrong.
"This method can be called to remove the 'ng-dirty' class and set the form to its pristine state (ng-pristine class). This method will also propagate to all the controls contained in this form."
So this only clears classes but not the $scope variables. You have to reset $scope.movie variable.
for example I added this default form variable
var defaultForm={
name: "",
star: "",
year: ""
}
and modified your reset code
$scope.reset = function() {
$scope.movie = angular.copy(defaultForm);
$scope.movieForm.$setPristine();
};
This works fine.
Check out the fiddle.
http://output.jsbin.com/hugiye/2
As per the angular docs $setPristine():
"This method can be called to remove the 'ng-dirty' class and set the form to its pristine state (ng-pristine class). This method will also propagate to all the controls contained in this form."
This only reset classes not the actual scope variable.
If you want to set scope variable to default state then you should reset scope variable too.
`
var movie = {
name: '',
star: '',
year: ''
};
and in your reset function you have to add this
$scope.movie = angular.copy(movie);
`

Angular - Have input save to variables in factory to be used elsewhere

I'm creating a single page application and need to be able to display input from one template onto another. The way I have my factory currently set up is not working. Can someone help with this?
Factory:
angular.module('BSG').factory('airplaneFactory', function() {
var name = '';
var last_name = '';
var color = '';
function setData(n, ln, c) {
name = n;
last_name = ln;
color = c;
}
return {
name: name,
last_name: last_name,
color: color,
setData: setData
}
})
Controller:
angular.module('BSG').controller('AirplaneCtrl', ['$scope', 'airplaneFactory', function($scope, airplaneFactory) {
'use strict';
function updateFactory() {
airplaneFactory.setData($scope.name, $scope.last_name, $scope.color);
}
}]);
Snippet from template where input is gathered:
<h2>Who is this story about?</h2>
<p><input type="text" placeholder="name" ng-model="name" ng-change="updateFactory()"></p>
<h2>What is {{ name }}'s last name?</h2>
<p><input type="text" placeholder="last name" ng-model="last_name" ng-change="updateFactory()"></p>
It doesn't work because there is no two-way binding in $scope.name = airplaneFactory.name;. That simply writes the factory property value into a scope variable.
To save data in your factory, you can create a setter function:
angular.module('BSG').factory('airplaneFactory', function() {
var name = '';
var last_name = '';
var color = '';
function setData(n, ln, c) {
name = n;
last_name = ln;
color = c;
}
return {
name: name,
last_name: last_name,
color: color,
setData: setData
}
})
And since the services are singletons, the data will remain the same on different controllers.
So to set the data from a controller, you'd do something like this:
airplaneFactory.setData('John', 'Doe', 'red');
To have the factory updates happen automatically, you should set $watchers on the controller scope properties or use onchange listeners on your input elements. For example:
<h2>Who is this story about?</h2>
<p><input type="text" placeholder="name" ng-model="name" ng-change="updateFactory()"></p>
<h2>What is {{ name }}'s last name?</h2>
<p><input type="text" placeholder="last name" ng-model="last_name" ng-change="updateFactory()"></p>
And then in the controller you'd have the updateFactory function which will call the factory setData method:
function updateFactory() {
airplaneFactory.setData($scope.name, $scope.last_name, $scope.color);
}
UPDATE
Here is a Plunker where everything is working, I've added a button to fetch the latest data from the factory, so you can test it: http://plnkr.co/edit/JQd22tEYSPuJwutCLH1B?p=preview
Don't forget that all ng-* attributes are working with scope properties, so:
function updateFactory() {...
won't be enough, since you need to have a scope property with that name:
$scope.updateFactory = updateFactory;
Or simply define the whole function like I did in the Plunker example:
$scope.updateFactory = function() {... etc
Use rootScope to access name, last_name and color in every view and controller (inject the $rootScoope...

AngularJS Checkbox not working

I have a class called Case that contains a list of executionSteps. Each executionStep has a boolean property called enabled. I am trying to set in on the HTML side but it never gets updated on the JS side.
HTML side
<td>
<input type="checkbox"
ng-checked="acase.executionSteps[0].enabled"
ng-model="aa" ng-change="updateCaseExecutionStep('{{study.id}}','{{acase.id}}','{{acase.executionSteps[0].id}}','{{acase.executionSteps[0]}}')"/>
</td>`
On the controller side I have the function
updateCaseExecutionStep
defined as shown below
$scope.updateCaseExecutionStep = function(studyId,caseId,executionStepId,executionStep){
...
...
}
Problem is when I update my checkbox or even manually update the enabled property of the executionStep
$scope.updateCaseExecutionStep = function(studyId,caseId,executionStepId,executionStep){
executionStep.enabled = true;
...
}
I don't see any change. The enabled property of executionStep passed in the JS does not change. Please help.
Do I have to modify somehow on the The HTML side ?
You are trying to force too complex solution. To start with, you do not need ng-checked nor ng-change when you are using ng-model.
Let's say you have the following controller
app.controller('MainCtrl', function($scope) {
$scope.case = {
caseId: 0,
steps: [
{ id: 1, name: 'First step', enabled: true },
{ id: 2, name: 'Second step', enabled: false },
{ id: 2, name: 'Third step', enabled: false }]
};
});
And related HTML
<div ng-repeat="step in case.steps">
<input type="checkbox" ng-model="step.enabled"> {{ step.name }}
</div>
That's all it takes!
Example Plunk here http://plnkr.co/edit/QjD91l
Edit:
If you need to do some processing based on selection, then yes, you could add ng-change to input control. Then HTML becomes
<input type="checkbox" ng-model="step.enabled" ng-change="stateChanged(step)"> {{ step.name }}
And in controller
$scope.stateChanged = function(step){
console.log('Changed step id:' + step.id + ' enabled state to ' + step.enabled;
};
I had to abandon ng-model for my checkbox as it was not picking up the initial value that was set in the model (in response to a service call). All of the other input controls were responding correctly to the model (and, interestingly, were correctly enabled/disabled based on the value backing the checkbox).
Instead, I used the 'checked' attibute and ng-click, as so:
<input type="text" ng-disabled="!myModel.isFruit" ng-model="myModel.seedCount">
<input type="checkbox" checked="{{myModel.isFruit}}" ng-click="onFruitClicked()"> Is Fruit
In my controller:
$scope.myModel = {
isFruit : myServices.getIsFruit(),
seedCount : myServices.getSeedCount()
};
$scope.onFruitClicked = function() {
// toggle the value
$scope.myModel.isFruit = !$scope.myModel.isFruit;
// save the new value
myServices.setIsFruit($scope.myModel.isFruit);
};

Saving a checkbox list in Angularjs with node

I want to save and load a checkbox list using binding in angularjs with a node backend. This SO question (How do I bind to list of checkbox values with AngularJS?) answers how I can load the check box from a static javascript object but how can I save the checkbox values after the user selects them?
I want to save the checkbox values in a single field but how can I tell angular to bind the checkbox values into a single field defined in a model to my mongodb? I cant just use ng-model as there are multiple checkboxes.
Needless to say that I am new to angular so any help here is greatly appreciated ...
Thanks for any help you can provide.
kseudo
Just add ng-model to your checkbox. By this way you can update model in controller on any checkbox state change.
Here is example:
HTML
<div ng-controller="Ctrl">
<label ng-repeat="fruit in fruits">
<input
type="checkbox"
name="fruit.name"
ng-model="fruit.value"
> {{fruit.name}}
</label>
<pre>{{fruits| json}}</pre>
</div>
JS
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.fruits = [{
name: 'apple',
value: false
}, {
name: 'orange',
value: false
}, {
name: 'pear',
value: false
}, {
name: 'naartjie',
value: false
}];
}
Demo Fiddle
[EDIT]
BTW we can make the copy by using angular.copy() method. When we press button, the new copy of fruits model will be created (and you should send it to server as json). Old model fruits will stay the same:
$scope.fruitsCopy = [];
$scope.init = function(){
$scope.fruitsCopy = angular.copy($scope.fruits );
}
To convert data to JSon I would use:
var jsonData = JSON.stringify($scope.fruitsCopy);
Demo2 Fiddle
Let's say you defined your model as such:
function Ctrl($scope) {
$scope.items = [{
name: 'A',
checked: false
}, {
name: 'B',
checked: false
}, {
name: 'C',
checked: false
}, {
name: 'D',
checked: false
}];
}
And created a view for the model:
<ul>
<li ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.checked">
{{item.name}}
</label>
</li>
</ul>
<button ng-click="save()">save</button>
Next you have to define save function:
$scope.save = function() {
//angular.toJson converts array to string, something like
// '[{"name":"A","checked":false},{"name":"B","checked":true},...]'
var json = angular.toJson($scope.items);
//Service is angular service for your model that makes http requests to your backend
Service.save(json).then(function(){
//here you can notify user that data is persisted
}, function() {
//here you can notify user that there was a problem with request
});
}
And a simple model service:
.service('Service', function($http) {
return new function() {
this.save = function(data) {
return $http.post('url/to/backend', data);
}
}
});

AngularJS default value for ng-model

Is it possible to make ng-models get default values, like ''?
I have a form on which I have used jQuery's serialize function, and whenever a a value is not present, it will still include it in the serialized data, E.g. {name: '', age: ''}.
However, when I use try using posting it by using Angular's $http and getting the model from $scope, it appears as undefined when it is empty.
You may also try:
<div ng-init="foo={}">
<input type="text" ng-model="foo.name=''"/>
</div>
or:
<input type="text" ng-init="foo.name=''"/>
it will present:
foo = { name: '', .... }
You can simply define the properties on the model in the scope in advance of using them in your view.
If you show your controller code I'll show you what you need to update to add them to the scope.
It should look something like:
In your js file that defined the app
angular.module("MyModule", []).controller('MyCtrl', ['$scope', function ($scope) {
$scope.myModel = {name:""};
}]);
In your HTML
<input type="text" ng-model="myModel.name"/>
Note I prefer not having global variables/functions so I'm using a different syntax provided by Angular to avoid this and to allow for minification.
You shouldn't need to use jQuery serialize.
Your form should be bound to the scope like this:
<form ng-controller="MyCtrl" ng-submit="submit()">
<input type="text" name="name" ng-model="formData.name">
<input type="text" name="age" ng-model="formData.age">
<input type="submit" value="Submit Form">
</form>
function MyCtrl($scope) {
$scope.formData = {};
$scope.submit = function() {
$http.post('/someUrl', $scope.formData)
};
}
So that in $http you can simply pass $scope.formData.
Is there a more effective way to serialize a form with angularjs?
Sometimes, you don't know if the value is set or not. That's what defaults are for.
I use this when I need to make sure a variable has some value. You can set it in your controller
$scope.possiblyEmptyVariable = $scope.possiblyEmptyVariable || "default value";
Works great without Angular as well
// some object that may or may not have a property 'x' set
var data = {};
// Set default value of "default"
// carefull, values like 'null', false, 0 will still fall to defaults
data.x = data.x || "default";
// Set default value of "default"
// this will not fall to defaults on values like 'null', false, 0
data.x = !"x" in data || "default";
If you need to set a bunch of values, use angular.extend or jQuery.extend
data.z = "value already set";
var defaults = { x: "default", y: 1, z: "default" };
var newData = angular.extend({}, defaults, data);
// { x: "default, y: 1, z: "value already set" }
Hope that helps

Resources