Computed Methods not updating on change checkbox value in vue js - javascript

I have made a codepen demonstrating a problem I'm having with a checkbox not working. On changes, the value of clipsData does not get updated.
https://codepen.io/bozlurrahman/pen/BeZVzR?editors=1010
<div id="video-clips-wrap">
<div>{{clipsData}}</div>
<li v-for="(clip, index) in clips" v-bind:key="index">
<div class="vl-subsource-container">
<input type="checkbox" value="issubsource" v-model="clip.subsourcesettings" v-on:change="viewSubSource(index)"><label>Not Update on change: {{clip.issubsource}}</label>
<div v-if="clip.subsourcesettings.length">
<label>Dynamic Contents</label>
</div>
</div>
<div class="meditations-options">
<label>Meditations: </label>
<input type="checkbox" value="motivation" v-model="clip.meditations"><label>1. Motivation</label>
<input type="checkbox" value="gratitude" v-model="clip.meditations"><label>2. Gratitude</label>
</div>
</li>
</div>
var video_clip_data_var = "[{\"meditations\":[\"motivation\",\"gratitude\"]}]";
var VideoClip = new Vue({
el: '#video-clips-wrap',
data: {
clips: [],
loading: false,
},
created: function () {
this.clips = JSON.parse(video_clip_data_var);
for (var i = 0; i < this.clips.length; i++) {
// if( typeof this.clips[i].meditations == "string" )
// this.clips[i].meditations = this.clips[i].meditations.split(',');
this.clips[i].subsourcesettings = "issubsource".split(',');
this.clips[i].subsources = [];
}
},
methods: {
viewSubSource: function (index) {
console.log(`this.clips[`+index+`].subsourcesettings`,this.clips[index].subsourcesettings);
console.log(`this.clips`,this.clips);
// this.clipsData = JSON.stringify(this.clips);
},
},
computed: {
clipsData: function () {
return JSON.stringify(this.clips);
},
}
});
Is there any one who can help me to fix this problem? When clicking on the check box, the hidden content should show directly.
Thanks.

Replace that
this.clips[i].subsourcesettings = "issubsource".split(',');
this.clips[i].subsources = [];
to
Vue.set(this.clips[i], 'subsourcesettings', "issubsource".split(','))
Vue.set(this.clips[i], 'subsources', [])
Here you can find more details about your problem.

Related

Leaflet GeoJSON filters

I have managed to get the below working with help from people on here but stuck at a JS problem. Currently, my problem is the map now only shows the two markers where carp and tench both = 1 in the GeoJSON. What I am trying to do is something more dynamic:
I'd like to have the checkboxes off as default
I'd also like to fix the problem where only the results that match carp = 1 and tench = 1 show on the map. I'd like the filter to be dynamic with each selection instead of restricting the results from the off
https://jsfiddle.net/chrisjenner/0ez7k8um/
Filter code:
<div class="input" id="carp">
<input type="checkbox" class="carp" name="1" value="1" checked="true">
<label for="1">Carp</label>
</div>
<div class="input" id="tench">
<input type="checkbox" class="tench" name="2" value="1" checked="true">
<label for="2">Tench</label>
</div>
const lakes = L.geoJSON(null, {
style: function(feature) {
return {
color: feature.properties.color
};
},
filter: (feature) => {
const isCarpChecked = checkboxStates.carp.includes(feature.properties.carp)
const isTenchChecked = checkboxStates.tench.includes(feature.properties.tench)
return isCarpChecked && isTenchChecked //only true if both are true
}
}).bindPopup(function(layer) {
return layer.feature.properties.name;
}).addTo(map);
function updateCheckboxStates() {
checkboxStates = {
carp: [],
tench: []
}
for (let input of document.querySelectorAll('input')) {
if (input.checked) {
var numValue = parseInt(input.value);
switch (input.className) {
case 'carp':
checkboxStates.carp.push(numValue);
break
case 'tench':
checkboxStates.tench.push(numValue);
break
}
}
}
}
for (let input of document.querySelectorAll('input')) {
input.onchange = (e) => {
lakes.clearLayers()
updateCheckboxStates()
lakes.addData(lakes_data)
}
}
updateCheckboxStates()
lakes.addData(lakes_data)

Vue Js range checkbox selection with shift

