My Vue.js version is v1.0.28. I was wondering how I could use Vue.$set to set a deep property of an object array. For example, I have:
this.pairs = [
{
"left_media": {
"name": "A",
"show": false
},
"right_media": {
"name": "B",
"show": false
}
},
{
"left_media": {
"name": "B",
"show": false
},
"right_media": {
"name": "A",
"show": false
}
}
]
I need to loop through this.pairs array and update the show property of each media name == A to true.
I can seem to update them easily via a for loop, but the view won't change. I've done some research and seems like Vue.$set can help fix this, but I can't seem to get it work in this case.
How do I use $set to do: this.pairs[i].left_media.show = false;?
EDIT
AJAX call to update show_comment
toggleComment: function(media, which_media) {
this.$http.post('/api/project-media/update.json', {
id: media.id,
show_comment: !media.show_comment
})
.then(function (response) {
if (response.body.success) {
// update show_comment status for this media in all pairs\
for (let pair of this.pairs) {
for (let key of Object.keys(pair)) {
if (key == 'project_media_left' || key == 'project_media_right') {
if (pair[key].id == media.id) {
this.$set(pair[key], 'show_comment', !media.show_comment);
}
}
}
}
}
}, function (response) {
});
}
If your target environment supports Object.values or you are using babel or something to compile, then its just
for (let pair of this.pairs)
for (let v of Object.values(pair))
if ('A' == v.name) v.show = true
If not then
for (let pair of this.pairs)
for (let key of Object.keys(pair))
if (pair[key].name == 'A')
this.$set(pair[key], 'show', true)
Example.
But to be honest,
for (let pair of this.pairs)
for (let key of Object.keys(pair))
if (pair[key].name == prop)
pair[key].show = true
also works for me, so I wonder if there is something else going on with your issue.
Related
Is there any good way to use a nested object to generate vue components?
I have a deeply nested object that looks like this:
"api": {
"v1": {
"groups": {
"create": true,
"get": true,
"item": {
"get": true,
"destroy": false
}
}
}
}
I want to generate a form which has checkboxes for each of the values of the object.
I'm having trouble binding the values of the object to the v-models in a Vue checkbox
I've tried making a list of lookup keys like ["api.v1.groups.create", "api.v1.groups.get"]
then using a function like the following to get the entries:
getPerm (p) {
return p.split('.').reduce(
(xs, x) => (xs && xs[x]) ? xs[x] : null,
this.role.permissions)
}
However, this does not work because it gives me the boolean and not the reference.
Check out my:
Code sandbox to flatten dict and create a list from a nested entry
( I forked it from a basic Vue template so it still has some references to 'chart' in there)
Once you have flattened the dictionary you use them to generate vue components using v-for as you normally would!
Below is the processing I used to flatten the dictionary if you would like to check that here on site:
// Define out nested object
var nested = {
"api": {
"v1": {
"groups": {
"create": true,
"get": true,
"item": {
"get": true,
"destroy": false
}
}
}
}
}
// function for checking if we are at the bottom of the Object
const isObj = o => o?.constructor === Object;
// I had to use a class to store the output in the different recursive scopes
class NestedProcessor {
// Constructur starts the function and returns the dictionary as flat
constructor(leveled_dict) {
this.output = {}
this.process_dict_recursive(leveled_dict)
return this.ouput
}
process_dict_recursive(leveled_dict, keyList = [], depth = 0) {
if (isObj(leveled_dict)) { // check if we have hit the bottom
keyList.push('')
depth++
for (let key in leveled_dict) {
keyList[depth - 1] = key
this.process_dict_recursive(leveled_dict[key], keyList, depth) // call the function recursively at our new depth
}
}
else {
// Create our lookup keys
let path = ''
keyList.forEach((v) => {
path += v
path += '.'
})
path = path.slice(0, -1) // Remove the last '.'
this.output[path] = leveled_dict
}
}
}
console.log(new NestedProcessor(nested))
//{
// "output": {
// "api.v1.groups.create": true,
// "api.v1.groups.get": true,
// "api.v1.groups.item.get": true,
// "api.v1.groups.item.destroy": false
// }
//}
Notes:
I used a class, because I couldn't figure out how to handle variable scope within the recursion
I needed a way to check if we were at the bottom so grabbed a function from SO to check for that.
Below is my request body and which is sending from client side
var credentials = {
"ConsumerData": {
"ConsumerStoreId": "a",
"ConsumerUserId": "a"
},
"CustomerData": {
"CustomerId": "2345678890"
},
"ErnD": {
"UID": "3",
"TxnDt": "1"
},
"PurD": [{
"ItemCode": "3456tghw3",
"ItemEANCode": "223222122"
},
{
"ItemCode": "8jghw3865",
"ItemEANCode": "3334443222"
}
]
}
for testing i am sending var credentials = {} empty credentials
In server side controller(node,express) i want to check req.body empty or not
if(!req.body)
{
console.log('Object missing');
}
if(!req.body.ConsumerData.ConsumerStoreId)
{
console.log('ConsumerStoreId missing');
}
if(!req.body.CustomerData.CustomerId)
{
console.log('CustomerId missing');
}
if(!req.body.ErnD.UID)
{
console.log('UID missing');
}
console.log('outside');
i am checking everything but alwasys its printing outside only
ES2015:
if(req.body.constructor === Object && Object.keys(req.body).length === 0) {
console.log('Object missing');
}
PRE ES2015:
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return JSON.stringify(obj) === JSON.stringify({});
}
if(isEmpty(req.body)) {
console.log('Object missing');
}
For more ways in a pre es2015 style: https://coderwall.com/p/_g3x9q/how-to-check-if-javascript-object-is-empty
if (Object.keys(req.body).length === 0) {
// Do something
}
When the req.body is empty, it returns an empty object, as such, making !req.body return false even when it's empty. Instead, you should test for !Object.keys(req.body).length. What it will do is take every key from the object and count, if no keys where found it would return 0. Then all we need to do is use the same method that we use on empty arrays, testing for !arr.length making it possible to receive a boolean stating it's fullness or the lack of it.
Then, we end up with the code:
router.post('/auth/signup', function(req, res) {
if(!Object.keys(req.body).length) {
// is empty
} else if(!req.body.param1 || !req.body.param2 || !req.body.param3) {
let params = [req.body.param1, req.body.param2, req.body.param3];
let lackingParam = params.findIndex(param => !param === true) > 0 ? params.findIndex(param => !param === true) > 1 ? "req.body.param3" : "req.body.param2" : "req.body.param1";
// lacking only lackingParam
} else {
// not empty
}
});
What about using lodash.isempty
const isEmpty = require('lodash.isempty');
if(isEmpty(req.body)) {
console.log('Empty Object');
}
Here are the docs https://lodash.com/docs/4.17.11#isEmpty
Checks if value is an empty object, collection, map, or set.
Objects are considered empty if they have no own enumerable string
keyed properties.
Array-like values such as arguments objects, arrays, buffers, strings,
or jQuery-like collections are considered empty if they have a length
of 0. Similarly, maps and sets are considered empty if they have a
size of 0.
I have an Javascript object like so...
var strategies = [{
"strategy": {
"category": "war"
}
}, {
"strategy": {
"category": "farming"
}
}]
I then have an array that indicates which results I'd like back. It can be any of the following: [] OR ["war"] ["farming"] OR ["war", "farming"].
If we have the [], I want to return no results. But if ["war", "farming"] I want to return both of the results above.
How do I accomplish this with Array.prototype.filter? I saw this post, but couldn't reason through it.
strategies.filter((strategy) =>
????
)
Thanks for your help.
You can just check the value with indexOf:
var categories = ['war', 'farming'];
var filtered = strategies.filter((obj) => {
return categories.indexOf(obj.strategy.category) > -1;
});
Your object, strategy was a wrapped object, so my first line was setting it to its inner strategy and then filter as needed.
var strategies = [{
"strategy": {
"category": "war"
}
}, {
"strategy": {
"category": "farming"
}
}]
var b = ["war", "farming"];
strategies.filter(function(strategy){
strategy = strategy.strategy;
for(var i in b){
if (b[i] == strategy["category"]) {
return true;
}
}
return false;
});
Tests the input array to see if it's empty as per your requirements:
function filterObj(arr) {
return !arr.length ? arr :
strategies.filter((el) => arr.indexOf(el.strategy.category) > -1);
}
filterObj(['war', 'farming'])
DEMO
I have an object like this:
var obj = {
heroes: {
"1": {
label: "Spiderman"
},
"2": {
label: "Iron Man"
},
}
}
What I want to know it, whether there is an object e.g. 2 in obj.heroes.
I tried this, but it didn't work:
var name = "heroes"; //Will be a for-loop later
try {
if(name["2"] in obj)
console.log("There is an 2nd superhero!");
} catch(e) {console.log(e);}
..got only errors: "Cannot read property '2' of undefined"
I hope you are able to help me. Thanks
Try
if ("2" in obj[name]){
console.log("There is an 2nd superhero!");
}
But if you are trying to identify counts, it might be better if you used arrays
var obj = {
heroes: [
{label: "Spiderman"},
{label: "Iron Man"}
]
}
And check with
if (obj[name].length > 1) {
console.log("There is an 2nd superhero!");
}
You can do something like:
try {
console.log(obj.heroes["2"]);
} catch (e) {
console.log('nope :c');
}
However, it would be better to store heroes as an array:
var obj = {
heroes: [
{
label: 'Spiderman'
},
{
label: 'Ironman'
}
]
};
Using the array makes a little more sense since heroes consists of multiple hero objects.
If 2nd superhero doesn't exist the condition return false.
if(obj.heroes["2"])
console.log("There is an 2nd superhero!");
Or :
var count = 0;
for (var x in obj.heroes) {
if (obj.heroes.hasOwnProperty(x)) {
count++;
}
}
console.log("You see "+ count +" heroes.");
this code will hunt it down for you
var delve = function(object, property, dodge) {
if (!dodge) dodge = object;
for (var i in object) {
if (object[i] === dodge) continue;
if (typeof(object[i]) == typeof({})) this.delve(object[i], property, dodge)
if (i == property) console.log(object[i]);
}
}
delve(heroes,'2')
I am working with a JSON object which can have a property ids at any leaf. I want to traverse the object and find all of the instances of the ids property and store each id in a collection.
Mocked up JSON Object (the ids property could be at much deeper property locations).
{
"id": "b38a683d-3fb6-408f-9ef6-f4b853ed1193",
"foo": {
"ids": [
{
"id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
},
{
"id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
},
{
"id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
}
],
"baz": "super"
},
"bar": {
"ids": [
{
"id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
},
{
"id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
},
{
"id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
}
]
}
}
I am using the following code to traverse the above JSON.
var jsonFile = require('./file_test.json'); // the above in my local directory
function traverse(obj, ids) {
for (var prop in obj) {
if (typeof obj[prop] == "object" && obj[prop]) {
if (prop == 'ids') {
for (var i = obj[prop].length - 1; i >= 0; i--) {
ids.push(obj[prop][i]._id);
};
}
traverse(obj[prop], ids);
}
}
}
var ids = new Array();
traverse(jsonFile, ids);
console.log('ids', ids);
The above nets the following:
ids
[
'b68d0c8c-548e-472f-9b01-f25d4b199a71',
'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881',
'b68d0c8c-548e-472f-9b01-f25d4b199a71',
'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881'
]
While my code works I am not convinced that I am doing this the most efficient or best way. Is there a better way to find all instances of the ids property? Perhaps without passing in an array but returning one? Or setting up for a callback with an ids array?
If the data was actually a JSON string, and not a JavaScript object, you could have something like:
// assuming `json` is the data string
var ids = [];
var data = JSON.parse(json, function(key, value) {
if (key === "id")
ids.push(value);
return value;
});
See reviver on JSON.parse method.
what you have is fine, but this is a little shorter and uses the .map function:
var jsonFile = require('./file_test.json'); // the above in my local directory
function traverse(obj) {
var ids = [];
for (var prop in obj) {
if (typeof obj[prop] == "object" && obj[prop]) {
if (prop == 'ids') {
ids = obj[prop].map(function(elem){
return elem.id;
})
}
ids =ids.concat(traverse(obj[prop]));
}
}
return ids;
}
var ids =traverse(jsonFile);
console.log('ids', ids);
What you're basically trying to do is a tree search of this JSON object, am I right? So if we assume that ids is always a leaf then we do not need to traverse those nodes as we know they are at the leaf and will contain what we want.
Change the if {...} traverse to if {...} else {traverse}
If it is possible to change the data structure of ids to a list of strings instead of a list of objects then you will be able to save the iteration over the array and just merge it onto the ids array passed in, but it depends completely on the context and whether or not you can make this change!
Sorry I'm not of more help!
Assuming ES5 is available natively or via a shim:
function gimmeIds(obj) {
return Object.keys(obj||{})
.reduce(function(ids, key) {
if(key === 'ids') {
return ids.concat(obj[key].map(function(idObj) {
return idObj.id;
}));
}
if(obj[key] && typeof obj[key] == 'object') {
return ids.concat(gimmeIds(obj[key]));
}
return ids;
}, []);
}
Using object-scan this becomes very simple. Note that you can easily specify what is targeted (in this case **.ids[*].id)
// const objectScan = require('object-scan');
const data = { id: 'b38a683d-3fb6-408f-9ef6-f4b853ed1193', foo: { ids: [{ id: 'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' }, { id: 'd1cc529d-d5d2-4460-b2bb-acf24a7c5999' }, { id: 'b68d0c8c-548e-472f-9b01-f25d4b199a71' }], baz: 'super' }, bar: { ids: [{ id: 'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' }, { id: 'd1cc529d-d5d2-4460-b2bb-acf24a7c5999' }, { id: 'b68d0c8c-548e-472f-9b01-f25d4b199a71' }] } };
const findIds = (input) => objectScan(['**.ids[*].id'], { rtn: 'value' })(input);
console.log(findIds(data));
/* => [ 'b68d0c8c-548e-472f-9b01-f25d4b199a71',
'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881',
'b68d0c8c-548e-472f-9b01-f25d4b199a71',
'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' ]
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan