ng-change event firing only once inside ng-repeat - javascript

Here in my form, I am using two radio buttons and would like to check the selected values. But this does not work as expected. I would like to determine the individual radio selection value and display Big/Small in the Result column
<div ng-controller="CityCtrl">
<table>
<tr>
<th>Name</th>
<th>Big</th>
<th>Small</th>
<th>Result</th>
</tr>
<tr ng-repeat="city in cities">
<td>{{ city.name }}</td>
<td>
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="big" value="big_value" ng-change="check(city.name, big)">
</td>
<td>
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="small" value="small_value" ng-change="check(city.name, small)">
</td>
<td>***</td>
</tr>
</table>
</div>
Controller:
var myApp = angular.module('cityApp', []);
function CityCtrl($scope) {
$scope.big = '';
$scope.small = '';
$scope.cities = [{
id: 1,
name: 'Tokyo'
}, {
id: 2,
name: 'Guangzhou'
}, {
id: 3,
name: 'Seoul'
}];
$scope.check = function(city, value) {
console.log("City->" + city + " ::: Value->" + value);
};
}
From other answers I did infer a few things:
Understand Prototypical Inheritance in JS ?!
ng-repeat creates child scope that affects these radios inside ng-repeat
use $parent on ng-modal in the radio
I did played around, but couldn't make this work, I guess I am making some obvious mistake here.
Here is the fiddle:
http://jsfiddle.net/mw2us37h/
NOTE:
I am not using ng-value but passing a hardcoded value.
Changing to ng-click doesnt work either

Flagged this as a duplicate, but it seems like the solution is to use ng-click instead of ng-change when working with radio buttons in angularjs.
Here is an edit of your fiddle that works: http://jsfiddle.net/mw2us37h/1/

I'm not sure I understand what your trying. I think you like to display the radio button value in the result column and/or have a function on click where the value is passed.
Display the radio button value in result column
I would change the ngModel value so it binds to something city specific. So change:
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="big" value="big_value">
to:
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="city.size" value="big_value">
and
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="small" value="small_value">
to
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="city.size" value="small_value">
So the model points to the same variable city.size, this is where the selected value will be saved. Then change *** in the result column to city.size
See changes fiddle: http://jsfiddle.net/u2kxuyr7/
Get value on ngClick
Use ngChange as you allready do I would do it a bit different for simplicity. Change:
ng-change="check(city.name, whatEverValue)"
to:
ng-change="check(city)"
and the check function would look like:
$scope.check = function(city) {
console.log("City->" + city.name + " ::: Value->" + city.size);
};
See fiddle: http://jsfiddle.net/Lmszxj8s/
Then ngChange is fired on each change.

Related

Checkbox Unchecking after AngularJS sorting

I currently have a table listing jobs. Each job has a checkbox.
<form name="jobsSampleSelectForm" id="jobs-sample-select-form">
<div jobs="data">
<div ng-form="jobsSampleSelectForm">
<table ng-table="tableParams" class="table" show-filter="true">
<tr ng-repeat="job in $data" class="table-highlight" ng-class="{deactivated: job.is_active == 'No'}">
<td data-title="'Sample'"><input type="checkbox" ng-model="job.selected"></td>
<td data-title="'QC ID'" sortable="'id'">{{job.id}}</td>
<td data-title="'Date Submitted'" sortable="'dateJob'">{{job.dateJob}}</td>
</tr>
</table>
</div></div>
<button type="submit" class="btn btn-primary-red" ng-click="sampleJobs()" ng-disabled="(jobs | filter: job.selected != true).length <= 0">Sample Jobs</button>
</form>
This allows the user to select the jobs they want. However, when the user sorts a column the checkbox will uncheck itself. This seems to be a common issue, so from the advice from this SO question I added a selected attribute to each job in my controller.
jobsService.getJobsByStatuses(["pre_sampling", "sampling", "setup"])
.success(function(data){
data.forEach(function(job){ job.selected = false; });
$scope.jobs = data;
var filteredData = params.filter() ? $filter('filter')(data, params.filter()) : data;
var orderedData = params.sorting() ? $filter('orderBy')(filteredData, params.orderBy()) : data;
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
params.total(orderedData.length);
})
This did not seem to cause a difference. What am I missing?
I created a Fiddle to show the basic idea. When the user selects checkboxes, then sorts, the checkboxes become unchecked.
just change
<td data-title="'Sample'"><input type="checkbox" ng-model="job.selected"></td>
to
<td data-title="'Sample'"><input type="checkbox" ng-model="user.selected"></td>
If you want to persist the row selection then you should bind the checkbox to user.selected instead of binding to job.selected.
If you use user.selected for ng-model each row will have its own user object with selected property binded to checkbox so that you will get the selection even after the sorting
see the updated fiddle
<input type="checkbox" ng-model="user.selected">
Working fiddle http://jsfiddle.net/PQvQ2/83/
You are not capturing the checkbox value anywhere. In the user object, you already have "selected" to capture the selection. Using the same will store the selection. If you are using a different object "job", you will capture the index of selection but it will not be mapped to the correct value. Since, "job" object only store the checkbox selected, nothing more.
The issue was that I was calling getData every time a filter was applied. This was setting my selected attribute on each job to false. To fix this issue, I moved my code to get the data outside of the getData function.

