I have a table that sets out a list of rules. When the checkboxes are clicked, I need them to take this "true" value and post to an API endpoint. This has been set up, but what I am getting back is that "associated_rule" is undefined.
I have tried setting $scope.associated_rule.selected = true; in my controller, but this still doesn't define the variable and throws up the same error in the console.
Here is my HTML form:
<form name="rules_form" method="post" ng-submit="attach()">
<table class="table table-striped table-hover" ng-model="associated_rules">
<thead>
<th>Rule Types:</th>
<th>Description:</th>
<th>Start Time:</th>
<th>End Time:</th>
<th>Apply Rule to Vehicle:</th>
</thead>
<tr ng-repeat="associated_rule in associated_rules">
<td>#{{ associated_rule.resource_ids.accounts }}</td>
<td>#{{ associated_rule.description }}</td>
<td>#{{ associated_rule.start_time }}</td>
<td>#{{ associated_rule.end_time }}</td>
<td><input type="checkbox" ng-model="associated_rule.selected" aria-label="rule"></td>
</tr>
</table>
<button class="btn btn-primary" ng-click="attach()">Attach</button>
</form>
My Controller event:
$scope.attach = function () {
$scope.associated_rule.selected = true;
var i;
for (i = 0; i < $scope.associated_rule.selected.length; i++) {
//need to create a loop where the true value is picked up and then I can send the data using a POST method. But I'm stuck on this.
}
console.log(the result of the event);
};
For now, I just want the results to console.log, so I can see that the event is creating the loop and displaying the results. After that, I should be OK.
Any help would be much appreciated.
I have fixed this by defining the $scope.rule as an empty array and setting the $scope.rule.selected to "false" by default.
Great! step one! But the checkboxes are ALL selecting when you click A checkbox - think this may be the ng-repeat causing me a pain in the backside.
$scope.rule = [];
$scope.rule.selected = false;
So, how do I ensure that only the checkboxes set that I select and not all at once?
Fixed this too; as the above was just making the entire array selected; as i wasn't drilling down into the array. This did it:
ng-model="rules.selected[associated_rule.id]"
by modelling the a rule within that defined array, it shows up when testing. Brill. :)
By mistake you are changing the value of your check box on clicking the button:
$scope.associated_rule.selected = true;
This will give the current value, selected or not selected
$log.log($scope.associated_rule.selected);
Related
I have a Django application and a page where data is written to a table that I am styling using DataTables. I have a very simple problem that has proven remarkably complicated to figure out. I have a dropdown filter where users can select an option, click filter, and then an ajax request updates the html of the table without reloading the page. Problem is, this does not update the DataTable.
My html:
<table class="table" id="results-table">
<thead>
<tr>
<th scope="col">COL 1</th>
<th scope="col">COL 2</th>
<th scope="col">COL 3</th>
<th scope="col">COL 4</th>
<th scope="col">COL 5</th>
</tr>
</thead>
<tbody class="table_body">
{% include 'results/results_table.html' %}
</tbody>
</table>
results_table.html:
{% for result in result_set %}
<tr class="result-row">
<td>{{ result.col1 }}</td>
<td>{{ result.col2 }}</td>
<td>{{ result.col3 }}</td>
<td>{{ result.col4 }}</td>
<td>{{ result.col5 }}</td>
</tr>
{% endfor %}
javascript:
function filter_results() {
var IDs = [];
var IDSet = $('.id-select');
for (var i = 0; i < IDSet.length; i++) {
var ID = getID($(IDSet[i]));
IDs.push(ID);
}
// var data = [];
// data = $.ajax({
// url:"filter_results/" + IDs + "/",
// dataType: "json",
// async: false,
// cache: false,
// data: {},
// success: function(response) {
// $('#results-table').html(response);
// // console.log(response.length);
// // console.log(typeof response);
// //
// }
// }).responseJSON;
var dataTable = $('#results-table').DataTable();
dataTable.clear();
$('.table_body').html('').load("filter_results/" + IDs + "/", function() {
alert("Done");
});
dataTable.draw();
}
And views:
def filter_results(request, ids):
ids = [int(id) for id in ids.split(',')]
account_set = Account.objects.filter(id__in=ids)
form = ResultsFilterForm()
result_set = Result.objects.filter(account__in=account_set)
context = {
'form': form,
'result_set': result_set
}
return render(request, 'results/results_table.html', context)
What is happening is that the Ajax is correctly updating what I see on the HTML page, but it is not updating the actual data table. So I can filter for a particular ID, for instance, which has 2 results and this will work and show me the two results on the HTML page without reloading it. However, the DataTable still contains the rest of the results so there is still like a "next" page which makes no sense when there are only 2 results.
I also tried changing the view to return a JSON response with the code that is commented out of the JS and when I did that I got "Warning: DataTable encountered unexpected parameter '0' at row 0 column 0" even though the data coming from Django was the correct data in JSON form.
Really stuck here, appreciate the help.
I figured out a way to make it work. Though maybe not the "best" way to do this, it is simple so I hope this helps someone else. Change JavaScript as follows:
function filter_results() {
$('#results-table').DataTable().destroy();
var IDs = [];
var IDSet = $('.id-select');
for (var i = 0; i < IDSet.length; i++) {
var ID = getID($(IDSet[i]));
IDs.push(ID);
}
$('.table_body').html('').load("filter_results/" + IDs + "/", function() {
$('#results-table').DataTable();
});
}
That was it. All I needed to do was destroy the old DataTable at the beginning of the filter_results function and then re-create it. Note however that the recreation is the function to execute after the call to .load(). If you don't write it like this you will have an issue with JS recreating the DataTable before the the html is finished loading, which is a problem I encountered. Nesting that function inside the .load() call is an easy workaround though.
I know there is probably a better way to do this, one that involves updating rather than destroying and recreating the DataTable. If somebody knows of it feel free to post it as an answer but for now this workaround is good for me!
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.
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/
I have following lines of code in my AngularJS project.
<div ng-show="update">
<table border="1">
<tr ng-repeat="x in names">
<td>{{ x.uname}}</td>
<td>{{ x.upass}}</td>
<td><button ng-model="index" ng-click="show()"
ng-value="{{x.index}}">Edit</button></td>
</tr>
</table>
When Particular button is clicked I want to retrieve that button label.
For that I have written code in Show function:
var v = $scope.index;
alert(v);
But alert box is displaying "Undefined" when i click on button.
Please suggest me where I am wrong??
Thanks in advance
What about providing your value as an argument to the onclick function ?
<div ng-show="update">
<table border="1">
<tr ng-repeat="x in names">
<td>{{ x.uname}}</td>
<td>{{ x.upass}}</td>
<td><button ng-click="show(x.index)">Edit</button></td>
</tr>
</table>
and then
function show(id){
alert(id);
}
This should works. Also ng-model does not work on button.
under ng-click = show(item) - this gives the particular item object.
function show(obj){ console.log(obj)}
this function give the particular clicked obj from view.
ng-model doesn't work on button.
You can pass the index of the repeat iteration to the show method like this:
<button ng-click="show($index)" ...
and use it like this:
function show(i){
var v = names[i];
alert(v);
}
You can see it in action here: PLNKR DEMO
NOTE: if you are passing the index to the backend service the index may not match up with the actual array if you are using a filter in your view to alter/reduce the ng-repeat expression
Another option as pointed out by #Grundy is to just pass the object back, which saves you the step of having to access it from the array... (note: this is the suggested best practice)
I am assuming that you want the button label that you have clicked on and the index of it in the list provided. Here is what you can do.
<div ng-repeat="buttonId in testdata2">
<button ng-bind="buttonId" ng-click="clickedMe(buttonId, $index)"></button>
</div>
$scope.clickedMe = function(buttonId, index) {
console.log(buttonId + " - " + index);
};
Have your files in local and run them.
Simple html:
<table class="table table-condensed">
<tr data-ng-repeat="customer in customers" data-ng-class="customerSelectedClass(customer)">
<td>
{{customer.Name}}
</td>
</tr>
</table>
In my controller - two functions to select customer and return proper class to highlight a table row:
$scope.customerSelectedClass = function (customer) {
if (customer == $scope.selectedCustomer) {
console.log('returing info for ' + customer.Name);
return "info";
}
return "";
};
$scope.selectCustomer = function (customer) {
console.log('selecting ' + customer.Name);
$scope.selectedCustomer = customer;
}
I noticed that when I click on a customer link, customerSelectedClass function executes twice. selectCustomer function on ng-click directive executes once, as it should. Angular is only included once on the page. I wonder if this is a bug in Angular or something that I am doing wrong?
Behind the scenes, angular is setting up a $watch on the function that is resolving the class name. Because angular uses dirty checking to see if there has been a change, this method will be called twice during the $digest cycle. This is ok.
I would suggest that you don't add this code the the controller though, because if you are managing many css classes, you could be adding a lot of unnecessary code. Try something like this instead:
<table class="table table-condensed">
<tr data-ng-repeat="customer in customers" data-ng-class="{'info': customer == selectedCustomer}">
<td>
{{customer.Name}}
</td>
</tr>
</table>
Then, there is no need for a controller function customerSelectedClass. This will only add the info class if the right-hand side of the : resolves to true. And there is no problem resolving the correct customer in the ng-repeat.
Hope this helps.