I have an angular app in this plunkr
Here the button opens a modal which has a table. When an item in the table is checked or unchecked, it changes the values in the progress bar accordingly.
Below the table, it has a list of all the checked courses shown. How do i bind this data accordingly such that when a checkbox is checked, it also shows the item in this list dynamically? Here is the code i used for displaying this list of items:
<div ng-repeat="child in selectedCourses">
{{child.course.subject}}-{{child.course.course_no}}
</div>
The problem is that you push the same items many times into $scope.selectedCourses, which results into an error (because of duplicate values in ngRepeated items) which in turn causes the view to not be updated.
To prevent adding the same item many time, you should check if it is already present in the array:
$scop.checkplanneddetails = function (course) {
...
for (var k = 0; k < $scope.planneddetails.length; k++) {
if ($scope.requirementcoursename==$scope.planneddetails[k].course_name) {
if (!$scope.checkcoursefunction($scope.selectedCourses, course)) {
// ONLY add the course if it is NOT already present
$scope.selectedCourses.push(course);
}
course.checked = true;
return true;
}
}
return false;
};
There is also a problem with using ng-checked="checkplanneddetails(child), because checkplanneddetails is not idempotent. You should not use it with ngChecked, because it is run every time a $digest cycle takes place.
You should ngInit instead and also make sure you add course.checked = true at the proper place inside checkplanneddetails (see above):
<!-- Change that: -->
<input type="checkbox" ng-checked="checkplanneddetails(child)" ... />
<!-- To this: -->
<input type="checkbox" ng-init="checkplanneddetails(child)" ... />
See, also, this short demo.
Related
I am still new to AngularJS, I am trying to have a simple function that will toggle a checkbox on/off when clicked. So a clicked li element in the code below will either set that checkbox to on or off.
Can anyone suggest the best way to do this using AngularJS, I know jQuery would be simple but i'm trying to do this in the Angular way.
my html template
<ul>
<li ng-repeat="page in rule.pages" ng-click="toggleNetwork(page.id); ruleForm.$setDirty()">
<span class="pull-left"><i class="check material-icons nomargin">{{rule.pages.indexOf(page.id) >= 0 ? 'check_box' : 'check_box_outline_blank'}}</i></span>
</li>
</ul>
my Controller scope logic code
$scope.toggleNetwork = function(networkId) {
// function called when checkbox clicked
}
IF you just want the checkbox to toggle on/off, you don't really need to do anything. Angular will do it for you out of the box.
you only need a boolean variable in your controller, like this:
//myController.js
$scope.myCheckboxValue = false;
And your html should be something like this:
//myTemplate.html
<input type="checkbox" ng-model="myCheckboxValue">
Whenever you click the checkbox, the changes will already be reflected on myCheckboxValue.
I don't see checkbox in your html so I'm assuming that you want to li tag working as checkbox.
You can do it that way, if page can have additional property:
$scope.toggleNetwork = function(network) {
network.toggle = !network.toggle;
}
If you don't want to add property to network model you can store toggled networks in array;
$scope.toggledNetworks = [];
$scope.toggleNetwork = function(networkId) {
var index = $scope.toggledNetworks.indexOf(networkId);
if (index === -1)
$scope.toggledNetworks.splice(index, 1);
else
$scope.toggledNetworks.push(networkId)
}
Then if you want to check if network is toggled you can use function:
$scope.isToggled = function(networkId) {
return $scope.toggledNetworks.indexOf(networkId) !== -1;
}
Note:
rule.pages.indexOf(page.id)
Will always return -1. Pages contains objects, you want index of property of one object which is not in this array
I have a Kendo grid that has Child elements as shown in the Image below. Is there a way to read the elements that are checked.
It depends on when you want to find out what checkboxes are selected, but essentially it will work this way.
You simply add a listener to a button or a common class among the checkboxes that looks at the checkboxes and returns the checked ones.
The example from Kendo: http://dojo.telerik.com/UhANu
Specifically,
$("#showSelection").on("click", function () {
var checked = [];
for(var i in checkedIds){
if(checkedIds[i]){
checked.push(i);
}
}
alert(checked);
});
I've changed the above to a .on() instead of .bind because it's what I'm more familiar with being the idiomatic way of doing listeners, but both technically work.
If you'd rather have value of the checkboxes save each time you change them it'd be something like this:
$(".checkbox").on("click", function () {
var checked = [];
for(var i in checkedIds){
if(checkedIds[i]){
checked.push(i);
}
}
$('#checked-boxes').val(checked);
});
and in your html create an element that holds the values:
<label for="checked-boxes">Checkboxes that have been selected:</label>
<input type="text" id="checked-boxes" name="checked-boxes">
I am using AngularJS to create a page which contains a list of products that shows information such as a name, price, region etc. This is displayed kind of like an accordion with the name in the header and extra information in the body.
Since there could be a large amount of these items displayed I am using dirPagination (https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) to paginate these items. My markup at the moment looks like this:
<div class="custom-list" dir-paginate="asset in assets | itemsPerPage: 10 track by $index">
<div class="custom-list-item" ng-class="{'tag-hover': isHoveredTag(asset)}">
<div class="custom-list-item-heading" ng-click="toggleShowAssetDetails(asset)">
<p class="col-lg-5">{{ asset.name }}</p>
<div class="col-lg-offset-2 col-lg-3 rating">
<rating value="asset.rating" />
</div>
<button ng-click="addAsset(asset)"><span class="glyphicon glyphicon-plus"></span></button>
<div class="clearfix"></div>
</div>
<div class="custom-list-item-content" style="display: none" animate="shouldShowAssetDetails(asset)">
...
</div>
</div>
</div>
As you can see I'm using paginate in a pretty standard way just looping through the items in an array and displaying 10 per page. I also have a directive called rating which looks at a value called rating in the item. This is a number from 1 - 5 which is used to display a star rating system next to the name. The directive looks like this:
var rating = function ($compile) {
return {
restrict: "E",
scope: {
value: "="
},
link: function (scope, element, attrs) {
scope.$watch(attrs.rating, function () {
for (var i = 1; i <= 5; i++) {
if (i <= scope.value) {
var starElement = angular.element("<span class=\"icon icon-crown\"></span>");
$compile(starElement)(scope);
element.append(starElement);
} else {
var emptyStarElement = angular.element("<span class=\"icon-empty icon-crown\"></span>");
$compile(emptyStarElement)(scope);
element.append(emptyStarElement);
}
}
})
}
}
}
This looks at the value and inserts the icons based on the value of rating (e.g if the rating was 2 the directive would insert two icon-crown icon spans and 3 icon-empty icon-crown icon spans.
This was working perfectly fine before I included the pagination. However now it will only work for the first 10 items. When you change the page, the rating will not change and just keep the same icons from the previous page, even if the values are different.
I understand this is because the directive sets everything at the beginning and it will not run when you change page because the items aren't reloading they are just being shown and hidden again. But the directive is manipulating the DOM so it doesn't update when the page changes.
The problem is I don't know how to resolve this. I thought about changing the directive to look for the pagination current page instead but then I don't have access to the current list item.
I'd appreciate any help on getting the directive to update the icons when the page is changed.
Update
Here's a link to a Plunker project showing the problem I'm having: http://plnkr.co/edit/VSQ20eWCwVpaCoS7SeQq?p=preview
This is a very stripped down version of the section on my app that I'm having an issue with. There's no styling included although I have kept the CSS class structure. I've also changed the icons to use bootstrap ones just to simplify the Plunker project.
The functionality is the same however. If you go from page 1 to page 2 notice how the stars remain the same despite that fact that the asset rating values are different. However if you go to page 3 and back to page 2 or 1 they will change. The reason this happens is because there are less items on page 3 and therefore when you go back to page 1 or 2 the remaining items will be called again to retrieve the rating values.
You simply need to remove or replace track by $index.
Tracking by $index will give you the following behavior:
There is an array of max 10 length that represents the items to show. The first item will have index 0.
You go to the next page and the items in the array are replaced.
The first item in the array will still have index 0. Since you are tracking by index and the index has not changed, AngularJS will not recreate the DOM node representing the item. Since the DOM node isn't recreated, the directive will not execute this time and the rating will not update.
If you go from page 3 to page 2, the directive will execute for the 7 last elements on page 2, since they didn't exist on page 3, so they are considered new this time.
If you really need to use track by you should use a property that is actually related to the object (and unique), for example:
dir-paginate="asset in assets | itemsPerPage: 10 track by asset.name"
Demo: http://plnkr.co/edit/A80tSEliUkG5idBmGe3B?p=preview
I have a page with a list of items on it. Each row has a button. By pressing a button list item is added to another list on the same page (it's a typical "order" form). I'm using angular ng-repeater to show the first list. After user press a button an item info is added to JSON varaible. The question is what's the best way to show user's choice list on the same page? So far I'm think of adding an attribute to the first list so when user choose it, it'll be shown in the second list. But I also want first list to be modified by user without any changes to the second one. Any ideas?
Check the code below, the button ng-click directive is calling the function AddtoList2($index) to add the current List1 item to List2, optionally it removes the current item from List1.
At template side
<div ng-repeat="item1 in List1">
...
<input type="button" ng-click="AddtoList2($index)" />
</div>
<div ng-repeat="item2 in List2">
...
</div>
At controller side
$scope.List1 = [];
$scope.List2 = [];
$scope.AddtoList2 = function (idx) {
var item = $scope.List1[idx];
$scope.List2.push(item);
//If you want to remove from List 1
$scope.List1.splice(idx, 1)
};
I am having a very hard time trying to figure out a solution for this. I have a checkbox group where the first checkbox is the parent and the following in that set will be child. I want the parent checkbox to be selected automatically if one of the child's checkbox is selected. Similarly parent needs to be get unchecked if no child is selected.
Here is my jsfiddle example: http://jsfiddle.net/Alien_time/PqTR7/3/
The main difficulty I am facing is because the checkboxes are created dynamically and it has dynamic ng-models for each. I have tried the following so far:
1) ng-checked: This doesnt work for me since ng-checked doesnt bind the value with ng-model. I need the ng-model of the parent to be updated as well since this is going to reflect in the main form.
2) JS solution: I thought js method will be the solution, but dont know how to add the js to controller as the ng-model is dynamically generated.
3) On other posts, there are some method that uses select all when parent is checked. But I couldnt find a solution for my approach since its the other way around where I only want the parent selected if one of the child is selected.
For my form, I need to have a different ng-model for each checkbox thats why I am using the name to create a dynamic ng-model name. But I just couldnt figure out how to select the parent checkbox if a child is selected in this dynamic list.
I have been stuck on this for 2 days and searched a lot on the net. Can you help me please?
HERE is the working solution based on your fiddle.
JS
$scope.select = function(index){
if(index === 0){
$scope.slaves.forEach(function(slave, ind){
$scope.slaves[ind].isChecked = $scope.slaves[0].isChecked;
});
}
else {
var anyChild = false;
for(var i = 1; i < $scope.slaves.length; i++){
anyChild = anyChild || $scope.slaves[i].isChecked;
}
$scope.slaves[0].isChecked = anyChild;
}
}
HTML
<div ng-repeat="slave in slaves">
<input type="checkbox" ng-model="slave.isChecked" ng-click="select($index)" />
{{slave.name}} - {{ slave.description }}
</div>
To tell the truth I do not find the solution elegant -- you would be better off by encapsulating the logic of it in a custom directive.
Moreover it would be probably better to express parent-child relation by:
var parent = {
... // parent data
childeren : [child_1, ... , child_N] // array of children
}
A solution to this problem is to add a watch.
Add the following in your controller
$scope.$watch('checkboxData', function (newValue, oldValue) {
var anyChecked = false;
// see if any are checked
$scope.slaves.reduce(function (pVal, cVal, idx, arr) {
if (newValue[cVal.name]) anyChecked = anyChecked || newValue[cVal.name];
});
// replace the parent 'checked' in the model
$scope.checkboxData['Parent'] = anyChecked;
}, true);
And add the following to your input elements.
ng-checked='checkboxData[slave.name]'