Why can't I access props within my component? - javascript

I've copied the Grid Component Example into a single-file component (Grid.vue). Within that component, I'm not able to access the columns prop. console.log(this.columns) always prints: [__ob__: Observer] to the log. Can someone tell me why? This works fine in their example on the page and in JSFiddle.
Here's my Grid.vue file:
<script>
export default {
name: 'grid',
props: {
data: Array,
columns: Array,
filterKey: String
},
data: function() {
var sortOrders = {}
console.log(this.columns)
this.columns.forEach((column) => {
sortOrders[column] = 1
});
return {
sortCol: '',
sortOrders: sortOrders
}
},
computed: {
filteredData: function () {
var sortCol = this.sortCol
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortCol] || 1
var data = this.data
if (filterKey) {
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortCol) {
data = data.slice().sort((a, b) => {
a = a[sortCol]
b = b[sortCol]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortCol = key
console.log(this.sortOrders[key])
this.sortOrders[key] = this.sortOrders[key] * -1
console.log(this.sortOrders[key])
}
},
created() {
},
mounted() {
// var app = this
},
}
</script>
I'm using this component within another component like so:
<template>
<div>
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<grid :data="things" :columns="thingColumns" :filterKey="searchQuery"></grid>
</div>
</template>
<script>
import Grid from './Grid.vue';
export default {
name: 'things-grid',
data: function() {
return {
things: [],
thingColumns: [],
searchQuery: ''
}
},
mounted() {
var app = this
app.things = [
{id: 1, this: 'this 1', that: 'that 1', thing: 'thing 1'},
{id: 2, this: 'this 2', that: 'that 2', thing: 'thing 2'},
{id: 3, this: 'this 3', that: 'that 3', thing: 'thing 3'},
{id: 4, this: 'this 4', that: 'that 4', thing: 'thing 4'},
{id: 5, this: 'this 5', that: 'that 5', thing: 'thing 5'},
]
app.thingColumns = [
'this', 'that', 'thing'
]
app.searchQuery = ''
},
components: { Grid }
}
</script>

In:
<grid :data="things" :columns="thingColumns" :filterKey="searchQuery"></grid>
The value of this.thingColumns is passed as :columns when mounting.
Thus, the console.log(this.columns) inside Grid.vue/data() prints when it is mounting.
And when it is mounting, thingColumns is empty in the parent:
data: function() {
return {
things: [],
thingColumns: [], // initially empty
searchQuery: ''
}
},
mounted() {
var app = this
// ...
app.thingColumns = [ // this code only runs after it is mounted
'this', 'that', 'thing'
]
// ...
},
Since the console.log(this.columns) inside Grid.vue/data() prints when it is mounting, that is, before it is mounted, it prints an empty array:
[__ob__: Observer] // this is an empty array, the __ob__ thing is related to Vue internals
Because, well, parent's thingColumns will only have data after the mounted() hook executes.
And since it is a reactive array, when you update it, it will update the child grid component as well.
Solution:
Move the property initalization code from mounted() to created():
created() { // was mounted()
var app = this
// ...
app.thingColumns = [
'this', 'that', 'thing'
]
// ...
},
This will initialize the data sooner and make it available in time for the console.log() in the child to pick it up.

Related

Bootstrap-Vue table not updating unless debug is printed

I'm trying to add some Ctrl + click functionality to a Bootstrap-Vue table in order to select multiple rows. To check if it behaves correctly, I print out some debug info on the page and to the console.
<template>
<div>
<b-table striped hover :fields="fields" :items="items" #row-clicked="rowClick"></b-table>
Debug selected: {{selected.map(item => item.name).join(', ')}}
</div>
</template>
<script>
export default {
name: 'Table',
data () {
return {
fields: [
{key: 'id'},
{key: 'name'}
],
items: [
{id: '1', name: 'Row 1'},
{id: '2', name: 'Row 2'},
{id: '3', name: 'Row 3'},
{id: '4', name: 'Row 4'},
{id: '5', name: 'Row 5'}
],
selected: []
}
},
methods: {
rowClick(item, index, event) {
if(!event.ctrlKey) {
this.selected.forEach(selectedItem => selectedItem._rowVariant = null);
this.selected = [];
}
if(event.ctrlKey && this.selected.includes(item)) {
item._rowVariant = null;
this.selected = this.selected.filter(selectedItem => selectedItem !== item);
} else {
item._rowVariant = 'info';
this.selected.push(item);
}
console.log('Debug selected: ' + this.selected.map(item => item.name).join(', '));
}
}
}
</script>
This works fine, but when I remove the debug line in the template, the table is no longer visually updated and it seems _rowVariant has no effect, even though the console log indicates that the rows are still selected properly.
EDIT: Here is a fiddle: https://jsfiddle.net/aznan/24enw63a/39/
Strange! I could answer it better if you had a fiddle or plnk setup. But still this could be due to a lag.
Try wrapping the function withtin setTimeout like
rowClick(item, index, event) {
setTimeout(function(){
if(!event.ctrlKey) {
this.selected.forEach(selectedItem => selectedItem._rowVariant = null);
this.selected = [];
}
if(event.ctrlKey && this.selected.includes(item)) {
item._rowVariant = null;
this.selected = this.selected.filter(selectedItem => selectedItem !== item);
} else {
item._rowVariant = 'info';
this.selected.push(item);
}
});
}

Javascript Tree Structure to Array conversion

I am working on a treeview and I build a simple Node-Class: It consists of a name and an array of children:
class Node {
constructor(name, childNodes) {
this.name = name;
this.childNodes = childNodes;
}
}
Now my aim is to create a function that returns an object like this:
var tree = [
{
text: 'Parent 1',
nodes: [
{
text: 'Child 1',
nodes: [
{
text: 'Grandchild 1'
}
]
},
{
text: 'Child 2'
}
]
},
{
text: 'Parent 2'
},
];
I tried using a recursive method. It starts with an empty array and adds children until there are no more left:
function recTreeview(currentNode, treeview) {
var tempChildren = [];
currentNode.childNodes.forEach(child => {
tempChild.push(recTreeview(child, treeview));
});
return treeview.push({
text: currentNode.name,
nodes: tempChildren
})
}
But something with the recursive Treeview Function has to be wrong. When I create the tree and try to open it in the chrome dev console, it just shows a "5" instead of something like (5) [{…}, {…}, {…}, {…}, {…}]. What did I do wrong?
tree = recTreeview(parent, []);
tree;
You are returning the result of the push and not the actual treeview.
As per the Array.prototype.push() docs
Return value
The new length property of the object upon which the method was called.
So instead of return treeview.push(...) do treeview.push(...) and then return treeview
function recTreeview(currentNode, treeview) {
var tempChildren = [];
currentNode.childNodes.forEach(child => {
tempChild.push(recTreeview(child, treeview));
});
treeview.push({
text: currentNode.name,
nodes: tempChildren
});
return treeview;
}

Update part of model from scope selected object

Ok, I have model with one property(provider) as object. It can change at all.
There is example, where I change provider. There can be any parametrs, image can has dpi, json can has another parametr.
So, when I select anoter provider, how to merge model property(provider) and updated provider?
this.providerWasChange = function() {
// here I should update model with provider parametrs(update full object)
$scope.provider
}
https://jsfiddle.net/77z165uj/11/
Hm,
var model = {
id: '1',
name: '',
childModels: [{
id: '1.1',
name: 'item1',
provider: {
name: 'imageProvider'
options: {
transparent: false,
dpi: 96
}
}
}, {
id: '1.2',
name: 'item2'
provider: {
name: 'jsonProvider'
options: {
uppercase: true,
}
}
}]
}
$scope.providers = [{
name: 'jsonProvider',
displayNmae: "jsonProvider",
options:{
uppercase:$scope.providerOptions,
}
}, {
name: 'imageProvider',
displayNmae: "imageProvider",
options:{
transparent:$scope.transparent,
dpi::$scope.dpi
}
}];
_changeProvider = function(data) {
if (data !== null) {
for (var i = 0; i < $scope.providers.length; i++) {
if ($scope.providers[i].name === data.name) {
$scope.providers[i].options = data.options
return $scope.providers[i];
}
};
}
}
I'm looking for a fuction or angular method, that set chosen provider blank with setted options from model back. For example, I'd like to change provider of item 2 to image provider(old values(if there is coincidence) should rewrites to model(item2), other should be deleted, and new - setted)

ImmutableJS - update value in a List

I have a Map like this (in ImmutableJS):
{arrayOfValues: [
{one: {inside: 'first in array'}},
{one: {inside: 'second in array'}}
]}
And I want to update the value "inside" in the second entry in the "arrayOfValues" array. How can I do it? This is what I have now and it says "Uncaught Error: invalid keyPath"
theMap.update('arrayOfValues',(list)=>{
return list.setIn([1,'one','inside'],'updated value');
})
I also tried directly this and it didn't work:
theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');
After several hours of looking for the solution, I appreciate any help. Thank you.
What you are doing is correct (see this JSBin).
const orig = Immutable.fromJS({
arrayOfValues: [
{ one: { inside: 'first in array' } },
{ one: { inside: 'second in array' } },
]
});
const updated = orig.setIn(['arrayOfValues', 1, 'one', 'inside'], 'updated value');
console.log(updated.toJS());
// {
// arrayOfValues: [
// { one: { inside: 'first in array' } },
// { one: { inside: 'second in array' } },
// ]
// }
When you call orig.setIn(), it doesn't modify orig directly. That's the whole purpose of this Immutable library. It doesn't mutate the existing data but creates a new one from the existing one.
Your setIn example works as you should see in this plunkr:
http://plnkr.co/edit/1uXTWtKlykeuU6vB3xVO?p=preview
Perhaps you are assuming the value of theMap will be changed as a result of the setIn?
As these structures are immutable, you must capture the modified value in a new variable as var theMap2 = theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');
activePane is the index of Object in Array(List) that I had to modify
case CHANGE_SERVICE:
var obj = {
title: '1212121 Tab',
service: '',
tagName: '',
preDefinedApi: '',
methodType: '',
url: '',
urlParams: [{
label: '',
name: '',
value: '',
}],
headers: [{
label: '',
name: '',
value: '',
}],
};
var activePane = state.get('activePane');
var panes = state.setIn(['panes', activePane, 'service'], action.val);
return state.setIn(['panes', activePane, 'service'], action.val);

Recursive Function Causing Overflow

I am trying to write a recursive function in JavaScript. My function needs to search a tree of items. I have created a JSFiddle. When I run the JavaScript in Chrome, I get an error that says:
RangeError: Maximum call stack size exceeded
I assume this means that I'm not returning my value at the correct time. However, I continue to review the function and it looks correct to me. What am I doing wrong?
var sitemap = [
{
name: 'dashboards', children: [
{ name: 'dashboard 1', route: '/dashboards/dashboard1', html: '' }
]
},
{
name: 'objects', children: [
{ name: 'players', route: '/objects/players', html: '/objects/players.html' },
{ name: 'teams', route: '/objects/teams', html: '/objects/teams.html' },
{ name: 'coaches', route: '/objects/coaches', html: '/objects/coaches.html' },
{ name: 'cities', children: [
{ name: 'Chicago', route: '/cities/chicago',
html: '/objects/cities/chicago.html' },
{ name: 'Philadelphia', route: '/cities/philadelphia', html: '/objects/cities/philadelphia.html' }
]},
]
}
];
var getFromSitemap = function (path, entries) {
var sitemapItem = null;
if (entries) {
angular.forEach(sitemap, function (entry, key) {
if (entry.hasOwnProperty("children")) {
sitemapItem = getFromSitemap(path, entry.children);
} else if (entry.route === path) {
sitemapItem = entry;
}
});
}
return sitemapItem;
};
var getItem = function() {
var item = getFromSitemap('/cities/chicago', sitemap);
console.log(item);
}
Thank you!
You are calling foreach on the same object (sitemap) everytime:
angular.forEach(sitemap, function ...
It seems like you want to be calling it on entries recursively
angular.forEach(entries, function ....

Categories