AngularJS - radio button not updating model - javascript

First steps in AngularJS. I'm facing a strange problem related to this, but the solution doesn't work to me, maybe I'm missing something (as I said, I'm a really n00b with angular).
I'm my HTML, I'm building some radio buttons like that:
<div ng-Controller="PageTwo" >
<h3>General Information > Knowledge About </h3>
<div>
<b>User</b>
<div>
<div ng-repeat="option in userOptions">
<input type="radio" name="userGroups" ng-model="$parent.userSelected" value="{{option.id}}" id="{{option.id}}">{{option.text}}
</div>
</div>
Selected thing: {{userSelected}}
</div>
</div>
This is my related Controller:
uxctModule.controller ('PageTwo', function ($scope, ModelData){
$scope.data = ModelData;
$scope.userOptions = [{text:'Option 1', id:0}, {text:'Option 2', id:1}, {text:'Option 3',id:2}, {text:'Option 4', id:3}];;
$scope.userSelected = ModelData.knowledgeAboutUser;
});
The model is the following object
uxctModule.factory ("ModelData", function () {
var data = {
// more code here
knowledgeAboutUser : 3,
}
return data
});
Now, the problem is that I'm logging in the console the ModelData object, and I've noticed that it's not updating when clicking the radio buttons.
I think the binding it's ok: I've tried to change the value in the Model, and the app selects the corresponding radio button.
Any help it's really appreciated, I'm stuck on this for hours

You can remove the intermediate variable $scope.userSelected:
<div ng-repeat="option in userOptions">
<input type="radio" name="userGroups" ng-model="data.knowledgeAboutUser" value="{{option.id}}" id="{{option.id}}">{{option.text}}
</div>
Selected thing: {{data.knowledgeAboutUser}}

It working fine
just change
$scope.userSelected
to
$scope.userSelected.selected
Working Code
script
var app = angular.module('app', []);
app.factory ("ModelData", function () {
var data = {
// more code here
knowledgeAboutUser : 2,
}
return data
});
app.controller("PageTwo", function ($scope, ModelData) {
$scope.userSelected = {};
$scope.userOptions = [{text:'Option 1', id:0}, {text:'Option 2', id:1}, {text:'Option 3',id:2}, {text:'Option 4', id:3}];;
$scope.userSelected.selected = ModelData.knowledgeAboutUser;
});
html
<div ng-app="app" ng-Controller="PageTwo">
<h3>General Information > Knowledge About </h3>
<div> <b>User</b>
<div>
<div ng-repeat="option in userOptions">
<input type="radio" name="userGroups" ng-model="userSelected.selected" value="{{option.id}}" id="{{option.id}}">{{option.text}}
</div>
</div>
{{userSelected.selected}}
</div>
</div>

Related

ng-if should show only one object