will angular update one `ng-model` inside a <tfoot>

I'm doing angular table filter. My first attempt is to try to include it on the header. The icon show on the wrong position and when I focus the text box the sorting change.
So I move the box to the <tfoot> but it looks like it is somehow disabled because even when I change the content on text box the ng-model="filter_id" it doesnt change.
The outside text box, works perfect.
In the picture:
press 1 on <tfoot input text>: the textbox change to 1, event updateFilteredList trigger but filter_id is empty, neither <p input text> nor {{filter_id}} change.
press 1 again on <tfoot input text>: the textbox change to 11, same behaviour
press 1 on <p input text>: the textbox change to 1, {{filter_id}} update to 1 and updateFilteredList also receive filter_id=1 (even when filter for 1 return the same list);
EDIT
I found out when app start if I type on the external text box the interal get updated. But when I type a single char on the internal one, stop the updates.
<div ng-controller="eventCtrl">
<table class="table table-striped" at-table at-list="filteredList"
at-config="config" at-paginated>
<thead></thead>
<tfoot>
<tr>
<th><input type="text" ng-change="updateFilteredList()"
ng-model="filter_id" style="width: 50px" />
</th>
</tr>
</tfoot>
<tbody></tbody>
</table>
<at-pagination at-config="config" at-list="filteredList"></at-pagination>
<p>
<input type="text" ng-change="updateFilteredList()"
ng-model="filter_id" />
{{filter_id}}
</p>
</div>
I'm using this plug-in for my table. Is this a bug or maybe is some behaviour of the plugin? Is there a way to detect if plug-in overwrite some events?
Controller:
app5.controller('eventCtrl', ["$scope", "$filter", "$http" , function ($scope, $filter, $http) {
$scope.cars = [
{ Car_ID: 1, X: null, Y: null, RoadName: null, Azimuth: null, DateTime: null, Adress: null }
];
$scope.filteredList = $scope.cars;
$scope.filter_id = "";
$scope.updateFilteredList = function () {
console.log('filter_id: ' + $scope.filter_id);
$scope.filteredList = $filter("filter")($scope.cars, $scope.filter_id);
console.log('filteredList.length:' + $scope.filteredList.length);
};
Probably there is a better way, but this is working. Because the event was already triggering, I just get the value from the DOM before run the filter
$scope.filter_id = $('#filter_id').val();
Function
$scope.updateFilteredList = function () {
$scope.filter_id = $('#filter_id').val();
console.log('filter_id: ' + $scope.filter_id);
$scope.filteredList = $filter("filter")($scope.cars, $scope.filter_id);
console.log('filteredList.length:' + $scope.filteredList.length);
};

Parsing JSON result to object by using checkbox

I have loaded a JSON list into a table and I would like to parse 1 JSON result or multiple results into an object, so I can send it to the server.
My table looks like this so far:
HTML
<tr ng-repeat="t in student">
<td ng-model="herkanserNaam">{{ t.Name }}</td>
<td>{{ t.City }}</td>
<td>
<div class="checkbox" ng-click="laatzien(herkanserNaam, herkanserCheck)" ng-model="herkanserCheck">
<label>
<input type="checkbox">
</label>
</div>
</td>
</tr>
Controller
$scope.laatzien = function(name, active) {
var herkanser = [{
"name" : name,
"active" : false
}];
console.log(herkanser);
}
How would I be able to check one or multiple results and save the data(t.Name) into an object by using a checkbox? So far the function laatzien() is returning the empty values defined in herkanser.
The reason your laatzien method is failing is due to how you are using your directives. Let's work with the example you provided to get your laatzien method to fire.
HTML
<tr ng-repeat="student in students">
<td>{{ student.Name }}</td>
<td>{{ student.City }}</td>
<td>van</td>
<td>Huis</td>
<td>j.huis#student.han.nl</td>
<td>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="student.isActive" ng-change="laatzien(student)">
</label>
</div>
</td>
</tr>
Javascript
$scope.laatzien = function (student) {
var herkanser = [{
"name": student.name,
"active": student.isActive
}];
console.log(herkanser);
}
I have made some opinionated changes in your example for readability purposes, others were needed to get the directives to fire as expected. Below are the changes to your snippets.
Renamed the student array to students. This will require a change in your controller from $scope.student to $scope.students.
Renamed the t object to student.
Removed the ng-click directive from your div.
Added an ng-change directive on your checkbox. Now when you click the checkbox your laatzien method should fire.
Added an isActive property to your student. Inside of your laatzien method, you may now check the state of the checkbox. If the checkbox is checked, student.isActive = true. If the checkbox is not checked, student.isActive = false.
From your code, you seem to want to build the "list of checked students" and send that to the server. In other words, what you want, is to allow the user to check on multiple students and at the end collect everything that was checked and send it over to the server.
If that's the case then your strategy to put an ng-click over the checkbox is wrong.
What you need is to bind your checkbox to your $scope model. Such as this:
<input type="checkbox" ng-model="t.isChecked" ng-true-value="true" ng-false-value="false'">
When the user checks the checkbox for a student. Your model will automatically be updated.
To collect the data to send over the server you need to put an ng-click on a submit button. In the event handler, simply loop through every student in your $scope "students" model and only save the ones that have isChecked property to true to be sent over to the server.
Hope this helps!
You could make a function to push thet item into an obj like so...
$scope.students = [
{
"name":"John",
"city":"Boston"
},
{
"name":"Amy",
"city":"Dallas"
}
]
$scope.activeObj = [];
$scope.laatzien = function(obj) {
if($.inArray(obj, $scope.activeObj) == -1) {
$scope.activeObj.push(obj);
} else {
var index = $scope.activeObj.indexOf(obj);
$scope.activeObj.splice(index, 1);
}
}
http://jsfiddle.net/5fcnazb2/

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

knockout checked binding - only one checked

I have a list of Admins with a check box. I want to be able to select only one Admin.
HTML:
<tbody data-bind="foreach: people">
<tr>
<td>
<input type="checkbox" data-bind="attr: { value: id }, checked: $root.selectedAdmin">
<span data-bind="text: name"/>
</td>
</tr>
</tbody
JS:
function Admin(id, name) {
this.id = id;
this.name = name;
}
var listOfAdmin = [
new Admin(10, 'Wendy'),
new Admin(20, 'Rishi'),
new Admin(30, 'Christian')];
var viewModel = {
people: ko.observableArray(listOfAdmin),
selectedAdmin: ko.observableArray()
};
ko.applyBindings(viewModel);
For Example if Admin id 10 is selected the other admins should be deselected.
Is that Possible to do with Knockout?
You should really use radio buttons if you only want to allow multiple selection.
However if you still want to use checkboxes then on solution would be to combine the checked and the click binding:
Use the checked to check only when the current id equal to the selectedAdmin property and use the click binding to set the selectedAdmin.
So you HTML should look like this:
<input type="checkbox" data-bind="attr: { value: id },
checked: $root.selectedAdmin() == id,
click: $parent.select.bind($parent)" />
And in your view model you just need to implement the select function:
var viewModel = {
people: ko.observableArray(listOfAdmin),
selectedAdmin: ko.observableArray(),
select: function(data) {
this.selectedAdmin(data.id);
return true;
}
};
Demo JSFiddle.
Notes:
the return true; at the end of the select function. This is required to trigger the browser default behavior in this case to check the checkbox.
the .bind($parent) is needed to set the this in the select function to be the "parent" viewModel object.

Categories

Resources