Here in first condition i was able to disbale all parents except current parent that is selected.
checkbox.js
if (geoLocation.id === 5657){
var getParent = geoLocation.parent();
$.each(geoLocation.parent(),function(index,location) {
if (location.id !== geoLocation.id) {
var disableItemId = 'disabled' + location.id;
// Get **strong text**the model
var model = $parse(disableItemId);
// Assigns a value to it
model.assign($scope, true);
}
}
);
At this point i am trying to disbale all the child for the parents that are disabled in above condition. How to achieve that task with below code any help will be appreciated.
So far tried code...
$.each(geoLocation.parent().items,function(index,location) {
if(location.id !== geoLocation.id){
var disableItemId = 'disabled' + location.children.data;
// Get the model
var model = $parse(disableItemId);
// Assigns a value to it
model.assign($scope, true);
}
});
console.log(getParent);
}
If you plan to use angular, better express all of this in a model structure, and use ng-click and ng-disabled to achieve what you need.
Template:
<ul>
<li ng-repeat="item in checkboxes">
<input type="checkbox" ng-disabled="item.disabled" ng-click="disableOthers(item)"> {{item.name}}
</li>
</ul>
Controller:
$scope.disableOthers = function (item) {
// iterate over parents and childs and mark disabled to true
};
Related
Having issues getting some checkboxes to work properly. So in my component I have an array of objects set in a state variable tokenPermissions that look like this
tokenPermissions: [
{
groupName: "App",
allSelected: false,
someSelected: false,
summary: "Full access to all project operations",
permissions: [
{
name: "can_create_app",
summary: "Create new projects",
selected: false,
},
{
name: "can_delete_app",
summary: "Delete existing projects",
selected: false,
},
{
name: "can_edit_app",
summary: "Edit an existing project",
selected: false,
},
],
}
],
The goal is to loop through this array and have a parent and children checkboxes like so tokenPermissions[i].allSelected bound to the parent checkbox and for each object in tokenPermissions[i].permissions a corresponding checkbox bound to the selected property like so tokenPermissions[i].permissions[j].selected.
Desired behaviour when the parent checkbox is selected,
If all child checkboxes checked, uncheck all, including the parent
If child checkboxes are unchecked check all including the parent
if only some of the child checkboxes are selected, the parent would show the indeterminate - icon or sign and on click, uncheck all child checkboxes including the parent.
The issue is point 3. The issue is sometimes the parent checkbox is not correctly checked based on the state of the attribute bounded to. For example allSelected can be false but the parent checkbox is checked.
I put a complete working example on github here https://github.com/oaks-view/vuejs-checkbox-issue.
The bit of code with the binding is as follows
<ul
class="list-group"
v-for="(permissionGroup, permissionGroupIndex) in tokenPermissions"
:key="`${permissionGroup.groupName}_${permissionGroupIndex}`"
>
<li class="list-group-item">
<div class="permission-container">
<div>
<input
type="checkbox"
:indeterminate.prop="
!permissionGroup.allSelected && permissionGroup.someSelected
"
v-model="permissionGroup.allSelected"
:id="permissionGroup.groupName"
v-on:change="
permissionGroupCheckboxChanged($event, permissionGroupIndex)
"
/>
<label :for="permissionGroup.groupName" class="cursor-pointer"
>{{ permissionGroup.groupName }} -
<span style="color: red; margin-left: 14px; padding-right: 3px">{{
permissionGroup.allSelected
}}</span></label
>
</div>
<div class="permission-summary">
{{ permissionGroup.summary }}
</div>
</div>
<ul class="list-group">
<li
class="list-group-item list-group-item-no-margin"
v-for="(permission, permissionIndex) in permissionGroup.permissions"
:key="`${permissionGroup.groupName}_${permission.name}_${permissionIndex}`"
>
<div class="permission-container">
<div>
<input
type="checkbox"
:id="permission.name"
v-bind:checked="permission.selected"
v-on:change="
permissionGroupCheckboxChanged(
$event,
permissionGroupIndex,
permissionIndex
)
"
/>
<label :for="permission.name" class="cursor-pointer"
>{{ permission.name
}}<span
style="color: red; margin-left: 3px; padding-right: 3px"
> {{ permission.selected }}</span
></label
>
</div>
<div class="permission-summary">
{{ permission.summary }}
</div>
</div>
</li>
</ul>
</li>
</ul>
And for updating the checkbox
getPermissionGroupSelectionStatus: function (permissionGroup) {
let allSelected = true;
let someSelected = false;
permissionGroup.permissions.forEach((permission) => {
if (permission.selected === false) {
allSelected = false;
}
if (permission.selected === true) {
someSelected = true;
}
});
return { allSelected, someSelected };
},
permissionGroupCheckboxChanged: function (
$event,
permissionGroupIndex,
permissionIndex
) {
const { checked } = $event.target;
// its single permission selected
if (permissionIndex !== undefined) {
this.tokenPermissions[permissionGroupIndex].permissions[
permissionIndex
].selected = checked;
const { allSelected, someSelected } =
this.getPermissionGroupSelectionStatus(
this.tokenPermissions[permissionGroupIndex]
);
this.tokenPermissions[permissionGroupIndex].allSelected = allSelected;
this.tokenPermissions[permissionGroupIndex].someSelected = someSelected;
} else {
// its selectAll check box
const { allSelected, someSelected } =
this.getPermissionGroupSelectionStatus(
this.tokenPermissions[permissionGroupIndex]
);
let checkAll;
// no checkbox / permission is selected then set all
if (!someSelected && !allSelected) {
checkAll = true;
} else {
checkAll = false;
}
this.tokenPermissions[permissionGroupIndex].allSelected = checkAll;
this.tokenPermissions[permissionGroupIndex].someSelected = checkAll;
for (
let i = 0;
i < this.tokenPermissions[permissionGroupIndex].permissions.length;
i++
) {
this.tokenPermissions[permissionGroupIndex].permissions[i].selected =
checkAll;
}
}
},
It's a rendering problem.
Vue set the allSelected checkbox as checked, then in the same cycle updates it to false; you can read about Vue life cycle here: https://it.vuejs.org/v2/guide/instance.html
A pretty brutal (but simple) way to resolve it (which I don't recommend, but it's useful to understand what's happening) is to delay the update.
Wrap the last part of the method permissionGroupCheckboxChanged with a this.$nextTick:
this.$nextTick(() => {
this.tokenPermissions[permissionGroupIndex].allSelected = checkAll;
this.tokenPermissions[permissionGroupIndex].someSelected = checkAll;
for (
let i = 0;
i < this.tokenPermissions[permissionGroupIndex].permissions.length;
i++
) {
this.tokenPermissions[permissionGroupIndex].permissions[i].selected =
checkAll;
}
})
This way when you change the values, the engine reacts accordingly.
Still I don't recommend it (I think nextTick is useful to understand the Vue life cycle, but I would recommend against using it whenever is possible).
A less brutal (and simpler) way is to set the allSelected to null instead of false when checkAll is not true permissionGroupCheckboxChanged:
// this
this.tokenPermissions[permissionGroupIndex].allSelected = checkAll ? checkAll : null;
// instead of this
this.tokenPermissions[permissionGroupIndex].allSelected = checkAll;
this way the prop wins against the model (as the model value becomes null).
But the even better option (imho) would be to use a component of its own inside the v-for loop and have allSelected and someSelected as computed properties instead of values bound to real variables.
Usually you should not store ui status as data when it can be inferred from real data (I may be wrong, as I don't know your application, but in your case I suspect you are interested in the single checkboxes' values, while allSelected/someSelected are merely used for ui).
In my code i have two grid on for base layer other for utility layer .
Base layer has radio button and on clicking the logic used is working
{
var baseLayerGroup= new ol.layer.Group({
layers:[
openstreetmapstandard,openstreetmaphumanitarian
]
})
map.addLayer(baseLayerGroup);
//Layer Switcher Logic for Baselayer
var baseLayerElements = document.querySelectorAll('.sidebar1 > input[type=radio]');
for(let baseLayerElement of baseLayerElements){
baseLayerElement.addEventListener('change',function(){
let baseLayerElementValue = this.value;
baseLayerGroup.getLayers().forEach(function(element, index, array){
let baseLayerTitle = element.get('title');
element.setVisible(baseLayerTitle === baseLayerElementValue);
})
})
}
var dataLayerGroup= new ol.layer.Group({
layers:[
Sector_office,Roads
]
})
and my logic for checkbox is :
Sector_office.setVisible(true);
Roads.setVisible(false);
var toggleLayer = function(inputEl){
map.getLayers().forEach(function(layer){
if (layer.get('name') === inputEl.name)
layer.setVisible(inputEl.checked);
});
};
map.addLayer(dataLayerGroup);
but my checkbox logic is not working and here is my html page for it
<input type="checkbox" onClick="toggleLayer(this);" value="Sector_office" checked>Sector office<br>
<input type="checkbox" onClick="toggleLayer(this);" value="Roads" checked>Roads<br>
</div>
For what I see you have a couple of issues in your code.
In the handler function you are referencing the name attribute of the dom element, but I see not such attribute in the html. That will always result false. So you have to set the name attribute or use the value attribute.
The onclick event handler receive as a parameter a MouseEvent argument. To get a reference of the dom element inside the handler you can use this or you can use the event argument attribute target.
Resuming,
handler
const toggleLayer = function(e){
const chk = e.target.
map.getLayers().forEach(function(layer){
if (layer.get('name') === chk.value)
layer.setVisible(chk.checked);
});
};
html
<input type="checkbox" onclick="toggleLayer(event)"
value="Sector_office" checked>Sector office<br>
<input type="checkbox" onclick="toggleLayer(event)"
value="Roads" checked>Roads<br>
I have a small unordered list in a vue template:
<ul style="border-bottom:none !important; text-decoration:none">
<li class="commentToggle" v-bind:class="{active:commentActive}" v-on:click="setInputName('new')">New Comment</li>
<li class="commentToggle" v-bind:class="{active:commentActive}" v-on:click="setInputName('note')">Note</li>
</ul>
and I then have a data variable for commentActive and a function I call to set the input name:
data () {
return {
commentActive: false,
}
},
methods: {
setInputName(str) {
this.inputName = str;
this.commentActive = true;
},
}
And this is functional but it is obviously setting BOTH inputs to active when I click on one of them. How do I alter this to only set the clicked line item active?
You need to use a unique identifier to determine which comment is active. In the most rudimentary way using your current setup:
<li class="commentToggle"
v-bind:class="{active:commentActive === 'new'}"
v-on:click="setInputName('new')">
New Comment
</li>
<li class="commentToggle"
v-bind:class="{active:commentActive === 'note'}"
v-on:click="setInputName('note')">
Note
</li>
setInputName(str) {
this.inputName = str;
this.commentActive = str;
},
The adjustment that we made is to ensure that the commentActive matches the str that we've used. You can change that identifier to anything else, and pass additional arguments if you so choose.
I have a list of items in the DOM:
<ul id="my_list">
<li class="relevant">item 1 <a onclick="remove(1)">Remove</a></li>
<li class="relevant">item 2 <a onclick="remove(2)">Remove</a></li>
...
</ul>
The user can remove items from the list by clicking the respective 'remove' links in the item.
I want to reactively monitor this list for changes, so that when there are no items left it triggers a function. For example:
var num_list_items = $("#my_list li.relevant").length;
if( num_list_items == 0 ){
do_something();
}
My app is being built in Meteor, so would ideally like to know if there is any native functionality that can do this. If not, any other suggestions are welcome.
You can use a MutationObserver(link)
You'll instantiate one with something to do everytime the observed node mutates, if the mutation reports the desired condition you'll do something about it.
var observed = document.getElementById('my_list');
var whatIsObserved = { childList : true };
var mo = new MutationObserver(function(mutations){
mutations.forEach(function(mutation){
if(mutation.type === 'childList'){
//DOM Tree mutated
if(mutated.target.querySelectorAll('li.relevant').length === 0){
//mutated target has no childs.
}
}
});
});
mo.observe(observed, whatIsObserved);
Alternatively, have the remove function trigger your condition.
Is it at all easy to use jQuery.sortable on ng-repeat elements in AngularJS?
It would be awesome if re-ordering the items automatically propagated that ordering back into the source array. I'm afraid the two systems would fight though. Is there a better way to do this?
Angular UI has a sortable directive,Click Here for Demo
Code located at ui-sortable, usage:
<ul ui-sortable ng-model="items" ui-sortable-update="sorted">
<li ng-repeat="item in items track by $index" id="{{$index}}">{{ item }}</li>
</ul>
$scope.sorted = (event, ui) => { console.log(ui.item[0].getAttribute('id')) }
I tried to do the same and came up with the following solution:
angular.directive("my:sortable", function(expression, compiledElement){
return function(linkElement){
var scope = this;
linkElement.sortable(
{
placeholder: "ui-state-highlight",
opacity: 0.8,
update: function(event, ui) {
var model = scope.$tryEval(expression);
var newModel = [];
var items = [];
linkElement.children().each(function() {
var item = $(this);
// get old item index
var oldIndex = item.attr("ng:repeat-index");
if(oldIndex) {
// new model in new order
newModel.push(model[oldIndex]);
// items in original order
items[oldIndex] = item;
// and remove
item.detach();
}
});
// restore original dom order, so angular does not get confused
linkElement.append.apply(linkElement,items);
// clear old list
model.length = 0;
// add elements in new order
model.push.apply(model, newModel);
// presto
scope.$eval();
// Notify event handler
var onSortExpression = linkElement.attr("my:onsort");
if(onSortExpression) {
scope.$tryEval(onSortExpression, linkElement);
}
}
});
};
});
Used like this:
<ol id="todoList" my:sortable="todos" my:onsort="onSort()">
It seems to work fairly well. The trick is to undo the DOM manipulation made by sortable before updating the model, otherwise angular gets desynchronized from the DOM.
Notification of the changes works via the my:onsort expression which can call the controller methods.
I created a JsFiddle based on the angular todo tutorial to shows how it works: http://jsfiddle.net/M8YnR/180/
This is how I am doing it with angular v0.10.6. Here is the jsfiddle
angular.directive("my:sortable", function(expression, compiledElement){
// add my:sortable-index to children so we know the index in the model
compiledElement.children().attr("my:sortable-index","{{$index}}");
return function(linkElement){
var scope = this;
linkElement.sortable({
placeholder: "placeholder",
opacity: 0.8,
axis: "y",
update: function(event, ui) {
// get model
var model = scope.$apply(expression);
// remember its length
var modelLength = model.length;
// rember html nodes
var items = [];
// loop through items in new order
linkElement.children().each(function(index) {
var item = $(this);
// get old item index
var oldIndex = parseInt(item.attr("my:sortable-index"), 10);
// add item to the end of model
model.push(model[oldIndex]);
if(item.attr("my:sortable-index")) {
// items in original order to restore dom
items[oldIndex] = item;
// and remove item from dom
item.detach();
}
});
model.splice(0, modelLength);
// restore original dom order, so angular does not get confused
linkElement.append.apply(linkElement,items);
// notify angular of the change
scope.$digest();
}
});
};
});
Here's my implementation of sortable Angular.js directive without jquery.ui :
https://github.com/schartier/angular-sortable
you can go for ng-sortable directive which is lightweight and it does not uses jquery. here is link ng-sortable drag and drop elements
Demo for ng-sortable