I Have a component which parse a json file on mounted phase.
The problem is :
when I click on a button , a send another GET method to get another json file and transmit it to my compoment.
the problem is that the component don't reload itself with the new props and my component display the old values
If someone know how to refresh components , here is my code
<template>
<div class="perturbo">
<div class="col-md-3 btnMenu">
<button v-for="item,index in perturboButtonData.button_list" type="button"
v-bind:style="'background:white'"
class="btn-lg btn-block myBtnClass"
#click="activeButton(index)">
{{item.label}}
</button>
</div>
<div class="col-md-9">
<div class="row">
<graphe
v-for="ana,index in perturboData.ANA"
:key="ana.id"
:data="ana"
:index="index"
type="number"
:timeSpec="perturboData.liste_dates">
</graphe>
</div>
<div class="row">
<graphe
v-for="dig,index in perturboData.DIG"
:key="dig.id"
:index="index"
:data="dig"
type="number"
isDigit="YES"
:timeSpec="perturboData.liste_dates">
</graphe>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import Graphe from './Graphe/Graphe.vue'
export default {
name: 'perturbo',
components : {
'graphe' : Graphe
},
data: function () {
return {
isActivated: false,
perturboData: {},
perturboButtonData: {}
}
},
methods: {
activeButton : function (index) {
console.log(this.perturboButtonData)
axios.get('./static/cgi/' + this.perturboButtonData.button_list[index].link)
.then((response) => {
this.perturboData = response.data;
this.isActivated = true
})
}
},
mounted : function () {
axios.get('./static/cgi/format_json_perturbo.json')
.then((response) => {
this.perturboButtonData = response.data;
})
}
}
</script>
Here is the code of my graphe component
<template>
<div class="graphe">
<vue-chart
:chart-events="chartEvents"
:columns="columns"
:rows="rows"
chart-type="LineChart"
:options="options">
</vue-chart>
</div>
</template>
<script>
export default {
name: 'graphe',
props: {
data: {},
timeSpec : Array,
index: Number,
type: String,
isDigit:String,
},
data: function () {
return {
chartEvents: {
'select': function() {
},
'ready': function() {
}
},
rows: [],
columns: [],
options: {
title: this.data.name,
hAxis: {
},
vAxis: {
ticks : []
},
width: 650,
height: 350,
curveType : 'function'
}
}
},
methods: {
normaliseData : function () {
for (let i = 0; i < this.timeSpec.length; i++) {
this.rows[i] = []
this.rows[i][0] = parseFloat(this.timeSpec[i])
}
this.columns[0] = { 'type': 'number', 'label': 'time' }
for (let i = 0; i < this.data.data.length; i++){
this.columns[i+1] = {'type': this.type ,'label': this.data.data[i].name}
}
for (let i = 0; i < this.timeSpec.length; i++) {
for (let y = 0; y < this.data.data.length; y++) {
this.rows[i][y+1] = parseFloat(this.data.data[y].data[i])
}
}
if (this.isDigit == "YES"){
this.digRow(this.rows)
for (let v = 0; v < this.data.data.length; v ++){
this.options.vAxis.ticks[v] = { v: v, f: this.data.data[v].name}
}
this.options.curveType = ''
}
},
digRow : function (rowTab) {
let newRow = []
let lengthMax = rowTab.length
let rowTmp = []
let index = 0
for (let i = 0; i < lengthMax; i ++){
rowTmp[index] = []
rowTmp[index][0] = rowTab[i][0]
for(let y = 1; y < rowTab[i].length; y ++){
rowTmp[index][y] = rowTab[i][y] + y - 1
}
if (i + 1 !== rowTab.length)
{
newRow = rowTmp[index].slice()
newRow[0] = rowTab[i+1][0]
rowTmp.splice(index+1,0,newRow)
index = index + 2
}
}
this.rows = rowTmp
}
},
mounted: function () {
// // pour les colones
this.normaliseData()
}
}
</script>
EDIT : I know where the problem is :
The data received from the parent is treated just once on the mounted function ! , that's why it doesn't reload on change
Should I use a watcher on props ? how can I do that
Instead of using a method to normalize the data, use a computed property for your rows, columns and options properties. This way, it will update automatically if any of the dependent properties change.
For example, your options property could be a computed property that looks like this:
computed: {
options() {
let options = {
title: this.data.name,
hAxis: {
},
vAxis: {
ticks : []
},
width: 650,
height: 350,
curveType : 'function'
};
if (this.isDigit == "YES"){
this.digRow(this.rows)
for (let v = 0; v < this.data.data.length; v ++){
options.vAxis.ticks[v] = { v: v, f: this.data.data[v].name}
}
options.curveType = ''
}
return options;
}
}
Now, whenever this.data, this.isDigit, or this.rows changes, the options property will update as well.
Your rows and columns properties would look like this:
rows() {
let rows = [];
for (let i = 0; i < this.timeSpec.length; i++) {
rows[i] = []
rows[i][0] = parseFloat(this.timeSpec[i])
for (let y = 0; y < this.data.data.length; y++) {
rows[i][y+1] = parseFloat(this.data.data[y].data[i])
}
}
return rows;
},
columns() {
let columns = [];
columns[0] = { 'type': 'number', 'label': 'time' }
for (let i = 0; i < this.data.data.length; i++) {
columns[i+1] = {'type': this.type ,'label': this.data.data[i].name}
}
return columns;
},
Your changed property won't force view update
To react to state changes, it’s usually better to use a computed
property or watcher.
Try this variant
watch: {
timeSpec(){
//do something
//this.normaliseData()
}
}
Related
I want remove Data of table 'filter_object' with related table 'filter_link' bevor starting the action of button onClickSync().
The 'filter_link' table contains two foreign key : product_id and object_id.
I tried to delete the data by id in a for loop, but this only deletes the data from the 'filter_object' table without related product. In addition it slows down the deletion when I have several data. Could help me please ?
import template from './sw-vehicles-list.html.twig';
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('sw-vehicles-list', {
template,
inject: ['repositoryFactory'],
data() {
return {
repository: null,
showAddButton: true,
isLoading: false,
object: null,
};
},
metaInfo() {
return {
title: this.$createTitle()
};
},
computed: {
filterObjectRepository() {
return this.repositoryFactory.create('filter_object');
},
filterLinkRepository() {
return this.repositoryFactory.create('filter_link');
},
productRepository() {
return this.repositoryFactory.create('product');
},
},
created() {
this.object = this.repositoryFactory.create('filter_object');
this.link = this.repositoryFactory.create('filter_link');
},
methods: {
async onClickSync() {
this.isLoading = true;
await this.removeData();
this.repository.search(new Criteria(), Shopware.Context.api).then((result) => {
if (result.length) {
var i;
var manufacturer = [];
for (i = 0; i < result.length; i++) {
manufacturer.push(result[i]['manufacturer']);
}
var manufacturerFilter = Array.from(new Set(manufacturer));
var j;
for ( j = 0; j < manufacturerFilter.length; j++) {
// some code
}
}
});
},
removeData() {
return this.filterObjectRepository.search(new Criteria(), Shopware.Context.api).then((result) => {
if (result.length) {
var i;
for (i = 0; i < result.length; ++i) {
this.filterObjectRepository.delete(result[i]['id'], Shopware.Context.api).then(this.loadObject);
}
return null;
}
});
},
loadObject() {
this.filterObjectRepository.search(new Criteria(), Shopware.Context.api).then((result) => {
this.result = result;
});
},
}
});
You can use the syncDeleted method of the repository to delete multiple records.
const ids = result.map(record => record.id);
this.filterObjectRepository.syncDeleted(ids, Shopware.Context.api)
If you want filter_link records to be deleted when deleting records from filter_object you'll have to set the foreign keys ON DELETE subclause to CASCADE.
ALTER TABLE `filter_link`
DROP FOREIGN KEY `fk.filter_link.object_id`;
ALTER TABLE `filter_link`
ADD CONSTRAINT `fk.filter_link.object_id` FOREIGN KEY (`object_id`) REFERENCES `filter_object` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
I´m trying to dispatch an object which is created in a computed.
I can´t get it to work as I´m fairly new to vue.js
I want to dispatch the object "updateObject" to the vuex-store.
Tried with setters but didn´t work. I think if I can set the "varia" object to the same object like "updateObject" then I could maybe dispatch it?
Hope somebody can help me.
Here is my code:
<template>
<div class="detail">
<b-row align-v="center"><b-button variant="success" #click="submit()">submit</b-button></b-row>
// some more code...
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
subID: '',
res: '',
showAlert: true,
varia: null
}
},
computed: {
...mapState([
'FA',
'Main',
'Sub',
'layouttype'
]),
getVariable: function (Sub, layouttype) {
const subID = this.layouttype.sub_id
var filterObj = this.Sub.filter(function (e) {
return e.sub_id === subID
})
console.log(filterObj)
return filterObj
},
updateObject: {
// getterfunction
get: function () {
var len = this.getVariable.length
var res = []
for (var i = 0; i < len; i++) {
if (i in this.getVariable) {
var val = this.getVariable[i].variable
res.push(val)
}
}
console.log(res)
var ergebnis = {}
res.forEach(key => {
if (this.FA[key]) {
ergebnis[key] = this.FA[key]
}
})
return ergebnis
},
// setterfunction
set: function (value) {
this.varia = value
}
}
},
methods: {
submit () {
this.$store.dispatch('sendData', this.ergebnis)
}
}
}
</script>
It tell´s me "this.ergebnis" is undefined
You can try it declaring "ergebnis" as global variable under data as
export default {
data () {
return {
subID: '',
res: '',
showAlert: true,
varia: null,
ergebnis : {}
}
},
computed: {
...mapState([
'FA',
'Main',
'Sub',
'layouttype'
]),
getVariable: function (Sub, layouttype) {
const subID = this.layouttype.sub_id
var filterObj = this.Sub.filter(function (e) {
return e.sub_id === subID
})
console.log(filterObj)
return filterObj
},
updateObject: {
// getterfunction
get: function () {
var len = this.getVariable.length
var res = []
for (var i = 0; i < len; i++) {
if (i in this.getVariable) {
var val = this.getVariable[i].variable
res.push(val)
}
}
console.log(res)
res.forEach(key => {
if (this.FA[key]) {
this.ergebnis[key] = this.FA[key]
}
})
return this.ergebnis
},
// setterfunction
set: function (value) {
this.varia = value
}
}
},
methods: {
submit () {
this.$store.dispatch('sendData', this.ergebnis)
}
}
}
Now ergebnis is accessible
export class EstimateForm extends React.Component<IEstimateFormProps,
IEstimateFormState> {
state: IEstimateFormState = {
cellUpdateCss: 'red',
toRow: null,
fromRow: null,
estimateList: null,
estimateItemList: [],
poseList: null,
levelList: null,
partList: null,
selectedEstimate: null,
totalEstimateItems: 0,
selectedIndexes: [],
totalEstimateAmount: 0,
grid: null,
projectId: 0,
};
constructor(props, context) {
super(props, context);
this.state.estimateList = this.props.estimateList;
}
rowGetter = i => {
const row = this.state.estimateItemList[i];
const selectRevison = this.state.selectedEstimate.revision;
if (row['pose.poseName']) {
const poseCode =
row['pose.poseName'].substring(row['pose.poseName'].lastIndexOf('[') + 1,
row['pose.poseName'].lastIndexOf(']'));
for (const pose of this.state.poseList) {
if (pose.poseCode === poseCode) {
row.pose = pose;
}
}
}
if (row['level.levelName']) {
const levelCode = row['level.levelName'].substring(
row['level.levelName'].lastIndexOf('[') + 1,
row['level.levelName'].lastIndexOf(']')
);
for (const level of this.state.levelList) {
if (level.levelCode === levelCode) {
row.level = level;
}
}
}
if (row['level.part.partName']) {
const partCode = row['level.part.partName'].substring(
row['level.part.partName'].lastIndexOf('[') + 1,
row['level.part.partName'].lastIndexOf(']')
);
for (const part of this.state.partList) {
if (part.partCode === partCode) {
row.part = part;
}
}
}
row.get = key => eval('row.' + key);
row.totalCost = (row.materialCost + row.laborCost) * row.amount;
const changeColor = {
backgroundcolor: 'red'
};
const all = document.getElementsByClassName('react-grid-Row') as
HTMLCollectionOf<HTMLElement>;
debugger; if (row.revision > selectRevison) {
for (let i = this.state.fromRow; i <= this.state.toRow; i++) {
all[i].style.color = 'red'; //HERE
}
return row;
}
}
handleGridRowsUpdated = ({ fromRow, toRow, updated }) => {
const rows = this.state.estimateItemList.slice();
for (let i = fromRow; i <= toRow; i++) {
const rowToUpdate = rows[i];
const updatedRow = update(rowToUpdate, { $merge: updated });
rows[i] = updatedRow;
}
this.setState({ estimateItemList: rows, fromRow: (fromRow), toRow: (toRow)
}, () => {
});
};
saveEstimateItems = () => {
if (this.state.selectedEstimate == null) {
toast.warn(<Translate
contentKey="bpmApp.estimateForm.pleaseSelectEstimate">Please select an
estimate</Translate>);
return;
}
render() {
return ()
}
I wanna to change the row color when the condition row.revision > this.state.selectedEstimate.revision . How can I prevent the change of this.color. However TypeError: Cannot read property 'style' of undefined get error but row color is not change. how can i solve this problem it is my first project in react and i dont know where is the problemThanks for your feedback guys.
Okay, so without the rest of the context because your pasted code is difficult to read and understand, the simplest reason for your issue is in this chunk:
const all = document.getElementsByClassName('react-grid-Row') as
HTMLCollectionOf<HTMLElement>;
debugger; if (row.revision > selectRevison) {
for (let i = this.state.fromRow; i <= this.state.toRow; i++) {
all[i].style.color = 'red'; //HERE
}
Essentially there's multiple things that could go wrong here, but most likely there are either no rows with that class on the page, or less than your this.state.fromRow, I see you've got the debugger in there, but you are missing a few things:
You aren't doing a null check on all to make sure you are finding something
You aren't checking whether all.length > this.state.fromRow
You aren't breaking the for loop if all.length < this.state.toRow
It's failing because all[i] doesn't exist, or there's no values:
all = [0, 1]
and you are looking for all[3] for example
Throw in those fallbacks and check what all is on page load and you should be able to figure it out.
I have two objects in my root - obj and newObj. I watch changes on my obj object with deep: true, and on changes I update newObj accordingly.
In my vue debugger, the newObj seems updated as expected, however the component doesn't perform the for loop count. Or if I try to {{ newObj }}, it logs only the first update.
I tried to re-create the issue on this Fiddle.
my html:
<div id="app">
<button #click="appendTo">Append</button>
<my-comp v-bind:new-obj="newObj"></my-comp>
</div>
and vue:
new Vue({
el: '#app',
data: {
obj: {},
newObj: {}
},
methods: {
appendTo() {
if (typeof this.obj[1] === 'undefined') {
this.$set(this.obj, 1, {})
}
var randLetter = String.fromCharCode(Math.floor(Math.random() * (122 - 97)) + 97);
this.$set(this.obj[1], randLetter, [ [] ])
}
},
watch: {
obj: {
handler(obj) {
var oldKeys = Object.keys(obj)
var newKeys = Object.keys(this.newObj);
var removedIndex = newKeys.filter(x => oldKeys.indexOf(x) < 0 );
for (var i = 0, len = removedIndex.length; i < len; i++) {
delete this.newObj[removedIndex[i]]
}
oldKeys.map((key) => {
if (this.newObj.hasOwnProperty(key)) {
var newInnerKeys = Object.keys(this.newObj[key]);
var oldInnerKeys = Object.keys(obj[key]);
var additions = oldInnerKeys.filter(x => newInnerKeys.indexOf(x) < 0);
for (var i = 0, len = additions.length; i < len; i++) {
// here
this.$set(this.newObj[key], additions[i], [ [] ]);
}
var deletions = newInnerKeys.filter(x => oldInnerKeys.indexOf(x) < 0);
for (var i = 0, len = deletions.length; i < len; i++) {
delete this.newObj[key][deletions[i]]
}
} else {
this.newObj[key] = {}
for (var innerKey in obj[key]) {
this.$set(this.newObj, key, {
[innerKey]: [ [] ]
});
}
}
console.log(obj);
console.log(this.newObj)
});
},
deep: true
}
}
})
Vue.component('my-comp', {
props: ['newObj'],
template: `
<div>
<div v-for="item in newObj">
test
</div>
</div>
`
})
Your data newObj has a getter and setter defined by vue. When the setter is called, the UI is re-rendered. The setter is triggered when you change the reference of newObj, not when you change its value. I mean:
this.newObj = {} // triggered
this.newObj['key'] = 'value' // not triggered
You can add a deep watcher on the property this.newObj. Or change its reference with a trick:
this.newObj = Object.assign({}, this.newObjec);
which create a copy of the object newObject.
Here is the fiddle updated.
https://jsfiddle.net/749nc5d2/
I understand from the caveats portion of the Vue docs that updating a value in an array in the following manner will not work:
this.arr[idx] = newVal
and that one should use splice(). I am using a 2D array to store grid data, and I am having a difficult time updating the value when a cell in the grid is clicked.
Here is my template:
<tr
v-for="(row, rowKey, index) in grid"
:key="rowKey">
<th
class="row-col-label"
>{{rowKey+1}}</th>
<td
v-for="(col, colKey, index) in row"
:key="colKey"
#click="selectCell(rowKey, colKey)"
:class="{'selected' : cellSelected(rowKey, colKey)}"
>
{{col}}
</td>
</tr>
And here is the relevant code for the Vue component:
created () {
this.initColHead()
this.createSpreadSheet()
},
data () {
return {
selected: '',
grid: [],
colHead: [' '],
isSelected: false
}
},
methods: {
initColHead () {
this.colHead.push(...'ABC'.split(''))
},
createSpreadSheet () {
for (let i = 0; i <= 2; i++) {
this.grid[i] = []
for (let j = 0; j <= 2; j++) {
this.grid[i][j] = false
}
}
},
selectCell (row, col) {
this.isSelected = true
console.log(`row ${row} col ${col}`)
this.grid[row].splice(col, 1, true)
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
console.log(this.grid[i][j])
}
}
},
cellSelected (row, col) {
return (this.grid[row][col] === true)
}
}
So I am attempting to add a true value to the cell that is click at the given row col locations provided in the my selectCell method. However, the data in my grid is not updated to reflect the newly added value. How exactly do I update values in a multidimensional array in Vue?
One method that works:
selectCell (row, col) {
//make a copy of the row
const newRow = this.grid[row].slice(0)
// update the value
newRow[col] = true
// update it in the grid
this.$set(this.grid, row, newRow)
},
Here is an example.
console.clear()
new Vue({
el: "#app",
created() {
this.initColHead()
this.createSpreadSheet()
},
data() {
return {
selected: '',
grid: [],
colHead: [' '],
isSelected: false
}
},
methods: {
initColHead() {
this.colHead.push(...'ABC'.split(''))
},
createSpreadSheet() {
for (let i = 0; i <= 2; i++) {
this.grid[i] = []
for (let j = 0; j <= 2; j++) {
this.grid[i][j] = false
}
}
},
selectCell(row, col) {
const newRow = this.grid[row].slice(0)
newRow[col] = true
this.$set(this.grid, row, newRow)
},
cellSelected(row, col) {
return (this.grid[row][col] === true)
}
}
})
.selected {
background-color: green;
}
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<table>
<tr v-for="(row, rowKey, index) in grid" :key="rowKey">
<th class="row-col-label">{{rowKey+1}}</th>
<td v-for="(col, colKey, index) in row" :key="colKey" #click="selectCell(rowKey, colKey)" :class="{'selected' : cellSelected(rowKey, colKey)}">
{{col}}
</td>
</tr>
</table>
</div>
If I think of something better I'll update later.
The difficulty is that you're building the array in a way that Vue does not make its rows reactive. You could build the array and then assign it to the data item as a whole so that Vue would make it reactive, or you can build the array (at last the rows) using push, which will make them reactive. Then you can modify individual elements using splice. Modifying Bert's example:
console.clear()
new Vue({
el: "#app",
created() {
this.initColHead()
this.createSpreadSheet()
},
data() {
return {
selected: '',
grid: [],
colHead: [' '],
isSelected: false
}
},
methods: {
initColHead() {
this.colHead.push(...'ABC'.split(''))
},
createSpreadSheet() {
for (var i = 0; i <= 2; i++) {
this.grid.push([]);
for (var j = 0; j <= 2; j++) {
this.grid[i].push(false);
}
}
},
selectCell(row, col) {
this.grid[row].splice(col, 1, true);
},
cellSelected(row, col) {
return (this.grid[row][col] === true)
}
}
})
.selected {
background-color: green;
}
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<table>
<tr v-for="(row, rowKey, index) in grid" :key="rowKey">
<th class="row-col-label">{{rowKey+1}}</th>
<td v-for="(col, colKey, index) in row" :key="colKey" #click="selectCell(rowKey, colKey)" :class="{'selected' : cellSelected(rowKey, colKey)}">
{{col}}
</td>
</tr>
</table>
</div>