Selected sub items in a tree-shaped checkbox list - javascript

I have a viewmodel in javascript that includes checkbox items.
Javascript is here:
var data = [
{"name": "Computers",
"subcategories":[{"name":"Mac"}, {"name":"Desktop"}]},
{"name": "Mobile Phones" ,
"subcategories":[{"name":"Nokia"}, {"name":"Sony"}, {"name":"iPhone"}]}
];
var viewModel = {
categories: ko.observableArray(data),
selectedCategories: ko.observableArray()
};
ko.applyBindings(viewModel);
HTML code is like this:
<p>Categories: </p>
<div data-bind="foreach: categories">
<div>
<input type="checkbox" data-bind="checked: $parent.selectedCategories, checkedValue: $data.subcategories"/> <label data-bind="text: name"></label>
<ul data-bind="foreach: subcategories">
<li><input type="checkbox" data-bind="checked: $root.selectedCategories, checkedValue: $data"/> <label data-bind="text: name"></label></li>
</ul>
</div>
</div>
<p>Selected items: </p>
<div data-bind="text: ko.toJSON(selectedCategories)"></div>
When I click the main checkbox of a category (Computers) subcategories(Mac, Desktop) didn't populated. So I should clik again Mac and Desktop checkbox, but duplicated records inserting in selecteditems.
Here is jsfiddle code.

What's happening is the checkedValue binding in the parent category is storing the whole subcategories array as an array of objects when checked, when what I believe you want to happen is to store each object in the subcategories array individually. One way to accomplish that is with a click handler instead of checked and checkedValue.
JavaScript:
var data = [
{
"name": "Computers",
"subcategories": [{"name":"Mac"}, {"name":"Desktop"}]
},
{
"name": "Mobile Phones" ,
"subcategories": [{"name": "Nokia"}, {"name": "Sony"}, {"name": "iPhone"}]
}
];
var viewModel = {
categories: ko.observableArray(data),
selectedCategories: ko.observableArray(),
toggleCategory: function(category, event) {
var checked = event.target.checked;
ko.utils.arrayForEach(category.subcategories, function(item) {
viewModel.selectedCategories.remove(item);
if(checked) {
viewModel.selectedCategories.push(item);
}
});
return true;
},
};
ko.applyBindings(viewModel);
HTML
<p>Categories: </p>
<div data-bind="foreach: categories">
<div>
<input type="checkbox" data-bind="click: $root.toggleCategory"/> <label data-bind="text: name"></label>
<ul data-bind="foreach: subcategories">
<li><input type="checkbox" data-bind="checked: $root.selectedCategories, checkedValue: $data"/> <label data-bind="text: name"></label></li>
</ul>
</div>
</div>
<p>Selected items: </p>
<div data-bind="text: ko.toJSON(selectedCategories)"></div>
Notice the new toggleCategory method on the view model and the click: $root.toggleCategory binding that replaced the old checked and checkedValue bindings are the only pieces that have been changed.
Working JS Fiddle (tested in Chrome): http://jsfiddle.net/L2eWg/1/

Related

Add boolean checkboxes ng-model to object AngularJS