I have this html:
<div
class="data__file"
v-for="(data, index) in paginatedData"
:key="index"
>
<label class="data__info" :for="data.idfile" #click="onClickWithShift($event, index)">
<img
:src="data.link"
alt=""
:class= "{ 'data__image' : 1 ,'data__image-active' : (data.checked === 1) }"
/>
<input
v-if="isManager === true"
type="checkbox"
class="data__access"
:value="data.idaccess"
:checked="(data.checked === 1) ? 1 : null"
v-model="checkedFilesPermission"
/>
<input
v-if="isManager === false"
type="checkbox"
class="data__access"
:value="data.idfile"
:checked="(data.checked === 1) ? 1 : null"
v-model="checkedFilesDownload"
/>
</label>
</div>
This code generate list of checkbox inputs, then I need when user click on label with shift (because input`s is display:none), all checkboxes between clicked inputs will checked or unchecked like it make with jquery here
How can I shift-select multiple checkboxes like GMail?
But I cant realise how I can get it.
Big thanks to user Spiky Chathu, I did how He said, and its work without v-model , but when I try use v-model, it doesn`t work.
also this is my data:
data() {
return {
isManager: this.$store.getters.isManager,
checkedFilesPermission: [],
checkedFilesDownload: [],
lastCheckedIdx: -1,
checkedCount: 0,
paginatedData: [
{"link":"images/2020/08/20200803.jpg","idfile":296,"idaccess":2},
{"link":"images/2020/08/20200807.jpg","idfile":6,"idaccess":99},
{"link":"images/2020/08/20200812.jpg","idfile":26,"idaccess":29},
{"link":"images/2020/08/202123.jpg","idfile":960,"idaccess":2919},
{"link":"images/2020/08/2020032.jpg","idfile":16,"idaccess":9339},
{"link":"images/2020/08/20200000.jpg","idfile":2,"idaccess":9},
]
};
I think main problem that VUE somehow block input with v-model
I have come up with a solution to your problem. I have added a mock object to recreate the same scenario hoping that you have a array of objects.
Editted : Solution has been modified to cater multiple deselect scenario
new Vue({
el: '#app',
data: {
paginatedData: [
{"link":"https://img.icons8.com/list","idfile":296,"idaccess":2},
{"link":"https://img.icons8.com/list","idfile":6,"idaccess":99},
{"link":"https://img.icons8.com/list","idfile":26,"idaccess":29},
{"link":"https://img.icons8.com/list","idfile":960,"idaccess":2919},
{"link":"https://img.icons8.com/list","idfile":16,"idaccess":9339},
{"link":"https://img.icons8.com/list","idfile":2,"idaccess":9},
],
lastCheckedIdx: -1,
checkedFilesPermission : []
},
methods: {
onClickWithShift(event, idx, idFile) {
var action = (this.checkedFilesPermission.indexOf(idFile) === -1) ? 'select' : 'deselect';
if (event.shiftKey && this.lastCheckedIdx !== -1) {
var start = this.lastCheckedIdx;
var end = idx-1;
// can select top to bottom or bottom to top
// always start with lesser value
if (start > end) {
start = idx+1;
end = this.lastCheckedIdx;
}
for (var c = start; c <= end; c++) {
var currentIdFile = this.paginatedData[c]['idfile'];
this.markSelectDeselect(c, action, currentIdFile);
}
}
this.markSelectDeselect(idx, action, idFile);
this.lastCheckedIdx = idx;
if (this.checkedFilesPermission.length === 0) {
//reset last checked if nothing selected
this.lastCheckedIdx = -1;
}
},
markSelectDeselect(idx, action, idFile) {
var currentPos = this.checkedFilesPermission.indexOf(idFile);
if (action === 'select' && currentPos === -1) {
this.checkedFilesPermission.push(idFile);
} else if (action === 'deselect' && currentPos !== -1){
this.checkedFilesPermission.splice(currentPos, 1);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div
class="data__file"
v-for="(data, index) in paginatedData"
:key="index"
>
<input
:id="data.idfile"
type="checkbox"
class="data__access"
:value="data.idfile"
v-model="checkedFilesPermission"
/>
<label class="data__info" :for="data.idfile" #click="onClickWithShift($event, index, data.idfile)">
<img
:src="data.link"
alt=""
:class= "{ 'data__image' : 1 ,'data__image-active' : (checkedFilesPermission.indexOf(data.idfile) !== -1) }"
/>
</label>
</div>
</div>
You need to click on the image icon to see the result, since you have mentioned the input is hidden. I kept it visible here so that you can see it is actually getting changed
Here's something I just tried that seems to do the work
<template>
<div>
<div v-for="(item, index) in items" :key="index">
<label>
<input type="checkbox" v-model="item.checked" #click="checked($event, index)">
{{item.file}}
</label>
</div>
<pre>{{items}}</pre>
</div>
</template>
<script>
export default {
data() {
return {
lastCheckedIndex: null,
lastChange: null,
items: [
{ file: "foo1", idx: 10 },
{ file: "foo2", idx: 20 },
{ file: "foo3", idx: 40 },
{ file: "foo4", idx: 30 },
{ file: "foo5", idx: 10 },
{ file: "foo6", idx: 90 },
{ file: "foo8", idx: 50 },
]
}
},
methods: {
checked(event, index) {
// wheter or not to the multiple selection
if (event.shiftKey && (null != this.lastCheckedIndex) && (this.lastCheckedIndex != index)) {
const dir = index > this.lastCheckedIndex ? 1 : -1; // going up or down ?
const check = this.lastChange; // are we checking all or unchecking all ?
for (let i = this.lastCheckedIndex; i != index; i += dir) {
this.items[i].checked = check;
}
}
// save action
this.lastCheckedIndex = index;
this.lastChange = !this.items[index].checked; // onclick is triggered before the default change hence the !
}
},
};
</script>

React and JavaScript - binding functions explained [duplicate]

I am fairly new to the Facebook's React world. Their documentation seems to be very good but there are a few areas where I need a little bit of clarity. This is one of them.
Src: http://tuts-javascript.appspot.com/reactjs-add-remove-table-row
var CompanyApp = React.createClass({
getInitialState: function() {
return {companylist:this.props.companies};
},
handleNewRowSubmit: function( newcompany ) {
this.setState( {companylist: this.state.companylist.concat([newcompany])} );
},
handleCompanyRemove: function( company ) {
var index = -1;
var clength = this.state.companylist.length;
for( var i = 0; i < clength; i++ ) {
if( this.state.companylist[i].cname === company.cname ) {
index = i;
break;
}
}
this.state.companylist.splice( index, 1 );
this.setState( {companylist: this.state.companylist} );
},
render: function() {
var tableStyle = {width: '100%'};
var leftTdStyle = {width: '50%',padding:'20px',verticalAlign: 'top'};
var rightTdStyle = {width: '50%',padding:'20px',verticalAlign: 'top'};
return (
<table style={tableStyle}>
<tr>
<td style={leftTdStyle}>
<CompanyList clist={this.state.companylist} onCompanyRemove={this.handleCompanyRemove}/>
</td>
<td style={rightTdStyle}>
<NewRow onRowSubmit={this.handleNewRowSubmit}/>
</td>
</tr>
</table>
);
}
});
var CompanyList = React.createClass({
handleCompanyRemove: function(company){
this.props.onCompanyRemove( company );
},
render: function() {
var companies = [];
var that = this; // TODO: Needs to find out why that = this made it work; Was getting error that onCompanyDelete is not undefined
this.props.clist.forEach(function(company) {
companies.push(<Company company={company} onCompanyDelete={that.handleCompanyRemove} /> );
});
return (
<div>
<h3>List of Companies</h3>
<table className="table table-striped">
<thead><tr><th>Company Name</th><th>Employees</th><th>Head Office</th><th>Action</th></tr></thead>
<tbody>{companies}</tbody>
</table>
</div>
);
}
});
var Company = React.createClass({
handleRemoveCompany: function() {
this.props.onCompanyDelete( this.props.company );
return false;
},
render: function() {
return (
<tr>
<td>{this.props.company.cname}</td>
<td>{this.props.company.ecount}</td>
<td>{this.props.company.hoffice}</td>
<td><input type="button" className="btn btn-primary" value="Remove" onClick={this.handleRemoveCompany}/></td>
</tr>
);
}
});
var NewRow = React.createClass({
handleSubmit: function() {
var cname = this.refs.cname.getDOMNode().value;
var ecount = this.refs.ecount.getDOMNode().value;
var hoffice = this.refs.hoffice.getDOMNode().value;
var newrow = {cname: cname, ecount: ecount, hoffice: hoffice };
this.props.onRowSubmit( newrow );
this.refs.cname.getDOMNode().value = '';
this.refs.ecount.getDOMNode().value = '';
this.refs.hoffice.getDOMNode().value = '';
return false;
},
render: function() {
var inputStyle = {padding:'12px'}
return (
<div className="well">
<h3>Add A Company</h3>
<form onSubmit={this.handleSubmit}>
<div className="input-group input-group-lg" style={inputStyle}>
<input type="text" className="form-control col-md-8" placeholder="Company Name" ref="cname"/>
</div>
<div className="input-group input-group-lg" style={inputStyle}>
<input type="text" className="form-control col-md-8" placeholder="Employee Count" ref="ecount"/>
</div>
<div className="input-group input-group-lg" style={inputStyle}>
<input type="text" className="form-control col-md-8" placeholder="Headoffice" ref="hoffice"/>
</div>
<div className="input-group input-group-lg" style={inputStyle}>
<input type="submit" className="btn btn-primary" value="Add Company"/>
</div>
</form>
</div>
);
}
});
var defCompanies = [{cname:"Infosys Technologies",ecount:150000,hoffice:"Bangalore"},{cname:"TCS",ecount:140000,hoffice:"Mumbai"}];
React.renderComponent( <CompanyApp companies={defCompanies}/>, document.getElementById( "companyApp" ) );
This is a very good basic explanation of how ReactJS works. Thanks to the author.
But this comment,
var that = this; // TODO: Needs to find out why that = this made it work; Was getting error that onCompanyDelete is not undefined
Why is that necessary?
Is this the right way to do it? If not, what is?
Thanks in advance.
There's no mystery of "this" that is specific to ReactJS.
This is just a case of standard scoping issues that crop up with callbacks in JavaScript.
When you're in a react component, all methods on the base component will be scoped with the this being the current component, just like any other JavaScript "class".
In your snippet you have a render method which is a function on the base component and therefore this is equal to the component itself. However within that render method you're calling a callback with this.props.clist.forEach, any function callbacks inside the render method will need to be either bound to the correct this scope, or you can do var that = this (although this is an anti-pattern and should be discouraged)`.
Example, slightly simplified version of your snippet:
var MyComponent = React.createClass({
handleCompanyRemove: function(e) {
// ...
},
render: function() {
// this === MyComponent within this scope
this.props.someArray.forEach(function(item) {
// this !== MyComponent, therefore this.handleCompanyRemove cannot
// be called!
})
}
})
As you can see from the comments above, inside your callback for the .forEach you cannot use this directly without either defining a variable outside, or properly binding the function.
Other options to solve this are:
Binding the callback function to the correct this scope. Example:
this.props.someArray.forEach(function(item) {
// this === MyComponent within this scope too now!
// so you can call this.handleCompanyRemove with no problem
}.bind(this))
If you're using Babel/ES6 you can use the Fat Arrow function syntax which guarantees that this scope continues to the callback from the parent scope. Example:
this.props.someArray.forEach((item) => {
// this === MyComponent within this scope too now!
// so you can call this.handleCompanyRemove with no problem
})

Push data on array when checkbox is checked and remove data when checkbox is unchecked

I am taking data from table and display as multi check checkboxes.My checkboxes when checked pushes data on array for that particular checkbox.But when unchecked ,the respective data should be removed from the array.How can I achieve this?
HTML:
<div ng-repeat="data in filters.status" >
<label class="Form-label--tick">
<input type="checkbox" value="data.id" id="status" ng-model="status" class="Form-label-checkbox" ng-change="IfCheck(data.id,status)" >
<span class="Form-label-text"> {{data.status}}</span>
</label>
</div>
Javascript:
<script>
$scope.IfCheck = function (data, check) {
if (check) {
status.push(data);
$scope.checkedData[0].status = status;
}
else {
var index = $scope.status.indexOf(data);
$scope.status.splice(index);
}
};
</script>
This can be written as like this:
var app = angular.module('sa', []);
app.controller('FooCtrl', function($scope) {
$scope.ids = [];
$scope.filters = [{
id: 1,
status: false
}, {
id: 2,
status: false
}, {
id: 3,
status: false
}]
$scope.IfCheck = function(id, check) {
if (check) {
$scope.ids.push(id);
} else {
var index = $scope.ids.indexOf(id);
$scope.ids.splice(index, 1);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" ng-controller="FooCtrl">
<div ng-repeat="data in filters">
<label class="Form-label--tick">
<input type="checkbox" value="data.id" id="status" ng-model="data.status" class="Form-label-checkbox" ng-change="IfCheck(data.id, data.status)">
<span class="Form-label-text"> {{data.status}}</span>
</label>
</div>
Selected ids: {{ids}}
</div>
You can utilize the ng-model to see if the input is checked or unchecked. Note that I simplified the code, so you will need to add in your various attributes and logic to what's below:
HTML:
<input type="checkbox" ng-model="isChecked" ng-change="IfCheck()">
JS:
$scope.isChecked = false;
$scope.IfCheck = function () {
if ($scope.isChecked === true) {
// checked
} else {
// unchecked
}
};
This is the example plnkr for a checkbox input with ng-model that is on the Angular documentation.
For multiple checkboxes, you will need to track something like isChecked for each checkbox.

Managing an Array within an Array using KnockoutKS

I have an array within an array, for example I have the following objects:
{ruleGroups: [{
rules: [{
dataField1:ko.observable()
,operator:ko.observable()
,dataField2:ko.observable()
,boolean:ko.observable()
,duration:ko.observable()
}]
}]
};
How can I edit the array within the array?
I was able to improve the issue but still have problems with adding row when adding group, the new group works but the old groups run dead:
A working example is found here (http://jsfiddle.net/abarbaneld/UaKQn/41/)
Javascript:
var dataFields = function() {
var fields = [];
fields.push("datafield1");
fields.push("datafield2");
return fields;
};
var operators = function() {
var operator = [];
operator.push("Plus");
operator.push("Minus");
operator.push("Times");
operator.push("Divided By");
return operator;
};
var booleanOperators = function() {
var operator = [];
operator.push("Equal");
operator.push("Not Equal");
operator.push("Greater Than");
operator.push("Less Than");
operator.push("Contains");
operator.push("Etc...");
return operator;
};
var ruleObj = function () {
return {
dataField1:ko.observable()
,operator:ko.observable()
,dataField2:ko.observable()
,boolean:ko.observable()
,duration:ko.observable()
}
};
var ruleGroup = function() {
return rg = {
rules: ko.observableArray([new ruleObj()]),
addRow: function() {
rg.rules.push(new ruleObj());
console.log('Click Add Row', rg.rules);
},
removeRow : function() {
if(rg.rules().length > 1){
rg.rules.remove(this);
}
}
}
};
var ViewModel = function() {
var self = this;
self.datafields = ko.observableArray(dataFields());
self.operators = ko.observableArray(operators());
self.booleanOperators = ko.observableArray(booleanOperators());
self.groupRules = ko.observableArray([new ruleGroup()]);
self.addGroup = function() {
self.groupRules.push(new ruleGroup());
};
self.removeGroup = function() {
if(self.groupRules().length > 1){
self.groupRules.remove(this);
}
};
self.save = function() {
console.log('Saving Object', ko.toJS(self.groupRules));
};
};
ko.applyBindings(new ViewModel());
HTML
<div data-bind="foreach: { data: groupRules, as: 'groupRule' }" style="padding:10px;">
<div>
<div data-bind="foreach: { data: rules, as: 'rule' }" style="padding:10px;">
<div>
<select data-bind="options: $root.datafields(), value: rule.dataField1, optionsCaption: 'Choose...'"></select>
<select data-bind="options: $root.operators(), value: rule.operator, optionsCaption: 'Choose...'"></select>
<select data-bind="options: $root.datafields(), value: rule.dataField2, optionsCaption: 'Choose...',visible: operator"></select>
<select data-bind="options: $root.booleanOperators(), value: rule.boolean, optionsCaption: 'Choose...'"></select>
<input data-bind="value: rule.duration" />
<span data-bind="click: groupRule.addRow">Add</span>
<span data-bind="click: groupRule.removeRow">Remove</span>
</div>
</div>
<span data-bind="click: $parent.addGroup">[Add Group] </span>
<span data-bind="click: $parent.removeGroup">[Remove Group]</span>
</div>
</div>
<div>
<span data-bind="click:save">[Save]</span>
</div>
I was able to fix the issue by rearranging the function of ruleGroup to:
var ruleGroup = function() {
var rg = {
rules: ko.observableArray([new ruleObj()]),
addRow: function() {
rg.rules.push(new ruleObj());
console.log('Click Add Row', rg);
},
removeRow : function() {
if(rg.rules().length > 1){
rg.rules.remove(this);
}
}
}
return rg;
};
Not exactly sure why this made a difference but I think its due to now a new var is being created and referenced.
Working JSFiddle is found here http://jsfiddle.net/abarbaneld/UaKQn/

Categories