Hi dear Stackoverflow Community i have a problem. First here is my Code:
html:
<md-card md-theme-watch flex="100" ng-repeat="offer in company.offers">
<md-button class="md-fab md-mini md-primary md-fab-oi" aria-label="copy" ng-click="company.setEditVisibility()">
<oi-offer-edit offer="offer" is-change="true" ng-if="company.isEditVisible">
</oi-offer-edit>
</md-card>
My controller:
function setEditVisibility(){
vm.isEditVisible = !vm.isEditVisible;
}
it work just fine the problem is that it shows oi-offer-edit directive for every repeated Object.
If you need more info pls dont hesitate to ask!
If you don't want to touch your markup and want the oi-offer-edit element to be repeated, you have to use a boolean property on the offer object itself:
<md-card md-theme-watch flex="100" ng-repeat="offer in company.offers">
<md-button class="..." ng-click="offer.formVisible = !offer.formVisible">
<oi-offer-edit offer="offer" is-change="true" ng-if="offer.formVisible">
</oi-offer-edit>
</md-card>
Solution before i realized, that you want to have that directive in every md-card:
You might want to place your oi-offer-edit element outside your ng-repeat container, because as far as i see it in your snippet, you only need one with the offer-data of the selected company.offers.
So you could just cache the offer on the click handler and make your oi-offer-edit visible. Something like this:
<md-card md-theme-watch flex="100" ng-repeat="offer in company.offers">
<md-button class="..." ng-click="company.setEditVisibility(offer)">
</md-card>
<oi-offer-edit offer="currentSelectedOffer" is-change="true" ng-if="company.isEditVisible">
</oi-offer-edit>
function setEditVisibility(selectedOffer){
vm.currentSelectedOffer = selectedOffer;
vm.isEditVisible = !vm.isEditVisible;
}
it will because you have bounded to each ng-repeat object .
If you want to toggle the visibility of oi-offer-edit independently of each offer object then you should move the boolean flag you're checking in the ng-if directive into the offers array.
Check the below example it will help you accomplish what you want to do.
angular
.module('demo', [])
.controller('DefaultController', DefaultController);
function DefaultController() {
var vm = this;
vm.company = {
offers: [
{ id: 1, name: 'Offer 1' },
{ id: 2, name: 'Offer 2' },
{ id: 3, name: 'Offer 3' }
]
};
vm.setEditVisibility = setEditVisibility;
function setEditVisibility(id) {
for (var i = 0; i < vm.company.offers.length; i++) {
if (vm.company.offers[i].id === id) {
vm.company.offers[i].isEditVisible = !vm.company.offers[i].isEditVisible;
}
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
<div ng-repeat="offer in ctrl.company.offers">
{{offer.name}}
<button ng-click="ctrl.setEditVisibility(offer.id)">Toggle Edit Visibility</button>
<span ng-if="offer.isEditVisible">{{offer.name}} Edit Details</span>
</div>
</div>
</div>

Create an Angularjs web form that duplicates but doesn't clone

I have "attempted" to build an Angularjs web form that duplicates the input fields upon clicking the add button and removes newly added fields upon clicking the "close button" which seems to work okay. Great, right!?
http://plnkr.co/edit/jUulJQ52m9QEZLkG086B
However, the duplicate forms created after clicking the add button are exact clones of each other or the original. So, when there are two forms on the screen and data entered into form 1, it is duplicated on form 2 and visa versa.
My question:
How do I continue down the path of having the ability
to duplicate the elements, really, within the "fieldset" element of the form, but without it being cloned?
Following are snippets of my HTML and AngularJs file. I have wracked my head on this for a while now and keep ending up in the same spot.....A fresh set of eyes and/or any suggestions would be great!
Thanks!
Index.html
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4" ng-controller="formCtrl">
<h1>Expense Form</h1>
<form class="form-horizontal" id="itvExpenseForm" name="ExpenseForm" ng-repeat="field in table.fields track by $index" novalidate>
<fieldset id="innerForm" ng-model="table.fields[$index]">
<legend>
<span>{{$index + 1}}.</span>
</legend>
<div class="form-group" ng-controller="quarterListCtrl">
<label for="ddlQuarters" class="col-sm-4 control-label">Year Quarter:</label>
<div class="col-sm-8">
<select class="form-control" name="Quarters" id="ddlQuarters" ng-model="formData.quarter">
<option value="">---Please select---</option>
<option ng-repeat="q in quarters">{{q.name}}</option>
</select>
</div>
</div>
<div class="form-group">
<label for="ddlDate" class="col-sm-4 control-label">Purchase Date:</label>
<div class="col-sm-8">
<input class="form-control" id="ddlDate" type="date" ng-model="formData.date" required>
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<button id="removeMore" aria-hidden="false" ng-click="removeForm($index)" ng-show="table.fields.length > 1">Close</button>
<button id="addMore" aria-hidden="true" ng-click="addNewForm()">Add</button>
</div>
</div>
</fieldset>
</form>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<button type="submit" class="btn btn-success" ng-click="submitForm()">Submit</button>
</div>
</div>
</div>
<div class="col-md-4"></div>
</div>
</div>
</body>
App.JS
var app = angular.module('app', []);
app.controller('quarterListCtrl', function($scope, $http) {
$scope.quarters = quarters;
});
app.controller('accountsListCtrl', function($scope, $http) {
$scope.accounts = accounts;
});
app.controller('locationsListCtrl', function($scope, $http) {
$scope.locations = locations;
});
app.controller('formCtrl', function($scope, $http) {
$scope.formData = {};
var formCount = 1;
$scope.table = {
fields: []
};
// create first form on page load
$scope.table.fields.push(formCount);
// adds new field to table array which adds to page
$scope.addNewForm = function() {
formCount++;
$scope.table.fields.push(formCount);
};
// removes field from table array which removes form
$scope.removeForm = function(myIndex) {
if ($scope.table.fields.length > 1) {
$scope.table.fields.splice(myIndex, 1);
formCount;
}
};
});
var accounts = [{
'name': 'Account One',
'acct no': 123456789,
'id': 1
}, {
'name': 'Account Two',
'acct no': 987654321,
'id': 2
}];
var quarters = [{
'name': 'Fall',
'id': 1
}, {
'name': 'Winter',
'id': 2
}, {
'name': 'Spring',
'id': 3
}, {
'name': 'Summer',
'id': 4
}, ];
var locations = [{
'name': 'Here',
'id': 1
}, {
'name': 'There',
'id': 2
}, {
'name': 'Everywhere',
'id': 3
}];
As mentioned already, your formData is reference by all the elements in your array so a change in one element affects all of them.
To make things simpler, you don't need a formCount. Just have an array of formData and make use of angular.copy. So your controller becomes:
app.controller('formCtrl', function ($scope, $http) {
$scope.forms = [];
// create first form on page load
$scope.forms.push({});
// adds new field to table array which adds to page
$scope.addNewForm = function (formToCopy){
$scope.forms.push(angular.copy(formToCopy));
};
// removes field from table array which removes form
$scope.removeForm = function (formToRemove) {
var formIndex = $scope.forms.indexOf(formToRemove);
if (formIndex > -1) {
$scope.forms.splice(formIndex, 1);
}
};
});
See forked plunker for full markup changes but the main points are to change references from table.fields to just forms and pass the formData object to addNewForm and removeForm. So your ng-repeat becomes:
<form class="form-horizontal" name="ExpenseForm" ng-repeat="formData in forms track by $index" novalidate>
You can't use ng-model on a fieldset, so this doesn't do anything:
<fieldset id="innerForm" ng-model="table.fields[$index]">
Also, I would avoid using the id attribute on repeated elements otherwise there will be multiple elements with the same id on the page. You could opt for a dynamic id like id="innerForm-{{$index}}" but it's really not necessary in this case.
All your "ng-model" declarations link to a variable in the object "formData", e.g.
formData.quarter
formData.date
But you do only have one instance of formData on your scope. So there is one object $scope.formData and all duplicates of your form use that very same variable (formData.quarter) hence when you change quarter in your first form the quarter in the second form will change as well since its the very same variable.
What you need to do is to have a formData for every single created form.
For example you can have a variable
$scope.allFormDatas = [];
on your scope and in this array you will push a new instance of your old formData object, e.g.
$scope.addNewForm = function() {
formCount++;
$scope.table.fields.push(formCount);
var newFormData = {};
$scope.allFormDatas.push (newFormData);
};
now you will have to use that very instance of newFormData in your given forms, e.g.
<select class="form-control" name="Quarters" id="ddlQuarters" ng-model="allFormDatas[$index].quarter">
Furthermore you have to remove those formData objects from the array as well when you remove the form itself, of course.

Add dynamic model to newly created elements via directives in angularjs

Following is my PLNKR CODE which is working fine.
Problem - I need to add dynamic scope to these element so that I can grab the contact number + type.
I google the problem before asking but as I am new to directives in angular I am confused with the results, let me know what else I need to add to grab the result.
Following kind of result I am expecting -
contact: [
{number: 56432452, type: "Cell"},
{number: 67895644, type: "Work"},
{number: 78943245, type: "Cell"},
{number: 66793456, type: "Home"},
{number: 90546675, type: "Fax"},
];
Also, I need to use the same form in EDIT mode, let me know what are the extra things that I need to keep in mind while developing this functionality for the edit case.
Following is my directive code -
<div class="form-group">
<label class="col-sm-2 control-label">Contact Number<span class="asterisk">*</span></label>
<div class="col-sm-5">
<input type="text" class="form-control">
</div>
<div class="col-sm-2">
<select class="btn">
<option>Cell</option>
<option>Work</option>
<option>Home</option>
<option>Fax</option>
</select>
</div>
<div class="col-sm-1">
<img src="http://img.informer.com/icons/png/16/3225/3225535.png" class="remCls">
</div>
</div>
As you can see currently the select and input do not contain and ngModel. Let me know how do I introduce this to obtain the above mentioned result.
I'm not sure this is what you need but I think you could define your controller as:
myApp.controller("myCtrl", function($scope){
//Create and array of contacts in your model
$scope.contacts = [];
//Add a new contact to the model
$scope.addContact = function() {
var contacts = $scope.contacts;
contacts[contacts.length] = {};
}
//Remove a contact from the model based on its index
$scope.removeContact = function(index) {
$scope.contacts.splice(index, 1);
}
});
Then on your HTML, you leverage the Angular directives ng-repeat and ng-click:
<body ng-controller="myCtrl">
<button ng-click="addContact()"> Add Contact </button>
<div class="form-group" ng-repeat="contact in contacts">
<label>Contact Number</label>
<input type="text" ng-model="contact.contact">
<select ng-model="contact.type">
<option>Cell</option>
<option>Work</option>
<option>Home</option>
<option>Fax</option>
</select>
<button ng-click="removeContact($index)"> Remove Contact </button>
</div> <!-- Close Repeater -->
</body>
Here's your PLNKR link with the changes proposed:
http://plnkr.co/edit/VWCdXSnOsY18XoCKxO0t?p=preview
First of all I would like to thank ExpertSystem for suggesting me to think in Angular way. Then I would like to thank Foxandxss and medice from angular IRC for making the things right not by code but improving my concept and approach for angular.
This is the WORKING code, I came up with for the above problem.
Actually I don't need directive and managed things easily without it.
medice: directives are fine, but when you set up click events that
modify dom, it's gonna break
medice: angularjs can't bind directives properly
Following is my controller code -
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope){
$scope.cnctnum = [];
$scope.cncttype = [];
$scope.types = [
{name: "Cell", value: 1},
{name: "Work", value: 2},
{name: "Home", value: 3},
{name: "Fax", value: 4}
];
$scope.items = [];
var i =0;
$scope.addCnt = function(){
$scope.items.push(i);
i++;
};
$scope.remCl = function(index){
$scope.cnctnum.splice(index, 1);
$scope.cncttype.splice(index, 1);
$scope.items.splice(index, 1);
};
$scope.getval = function(){
console.log($scope.cnctnum);
console.log($scope.cncttype);
};
});

Angular ng-class performance issue when too many elements in DOM

I have been working on a complex angular page which has been causing performance issue. To highlight the problem I have created a fiddle http://jsfiddle.net/4ex2xgL1/3/ here.
Essentially the performance issue is being caused by ng-class statement which has a function in it.
<span class="done-{{todo.done}}" ng-class="myfunction()">{{todo.text}}</span>
The span is in an ng-repeat. On running the fiddle one can see that ng-class gets executed several times when the page loads and on each key up it gets called as many time as number of items in the TODO list.
This is a lot simpler case, in my case I have 780 items on my page and the function ends up being evaluated aroung 3000 times!
One of the solution we saw is to break up the scope but it will cause almost a rewrite of my app.
We also tried https://github.com/Pasvaz/bindonce but it doesn't seem to be working with highly dynamic content.
Any thoughts?
I built a tree with https://github.com/JimLiu/angular-ui-tree with almost 500 items to render, with quite a lot of listeners. It takes 5 seconds to render. Bindonce won't work there.
The only solution out there is make ng-repeat do less. Keep the list small with a pagination, search or anything. Its the best shot as far as I know.
Well here are my recommendations
use ng-change on the checkbox to manipulate dom or anything rather using ng-class, it will improve your performance drastically.
<li ng-repeat="todo in todos track by todo.id">
<input type="checkbox" ng-model="todo.done" ng-change="myfunction()">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
http://jsfiddle.net/4ex2xgL1/3/
use track by in ng-repeat if you have ids, more here http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/
dont show 780 items in a list. Use a searchbox to show some 100 or 50 or you know better
quick-ng-repeat not used yet, try testing it https://github.com/allaud/quick-ng-repeat
finally a few good http://tech.small-improvements.com/2013/09/10/angularjs-performance-with-large-lists/
Finally I found the solution and it will helps lot to improve performance in angular js.
If your model changes dynamically and if you have lots of data and then also it improve AngularJS pages rendering up to 1000% and more - no kidding !.
Fore more information you can visit : http://orangevolt.blogspot.in/2013/08/superspeed-your-angularjs-apps.html
Follow the steps:
download the library from the link:library
2.example without library:(check your console)
function MyController( $scope) {
var entries = [
{ label : 'one', value : 'first entry'},
{ label : 'two', value : 'second entry'},
{ label : 'three', value : 'third entry'}
];
$scope.label ="";
$scope.value ="";
$scope.order = 'label';
$scope.add = function() {
entries.push({
label : $scope.label,
value : $scope.value
});
};
$scope.getEntries = function() {
console && console.log( "getEntries() called");
return entries;
};
}
<script src="https://raw.githubusercontent.com/lodash/lodash/2.4.1/dist/lodash.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<form name="myform" ng-app ng-controller="MyController">
Label/Value :
<input type="text" required ng-model="label">
<input type="text" required ng-model="value">
<button
ng-disabled="!myform.$valid"
ng-click="add()"
>Add</button>
<fieldset>
<legend>
Entries sorted by
<select
ng-model="order"
ng-options="property for property in [ 'label', 'value']">
</select>
</legend>
<div ng-repeat="entry in getEntries() | orderBy:order">
{{entry.label}} = "{{entry.value}}"
</div>
</fieldset>
</form>
3.example with library:(check your console)
function MyController( $scope) {
var entries = [
{ label : 'one', value : 'first entry'},
{ label : 'two', value : 'second entry'},
{ label : 'three', value : 'third entry'}
];
$scope.label ="";
$scope.value ="";
$scope.order = 'label';
$scope.add = function() {
entries.push({
label : $scope.label,
value : $scope.value
});
// clear cache
$scope.getEntries.cache = {};
};
$scope.getEntries = _.memoize(
function() {
console && console.log( "getEntries() sorted by '" + $scope.order + " 'called");
// return entries sorted by value of $scope.order
return _.sortBy( entries, $scope.order);
},
function() {
// return the cache key for the current result to store
return $scope.order;
}
);
}
<script src="https://raw.githubusercontent.com/lodash/lodash/2.4.1/dist/lodash.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.20/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<form name="myform" ng-app ng-controller="MyController">
Label/Value :
<input type="text" required ng-model="label">
<input type="text" required ng-model="value">
<button
ng-disabled="!myform.$valid"
ng-click="add()"
>Add</button>
<fieldset>
<legend>
Entries sorted by
<select
ng-model="order"
ng-options="property for property in [ 'label', 'value']">
</select>
</legend>
<div ng-repeat="entry in getEntries()">
{{entry.label}} = "{{entry.value}}"
</div>
</fieldset>
</form>

How to get selected checkboxes on button click in angularjs

I want to do something like this
<input type="checkbox" ng-model="first" ng-click="chkSelect()"/><label>First</label>
<input type="checkbox" ng-model="second" ng-click="chkSelect()"/><label>Second</label>
<input type="checkbox" ng-model="third" ng-click="chkSelect()"/><label>Third</label>
<input type="checkbox" ng-model="forth" ng-click="chkSelect()"/><label>Forth</label>
<button>Selected</button>
On button click I want to display selected checkbox labelname.
$scope.chkSelect = function (value) {
console.log(value);
};
Because the checkboxes are mapped, you can reference $scope.first, $scope.second, etc in your chkSelect() function. It's also possible to have a set of checkboxes mapped as a single array of data instead of having to give each checkbox a name. This is handy if you are generating the checkboxes, perhaps from a set of data.
I agree with Bublebee Mans solution. You've left out a lot of detail on why you're trying to get the label. In any case if you REALLY want to get it you can do this:
$scope.chkSelect = function (value) {
for(var key in $scope){
var inputs = document.querySelectorAll("input[ng-model='" + key + "']");
if(inputs.length){
var selectedInput = inputs[0];
var label = selectedInput.nextSibling;
console.log(label.innerHTML);
}
};
};
You can mess around with it to see if it's indeed selected.
fiddle: http://jsfiddle.net/pzz6s/
Side note, for anybody who knows angular please forgive me.
If you are dealing with server data, you might need isolated html block and deal with data in controller only.
You can do it by creating array in controller, maybe your data from response, and use ngRepeat directive to deal independently in html code.
Here is what I am telling you:
HTML:
<form ng-controller="MyCtrl">
<label ng-repeat="name in names" for="{{name}}">
{{name}}
<input type="checkbox"
ng-model="my[name]"
id="{{name}}"
name="favorite" />
</label>
<div>You chose <label ng-repeat="(key, value) in my">
<span ng-show="value == true">{{key}}<span>
</label>
</div>
</form>
Javascript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.names = ['pizza', 'unicorns', 'robots'];
$scope.my = { };
}
You want to have something like the following in your controller (untested, working from memory):
$scope.checkBoxModels = [ { name: 'first', checked: false }, { name: 'second', checked: false }, { name: 'third', checked: false }, { name: 'fourth', checked: false } ];
Then in your view:
<input ng-repeat"checkboxModel in CheckBoxModels" ng-model="checkBoxModel.checked" ng-click="chkSelect(checkBoxModel)" /><label>{{checkBoxModel.name}}</label>
Then update your function:
$scope.chkSelect = function (checkBoxModel) {
console.log(checkBoxModel.name);
};

Categories

Resources