I am trying to do something like tree-checkboxes.
I would like to add boolean to children of checked name : ex. after click checkbox at name : eva - all her childrens will be also checked.
Like that :
$scope.messages =
{
"family": [
{
"name": "eva",
"checked" : true,
"childrens": [
{
"name": "John",
"checked" : true,
"childrens": [
{
"name": "Jacob",
"checked" : true,
}
My code :
<div ng-repeat="key in messages">
<ul ng-repeat=" (x, y) in key" style="list-style:none;">
<li>
<input type="checkbox" ng-model="y.check" ng-change="changeValue(shapes, y.name)" /> {{y.name}} {{y.check}}</li>
<li style="margin-left:15px;" ng-repeat="(a,b) in y.childrens">
<input type="checkbox" ng-model="b.check" ng-change="changeValue(shapes, b.name)" /> {{b.name}} {{b.check}}
<ul style="margin-left:15px;" ng-repeat="p in b.childrens">
<li>
<input type="checkbox" ng-model="p.check" ng-change="changeValue(shapes, p.name)" /> {{p.name}} {{p.check}}</li>
</ul>
</li>
</ul>
</div>
Here is my plnkr : http://plnkr.co/edit/lFYUvcTt1W709vam5Dfv?p=preview
I forked your pinkr
As you can see, I update the childrens in the ng-change and this function is recursive so all childrens will be checked aswell
$scope.changeValue = function(foo, person) {
for(var i in person.childrens){
person.childrens[i].check = person.check;
$scope.changeValue(foo, person.childrens[i]);
}
};
If a person is checked, the children will be checked. If a child is check, it won't update the parent.

First checkbox should be checked in ng-repeat if button clicked

Hi all I am new to angularjs could anybody help me please? All what I need when I click a button the first checkbox in ng-repeat list to be checked. Am I doing anything wrong? Here is my code:
Checkbox
<fieldset class="top5">
<legend title="Categories"><span class="info blackLabelUpper"><b>Dish Categories</b><span class="TextCtrlReqBgImage"> </span></span></legend>
<span class="checkbox-list" ng-repeat="dishType in dishTypes">
<input type='checkbox' ng-click="toggleDishType(dishType)" ng-model="dishType.checked" value="{{dishType}}" ng-checked="selectedDish.Type.indexOf(dishType) > -1">
{{dishType}}<br />
</span>
</fieldset>
Button
<kendo-button id="btnNewDish" class="k-primary" ng-click="onNewDishClick(true)">
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>New Dish
</kendo-button>
Function
scope.onNewDishClick = function (showNewDIshMessage) {
scope.dishTypes[0].dishType = true;
}
When you use ng-model on your inputs you don't need to use value to get result because ng-model save changes on self.
Feel free to ask question, and this sample maybe helps you.
var app = angular.module("app", []);
app.controller('ctrl', function($scope){
$scope.items = [
{somthing: false, title: 'a'},
{somthing: false, title: 'b'},
{somthing: false, title: 'c'},
{somthing: false, title: 'd'},
{somthing: false, title: 'e'}
]
$scope.checkAll = function(){
angular.forEach($scope.items, function(item){
item.somthing = !item.somthing;
});
}
$scope.checkFirstItem = function(){
$scope.items[0].somthing = !$scope.items[0].somthing;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items">
<input type="checkbox" ng-model="item.somthing">
{{item.somthing}}
{{item.title}}
</li>
</ul>
<button ng-click="checkAll()">check all</button>
<button ng-click="checkFirstItem()">check first item</button>
</div>
<div ng-repeat="restorent_type in restaurant_types" style="margin:5px;" class="col col-offset-10">
<label class="item item-radio radio-label {{restorent_type.RESCOD}}">
<input type="checkbox" name="group" ng-model="restorent.types" value="{{restorent_type.RESCOD}}" ng-click="filters.RESCOD = restorent_type.RESCOD" ng-checked="$first">
<div class="item-content">
{{restorent_type.RESCOD}}
</div>
<i class="radio-icon ion-checkmark"></i>
</label>
</div>
Or you can use the :
ng-model="dishType.checked" ng-init="dishType.checked=true"
I think the Function definition should be like this :
scope.onNewDishClick = function (showNewDIshMessage) {
scope.dishTypes[0].checked = true;
}

How to hide content with ng-if and a checkbox that's inside a ng-repeat and it's ng-model is an array

I have a list of checkboxes that are created using a ng-repeat, I use those checkboxes to get the IDs of the objects that are inside a list, these objects have a name property and a id property.
this is a object inside my categoryList
category = {
idCategory: 2,
name: 'myName'
};
And this is how I declare my checkboxes.
<div class="form-group">
<label>Select a category</label>
<label class="checkbox-inline" ng-repeat="category in categoryList">
<input type="checkbox" ng-model="idsCategory[category.idCategory]"> {{category.name}}
</label>
</div>
and then in my controller I get the ids like this
var categories= $scope.categoryList;
var idsSelected = [];
//get selected ids
for (var idCategory in categories) {
if (categories.hasOwnProperty(idCategory )) {
if (categories[idCategory ] === true) {
idsSelected .push(idCategory);
}
}
}
but now I want to hide and show some inputs in my HTML, if the user selects a category like "support" I want to show some inputs, but I don't know what condition put inside my ng-if since I declare my checkboxes ng-model like an array of ids like this ng-model="idsCategory[category.idCategory]"
how can I put my ng-if to hide the inputs I tried this but this doesn't work
<div id="b" ng-if="idsCategory.category.name=='Support'">
<div class="form-group">
<label>Support hours:</label>
<input class="form-control" placeholder="support houres">
</div>
</div>
Edited:
If we have HTML:
<div class="form-group">
<label>Select a category</label>
<label class="checkbox-inline" ng-repeat="category in categoryList">
<input type="checkbox" ng-model="idsCategory[category.idCategory]"> {{category.name}}
</label>
</div>
Conroller:
$scope.idsCategory = {};
$scope.categoryList = [{
idCategory: 1,
name: 'categoryA'
}, {
idCategory: 2,
name: 'recreation'
}, {
idCategory: 3,
name: 'develop'
}, {
idCategory: 4,
name: 'Support'
}];
Then we can use following ng-show:
<div id="b" ng-show="idsCategory[4]">
<div class="form-group">
<label>Support hours:</label>
<input class="form-control" placeholder="support houres">
</div>
</div>
See jsfiddle
In checkbox input we have ng-model="idsCategory[category.idCategory]", so idsCategory[4] stores info if category with idCategory = 4 is checked. That's why element with ng-show="idsCategory[4]" will be shown if category with idCategory = 4 is checked.

Knockoutjs computed not updated from observableArray

I want a computed to be updated when an observable array is updated. The array is populated with questions and answer (Yes or No). When the user change the answer of a question, I want some region to be visible or not.
So the computed is5B should be true if one of the question is answered "oui" and this should make the sections visible.
The is5B computed is only calculated at initialization and is not fired when the array is updated (it is updated, I checked with a breakpoint)
Here's the view model:
var section5Model = ko.validatedObservable({
Questions5A: ko.observableArray(GetQuestions('5A')),
Questions5B: ko.observableArray(),
Questions5C: ko.observableArray(),
ContactAQ: ko.observable(),
Date: ko.observable(''),
Heure: ko.observable(''),
CategorisePar: ko.observable(''),
DateCategorise: ko.observable(''),
RepOuiNon: [{ label: 'Oui', value: 0 }, { label: 'Non', value: 1 }]
});
section5Model().is5B = ko.computed(function () {
this.Questions5A().forEach(function (item) {
if (item.reponse == 'Oui') {
return true;
}
});
}, section5Model());
Here's the markup:
<div id="sectionContainer">
<div id='S5FormBlock1' class="formSection5">
<div id="QSection5A" data-bind="foreach: Questions5A">
<div class='mockTable'>
<div class="column200 centerLabel"><span data-bind="text: Texte"></span></div>
<div class="grayRoundBorder padR10" data-bind="foreach: $parent.RepOuiNon">
<input type="radio" data-bind="value: label, attr : {name: $parent.ID}, checked: $parent.reponse" /><span data-bind="text: label"></span>
</div>
</div>
<p />
</div>
<div data-bind="visible: is5B">Il s'agit d'une plainte qualité</div>
<div id="QSection5B" data-bind="visible: is5B,foreach: Questions5B">
<div class='mockTable'>
<div class="column200 centerLabel"><span data-bind="text: Texte"></span></div>
<div class="grayRoundBorder padR10" data-bind="foreach: $parent.RepOuiNon">
<input type="radio" data-bind="value: label, attr : {name: $parent.ID}, checked: $parent.reponse" /><span data-bind="text: label"></span>
</div>
</div>
<p />
</div>
<div data-bind="visible: is5C">Il s'agit d'une plainte d'insatisfaction</div>
<div id="QSection5C" data-bind="visible: is5C,foreach: Questions5C">
<div class='mockTable'>
<div class="column200 centerLabel"><span data-bind="text: Texte"></span></div>
<div class="grayRoundBorder padR10" data-bind="foreach: $parent.RepOuiNon">
<input type="radio" data-bind="value: label, attr : {name: $parent.ID}, checked: $parent.reponse" /><span data-bind="text: label"></span>
</div>
</div>
<p />
</div>
</div>
</div>
The problem that you have is that item.response is not observable. So if it change KnockoutJS doesn't know about that. To fix this you have to change that to observable
section5Model().is5B = ko.computed(function () {
this.Questions5A().forEach(function (item) {
if (item.reponse() == 'Oui') {
return true;
}
});
}, section5Model());
Computed are functions that are dependent on one or more other observables, and will automatically update whenever any of these dependencies change. so in your case no observable inside your computed function. so make at-least one variable in side computed as observable. in your case please make the item.response as observable. for that you need to return observable variable on GetQuestions('5A')
Please do Questions5A observableArray as like
var section5Model = ko.validatedObservable({
Questions5A: ko.observableArray([
{reponse : ko.observable('reponse 1 ') },
{reponse : ko.observable('reponse 2') },
/* other objects */
]),
/* other code */

Dynamic ng-model in ng-repeat

Using Angular JS, I have this object
var pages = [
{ name: 'users', title : "Users" },
{ name: 'roles', title : 'Roles' }
];
I'm trying to create a page for user roles. For that I need 4 checkboxes for each page. Each page must have 4 rules. Access, show, edit, delete.
Displaying pages in a ng-repeat
<div ng-controller='PageCtrl'>
<ul>
<li ng-repeat='page in pages'>
{{ page.title }}
<input type="checkbox" name="access"> Access
<input type="checkbox" name="show"> Show
<input type="checkbox" name="edit"> Edit
<input type="checkbox" name="delete"> Delete
</li>
</ul>
</div>
So I need to pass those checkboxes values to the $scope. How can I bind a model for each checkbox?
Like this
<input type="checkbox" name="access" ng-model='page.name+_access'> Access
Checkbox model name should start with page.name. Like users_access, users_show...
You can use the following syntax:
ng-model="page[page.name+'_access']"
But as a matter of fact, it looks one can move those groups into controller as well, then use another ng-repeat. Like this:
HTML:
<div ng-controller="PageCtrl">
<ul>
<li ng-repeat='page in pages'>{{ page.title }}
<label ng-repeat="right in rights">
<input type="checkbox" ng-model="page[page.name + '_' + right]" name="{{right}}" />{{right|capitalize}}</label>
<button ng-click="log(page)">Log</button>
</li>
</ul>
</div>
JS:
var module = angular.module('myAp', [])
.controller('PageCtrl', ['$scope', function ($scope) {
$scope.pages = [{
name: 'users',
title: "Users"
}, {
name: 'roles',
title: 'Roles'
}];
$scope.rights = ['access', 'show', 'edit', 'delete'];
$scope.log = function (page) {
console.log(page);
}
}]).filter('capitalize', function() {
return function (value) {
return value.charAt(0).toUpperCase() + value.slice(1);
};
});
Demo.

Categories