Component rendered twice are sharing states and they shouldn't share them - javascript

The problem is that two views (same component) load same info (each one is a furnace, MainFurnaceView), an they should be loading different one. At first render (it can be seen for some miliseconds) they render different info, so it's okay, but later both views are showing the same info, after a second unwanted render. I think they're sharing the states between them.
I've got a MainFurnaceView element rendered twice in the MainScreen, the only property I give to it is furnace, on the first view, furnace={'forge'}, on the second one furnace={'preheat'}. Because of this property they should be loading different info.
On the MainFurnaceView, it logs the furnace prop correctly in the constructor, and also takes well info from AsyncStorage (called on constructor also). On render, at first, each view logs info of the wanted furnace, but later it loads and logs info of the 2nd furnace on both views.
Code:
MainScreen
<MainFurnaceView
//Tried with and without both onRef
//onRef={(ref) => (this.Option = ref)}
furnace={"forge"}/>
<MainFurnaceView
//onRef={(ref) => (this.Option = ref)}
furnace={"preheat"}/>
MainFurnaceView
constructor(props) {
super(props);
console.log('Constructor:', this.props.furnace);//Logs well
this.state = {
//Pieces to use at furnace view, tried with Object.assign and directly without it
furnacePieces:
this.props.furnace === "forge"
? Object.assign(pieces.forge, {})
: Object.assign(pieces.preheat, {}),
};
this.getData()/*Call to AsyncStorage here*/;}
getData = async () => {
//Get data (data) from key and save it as state
try {
let furnace;
if (this.props.furnace === "forge")
furnace = await AsyncStorage.getItem(consts.KEY_FORGE_DIMENSIONS);
else furnace = await AsyncStorage.getItem(consts.KEY_PREHEAT_DIMENSIONS);
furnace = JSON.parse(furnace);
console.log("getData: ", this.props.furnace, furnace);
if (typeof furnace[0].r === "number") {
this.setState({
furnaceRows: furnace[0].r,
furnaceCols: furnace[0].c,
furnaceHeights: furnace[0].h,
});
} else {
console.warn("not found");
this.setState({
furnaceRows: 3,
furnaceCols: 3,
furnaceHeights: 1,
});
}
} catch (e) {
// error reading value
console.log(e);
}
};
I call console.log('Render: ', this.props.furnace) on the render and there I also log the property furnace.
Log:
Constructor: forge
Render: forge
Constructor: preheat
Render: preheat
getData: forge Array [
Object {
"c": 4,
"h": 1,
"r": 6,
},
]
Render: preheat
getData: preheat Array [
Object {
"c": 4,
"h": 1,
"r": 4,
},
]
Render: preheat
What I understand with this log is that the first view is created and rendered, and then the second view is created and rendered, but later both reload with the property furnace as 'preheat' instead of one with 'forge' and the other with 'preheat'.
UPDATE:
If I don't call getData on constructor, views aren't re-rendered, so the info for each furnace is taken well. The problem now is that I need to get those dimensions from AsyncStorage, and idk how to do it without, as getting that info makes a new render and the prop furnace is changed.
BUT
If I make anything to re-render the mainScreen with both mainFurnaceView's (for example open another modal and close it) it renders on both views the 'preheat' info.

Related

chartjs in react useEffect keeps on creating new charts?

I have this extremely odd issue.. every single time I try and use a chartjs object, it keeps on creating itself on a new object (new id). The initial load never works, and I have to refresh. Everytime I switch from unmounting this component to mounting, when I console.log the chart instance it's always incrementing in id. I'm confused as what's going wrong here.
here is a basic snipped of my code:
useEffect(() => {
if (chartContainer && chartContainer.current) {
const newChartInstance = new Chartjs(chartContainer.current!, chartConfig);
removeData(newChartInstance);
if (newChartInstance) {
setIsClear(true);
setChartInstance(newChartInstance);
return () => {
removeData(newChartInstance);
newChartInstance.destroy();
if (chartInstance) chartInstance.destroy();
}
}
};
}, [chartContainer]);
useEffect(() => {
if (chartInstance && delay) {
const intervalUpdate = setInterval(() => {
console.log("updating");
console.log(chartInstance);
chartInstance.update();
}, delay);
return () => clearInterval(intervalUpdate);
}
}, [chartInstance, delay])
return (
<div className={styles.chartContainer}>
<canvas ref={chartContainer} height={"140"}/>
</div>
);
On the initial load a chart is created, and I'm adding data to it. But I believe 2nd one gets created and that's holding no data & is what is being display on the canvas. I know there's two somehow because because when I log I get an id of 1, instead of 0.
When I console.log() chart instance, on the very first initial load is always:
Chart {id: 1, ctx: CanvasRenderingContext2D, canvas: canvas.chartjs-render-monitor, config: {…}, width: 1024, …}
Which I believe is why my chart is empty. When I refresh it goes back to:
Chart {id: 0, ctx: CanvasRenderingContext2D, canvas: canvas.chartjs-render-monitor, config: {…}, width: 1024, …}
which properly displays the data.

For sibling communication between many identical components, how should I store the data in the lowest-common ancestor?

Background:
I'm a Python/Vue developer; I've been using Vue since 2016.
I have a client who runs a weight loss / meal planning business: clients pay her to prepare weekly single-page PDF menus that tell them (the clients) exactly what to eat for breakfast, lunch, and dinner of every day of the week. (image of an example menu)
Each meal is shown as a list of ingredients.
Right now she's preparing these menus in Excel, and she hired me to reproduce and extend the functionality of what she has in Excel, but in a Python/Vue app.
The app I'm building for her has many "pages" ("top-level" components) to allow her to add/modify/delete objects like clients, ingredients, and recipes (image), but the most complicated part of the UI is the component in which she can define the meals for every meal of every day of the week (image). That component is named WeeklyMenu.vue.
WeeklyMenu.vue itself contains seven DailyMenu.vue children, one for each day of the week (Monday, Tuesday, etc.). (image)
Each DailyMenu.vue component itself contains four Meal.vue components, one for each of four meal types: Breakfast, Lunch, Dinner, and Snacks. (image)
Important: At the moment, the DailyMenu.vue and Meal.vue components themselves contain their data rather than accessing it from the Vuex store.
For example, the list of ingredients for each meal is contained within the Meal.vue component as a mealIngredients variable within the component's data attribute. (image)
Side-note: This means that there are lots of HTTP requests being sent to the back-end when the page loads as all of the meals are requesting their own data, rather than a single request being sent via a Vuex action (for example). This seems like it can't be best practice.
The problem:
The problem is that she is now asking me to add features in which a change to the data in one subcomponent should update the data in a different subcomponent.
For example, she wants the app to work so that when she has the same recipe in several different Meals of the week, then a change to an ingredient in one of the meals will propagate to the other meals that have the same recipe. (image explanation)
My question:
What is the best practice for handling a situation like this? Should I move the ingredient data into the Vuex store or (in the same vein) the lowest-common-ancestor WeeklyMenu.vue component? If so, how exactly should it work? Should there be a separate variable for each meal? Or should I have an object that contains data for all of the different meals? If I use a single object, do I need to worry that a watcher on that object in the Meal.vue component would be triggering even when a change was made to a different meal's data?
If I store all the meal ingredients in separate variables, I would need to pass all of those to every meal (so every meal would need to receive every other meal's ingredients as separate props). So that doesn't seem like the right way to go.
If a user is making a particular change to a particular meal, how would I only have the other meals with the same name react?
Related links:
Communication between sibling components in VueJs 2.0
I'm looking into whether it would make sense to move the ingredient data up to the level of the WeeklyMenu.vue component as described in the "Lowest Common Ancestor" approach (here and here).
Simplified example of the situation I'm trying to handle:
Without Vuex: https://codepen.io/NathanWailes/pen/zYBGjME
Using Vuex: https://codepen.io/NathanWailes/pen/WNxWxWe
With everything working (including the state being kept in Vuex) except the propagation: https://codepen.io/NathanWailes/pen/KKMYNVZ
Yes, problem domain seems complex enough to more than justify use of Vuex. I would not go with keeping data in components and sharing by props - that doesn't scale well
Keep each Recipe as an object in single object recipes - you don't need to worry about watchers. If one particular Recipe object will change, Vue will re-render only components using same Recipe object (and if done properly you don't even need watchers for that)
Create a "weekly menu" object inside the store
In leaf nodes (Meals) of that object just use some kind of reference (by name or unique ID if you have one) into recipes. As a result multiple Meal.vue components on a menu will use same object in the store and update automatically
I ended up getting it working in a simple example in CodePen, which I'm going to use as a guide when trying to get it working on the actual site.
The summary of my findings with this solution is, "Vue will actually update when the nested entries of a Vuex state object are updated; you don't need to worry about it not detecting those changes. So it's OK to just keep all the data in a single big Vuex store object when you have many duplicate sibling components that need to react to each other."
Here's the CodePen: https://codepen.io/NathanWailes/pen/NWRNgNz
Screenshot
Summary of what the CodePen example does
The data used to populate the menu all lives in the Vuex store in a single weeklyMenu object, which has child objects to break up the data into the different days / meals.
The individual meals have computed properties with get and set functions so that it can both get changes from the store and also update the store.
The DailyMenu and WeeklyMenu components get their aggregate data by simply having computed properties that iterate over the Vuex weeklyMenu object, and it "just works".
I have same-named meals update to match each other by iterating over the meals in the Vuex mutation and looking for meals with the same "Ingredient Name".
The code
HTML
<html>
<body>
<div id='weekly-menu'></div>
<h3>Requirements:</h3>
<ul>
<li>Each row should have all the numbers in it summed and displayed ('total daily calories').</li>
<li>The week as a whole should have all the numbers summed and displayed ('total weekly calories').</li>
<li>If two or more input boxes have the same text, a change in one numerical input should propagate to the other same-named numerical inputs.</li>
<li>Ideally the data (ingredient names and calories) should be stored in one place (the top-level component or a Vuex store) to make it more straightforward to populate it from the database with a single HTTP call (which is not simulated in this example).</li>
</ul>
</body>
</html>
JavaScript
const store = new Vuex.Store(
{
state: {
weeklyMenu: {
Sunday: {
Breakfast: {
name: 'aaa',
calories: 1
},
Lunch: {
name: 'bbb',
calories: 2
},
},
Monday: {
Breakfast: {
name: 'ccc',
calories: 3
},
Lunch: {
name: 'ddd',
calories: 4
},
}
}
},
mutations: {
updateIngredientCalories (state, {dayOfTheWeekName, mealName, newCalorieValue}) {
state.weeklyMenu[dayOfTheWeekName][mealName]['calories'] = newCalorieValue
const ingredientNameBeingUpdated = state.weeklyMenu[dayOfTheWeekName][mealName]['name']
for (const dayOfTheWeekName of Object.keys(state.weeklyMenu)) {
for (const mealName of Object.keys(state.weeklyMenu[dayOfTheWeekName])) {
const mealToCheck = state.weeklyMenu[dayOfTheWeekName][mealName]
const ingredientNameToCheck = mealToCheck['name']
if (ingredientNameToCheck === ingredientNameBeingUpdated) {
mealToCheck['calories'] = newCalorieValue
}
}
}
},
updateIngredientName (state, {dayOfTheWeekName, mealName, newValue}) {
state.weeklyMenu[dayOfTheWeekName][mealName]['name'] = newValue
}
}
}
)
var Meal = {
template: `
<td>
<h4>{{ mealName }}</h4>
Ingredient Name: <input v-model="ingredientName" /><br/>
Calories: <input v-model.number="ingredientCalories" />
</td>
`,
props: [
'dayOfTheWeekName',
'mealName'
],
computed: {
ingredientCalories: {
get () {
return this.$store.state.weeklyMenu[this.dayOfTheWeekName][this.mealName]['calories']
},
set (value) {
if (value === '' || value === undefined || value === null) {
value = 0
}
this.$store.commit('updateIngredientCalories', {
dayOfTheWeekName: this.dayOfTheWeekName,
mealName: this.mealName,
newCalorieValue: value
})
}
},
ingredientName: {
get () {
return this.$store.state.weeklyMenu[this.dayOfTheWeekName][this.mealName]['name']
},
set (value) {
this.$store.commit('updateIngredientName', {
dayOfTheWeekName: this.dayOfTheWeekName,
mealName: this.mealName,
newValue: value
})
}
}
}
};
var DailyMenu = {
template: `
<tr>
<td>
<h4>{{ dayOfTheWeekName }}</h4>
Total Daily Calories: {{ totalDailyCalories }}
</td>
<meal :day-of-the-week-name="dayOfTheWeekName" meal-name="Breakfast" />
<meal :day-of-the-week-name="dayOfTheWeekName" meal-name="Lunch" />
</tr>
`,
props: [
'dayOfTheWeekName'
],
data: function () {
return {
}
},
components: {
meal: Meal
},
computed: {
totalDailyCalories () {
let totalDailyCalories = 0
for (const mealName of Object.keys(this.$store.state.weeklyMenu[this.dayOfTheWeekName])) {
totalDailyCalories += this.$store.state.weeklyMenu[this.dayOfTheWeekName][mealName]['calories']
}
return totalDailyCalories
}
}
};
var app = new Vue({
el: '#weekly-menu',
template: `<div id="weekly-menu" class="container">
<div class="jumbotron">
<h2>Weekly Menu</h2>
Total Weekly Calories: {{ totalWeeklyCalories }}
<table class="table">
<tbody>
<daily_menu day-of-the-week-name="Sunday" />
<daily_menu day-of-the-week-name="Monday" />
</tbody>
</table>
</div>
</div>
`,
data: function () {
return {
}
},
computed: {
totalWeeklyCalories () {
let totalWeeklyCalories = 0
for (const dayOfTheWeekName of Object.keys(this.$store.state.weeklyMenu)) {
let totalDailyCalories = 0
for (const mealName of Object.keys(this.$store.state.weeklyMenu[dayOfTheWeekName])) {
totalDailyCalories += this.$store.state.weeklyMenu[dayOfTheWeekName][mealName]['calories']
}
totalWeeklyCalories += totalDailyCalories
}
return totalWeeklyCalories
}
},
components: {
daily_menu: DailyMenu
},
store: store
});

Working with Vuex with Vue and displaying data

This is kind of a long explanation of an issue that I'm having on a personal project. Basically, I want to set a data property before my page loads when I read in data from a CSV file using D3.JS. I almost have it done but running into a small issue. Please read on to get more detail.
Basically, when the user comes to a page in my application, I want to display weather graphs. Like I said, I'm using D3.js to read in the data and created an action to do that. It works perfectly fine-I can console.log the data and I know its been read. However, in my vue instance I have a data property, which would hold the data set like this:
data() {
return {
name: this.$store.state.name
weatherData: this.$store.state.yearData
}
}
I then want to ensure that the weatherData is filled, with data from the csv file so I display it on the page like this:
<p>{{ weatherData }}</p>
Nothing special here. When the page loads, weatherData is blank. But I have a beforeMount life cycle hook and if I comment out the only line in it then it will display the data. If I then refresh the page, fire the action to get the data and then uncomment out the line in the beforeMount hook then the data appears! So before I continue this is my full code for the store:
export const store = new Vuex.Store({
state: {
name: 'Weather Data'
yearData: []
},
getters: {
},
mutations: {
setYearData(state, data) {
state.yearData = data
}
},
actions: {
getYearData: ({commit}) => {
d3.csv("../src/components/data/alaska.csv")
.then(function(data){
let yearData = []
for (let i = 0; i < data.length; i++){
let day = data[i].AKST
yearData.push(day)
}
//console.log(yearData)
commit('setYearData', yearData)
})
}
})
Here are parts of the vue file: The template:
<p>{{ weatherData }}</p>
The Vue Intance:
export default {
name: 'Weather',
data() {
return {
name: this.$store.state.name,
weatherData: this.$store.state.yearData
}
},
methods: {
...mapActions([
'getYearData'
])
},
beforeMount(){
this.$store.dispatch('getYearData') //(un)Commenting out this line will make my data appear
}
}
Page when it loads: Notice empty array:
Then either comment out or comment the one line in the beforeMount hook and get this: THE DATA!!!
Again, my end goal is to have the action called and the data set before the page finishes loading. Finally, I know that I don't need VUEX but this project is further helping me understand it. Any guidance on why this is happening would be great.
use mapState instead of putting your data in the data object, which sometimes being late on updating the template.
just make your Vue instance to look like:
import {mapState} from 'vuex'
export default {
name: 'Weather',
data() {
return { }
},
computed:{
...mapState({
name: state=>state.name,
weatherData: state=>state.yearData
})
},
methods: {
...mapActions([
'getYearData'
])
},
beforeMount(){
this.$store.dispatch('getYearData') //(un)Commenting out this line will make my data appear
}
thats way, you work directly with one source of truth-the store, and your name and weatherData will be reactive as well.
more about mapState here: https://vuex.vuejs.org/guide/state.html#the-mapstate-helper

rendering vue.js components and passing in data

I'm having trouble figuring out how to render a parent component, display a list of contracts in a list on part of the page, and when a user clicks on one of them, display the details of that specific contract on the other part of the page.
Here is my slim file:
#contracts_area
.filter-section
ul
li.filter-item v-for="contract in contractsAry" :key="contract.id" #click="showContract(contract)"
| {{ contract.name }}
.display-section
component :is="currentView" transition="fade" transition-mode="out-in"
script type="text/x-template" id="manage-contracts-template"
div
h1 Blank when page is newly loaded for now
script type="text/x-template" id="view-contract-template"
div :apply_contract="showContract"
h1#display-item__name v-name="name"
javascript:
Vue.component('manage-template', {
template: '#manage-contracts-template'
});
Vue.component('view-contract', {
template: '#view-contract-template',
props: ['show_contract'],
data: function() {
return {
name: ''
}
},
methods: {
showContract: function(contract) {
return this.name = contract.name
}
}
});
Vue.http.headers.common['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content');
var contractsResource = Vue.resource('/all_contracts{/id}.json');
var contracts = new Vue({
el: '#contracts_area',
data: {
currentView: 'manage-template',
contractsAry: [],
errors: {}
},
mounted: function() {
var that = this;
contractsResource.get().then(
function(res) {
that.contractsAry = res.data;
}
)
},
methods: {
showContract: function(contract) {
this.currentView = 'view-contract'
}
}
});
Basically I'd like it so that when a user clicks on any contract item in the .filter-section, it shows the data for that contract in the .display-section. How can I achieve this?
In short you can bind a value to a prop.
.display-section
component :is="currentView" :contract="currentContract"
view-contract
props: ['contract']
contracts-area
data: {
currentContract: null,
},
methods: {
showContract: function(contract) {
this.currentView = "view-contract";
this.currentContract = contract;
}
}
There are multiple ways to pass data in Vue.
Binding values to props.
Using ref to directly call a method from a child component.
Custom Events. Note that to pass events globally, you will need a global event bus.
A single central source of truth (i.e. vuex)
I have illustrated methods 1, 2, 3 in Codepen
Note that 2nd and 3rd methods will only work after your component has been rendered. In your case, since your components for currentView are dynamic and when user clicked, display-section component does not yet exists; it will not receive any events yet. So their content will be empty at first.
To workaround this you can directly access $parent in mounted() from child component, however this would create coupling between them. Another solution is creating the components but conditionally displaying them. And one another solution would be waiting until child component has been mounted and then emitting events.
If your needs are simple I suggest binding values to props (1), else you may consider using something like vuex.

Passing data to components in vue.js

I'm struggling to understand how to pass data between components in vue.js. I have read through the docs several times and looked at many vue related questions and tutorials, but I'm still not getting it.
To wrap my head around this, I am hoping for help completing a pretty simple example
display a list of users in one component (done)
send the user data to a new component when a link is clicked (done) - see update at bottom.
edit user data and send it back to original component (haven't gotten this far)
Here is a fiddle, which fails on step two: https://jsfiddle.net/retrogradeMT/d1a8hps0/
I understand that I need to use props to pass data to the new component, but I'm not sure how to functionally do it. How do I bind the data to the new component?
HTML:
<div id="page-content">
<router-view></router-view>
</div>
<template id="userBlock" >
<ul>
<li v-for="user in users">{{user.name}} - <a v-link="{ path: '/new' }"> Show new component</a>
</li>
</ul>
</template>
<template id="newtemp" :name ="{{user.name}}">
<form>
<label>Name: </label><input v-model="name">
<input type="submit" value="Submit">
</form>
</template>
js for main component:
Vue.component('app-page', {
template: '#userBlock',
data: function() {
return{
users: []
}
},
ready: function () {
this.fetchUsers();
},
methods: {
fetchUsers: function(){
var users = [
{
id: 1,
name: 'tom'
},
{
id: 2,
name: 'brian'
},
{
id: 3,
name: 'sam'
},
];
this.$set('users', users);
}
}
})
JS for second component:
Vue.component('newtemp', {
template: '#newtemp',
props: 'name',
data: function() {
return {
name: name,
}
},
})
UPDATE
Ok, I've got the second step figured out. Here is a new fiddle showing the progress: https://jsfiddle.net/retrogradeMT/9pffnmjp/
Because I'm using Vue-router, I don't use props to send the data to a new component. Instead, I need set params on the v-link and then use a transition hook to accept it.
V-link changes see named routes in vue-router docs:
<a v-link="{ name: 'new', params: { name: user.name }}"> Show new component</a>
Then on the component, add data to the route options see transition hooks:
Vue.component('newtemp', {
template: '#newtemp',
route: {
data: function(transition) {
transition.next({
// saving the id which is passed in url
name: transition.to.params.name
});
}
},
data: function() {
return {
name:name,
}
},
})
-------------Following is applicable only to Vue 1 --------------
Passing data can be done in multiple ways. The method depends on the type of use.
If you want to pass data from your html while you add a new component. That is done using props.
<my-component prop-name="value"></my-component>
This prop value will be available to your component only if you add the prop name prop-name to your props attribute.
When data is passed from a component to another component because of some dynamic or static event. That is done by using event dispatchers and broadcasters. So for example if you have a component structure like this:
<my-parent>
<my-child-A></my-child-A>
<my-child-B></my-child-B>
</my-parent>
And you want to send data from <my-child-A> to <my-child-B> then in <my-child-A> you will have to dispatch an event:
this.$dispatch('event_name', data);
This event will travel all the way up the parent chain. And from whichever parent you have a branch toward <my-child-B> you broadcast the event along with the data. So in the parent:
events:{
'event_name' : function(data){
this.$broadcast('event_name', data);
},
Now this broadcast will travel down the child chain. And at whichever child you want to grab the event, in our case <my-child-B> we will add another event:
events: {
'event_name' : function(data){
// Your code.
},
},
The third way to pass data is through parameters in v-links. This method is used when components chains are completely destroyed or in cases when the URI changes. And i can see you already understand them.
Decide what type of data communication you want, and choose appropriately.
The best way to send data from a parent component to a child is using props.
Passing data from parent to child via props
Declare props (array or object) in the child
Pass it to the child via <child :name="variableOnParent">
See demo below:
Vue.component('child-comp', {
props: ['message'], // declare the props
template: '<p>At child-comp, using props in the template: {{ message }}</p>',
mounted: function () {
console.log('The props are also available in JS:', this.message);
}
})
new Vue({
el: '#app',
data: {
variableAtParent: 'DATA FROM PARENT!'
}
})
<script src="https://unpkg.com/vue#2.5.13/dist/vue.min.js"></script>
<div id="app">
<p>At Parent: {{ variableAtParent }}<br>And is reactive (edit it) <input v-model="variableAtParent"></p>
<child-comp :message="variableAtParent"></child-comp>
</div>
I think the issue is here:
<template id="newtemp" :name ="{{user.name}}">
When you prefix the prop with : you are indicating to Vue that it is a variable, not a string. So you don't need the {{}} around user.name. Try:
<template id="newtemp" :name ="user.name">
EDIT-----
The above is true, but the bigger issue here is that when you change the URL and go to a new route, the original component disappears. In order to have the second component edit the parent data, the second component would need to be a child component of the first one, or just a part of the same component.
The above-mentioned responses work well but if you want to pass data between 2 sibling components, then the event bus can also be used.
Check out this blog which would help you understand better.
supppose for 2 components : CompA & CompB having same parent and main.js for setting up main vue app. For passing data from CompA to CompB without involving parent component you can do the following.
in main.js file, declare a separate global Vue instance, that will be event bus.
export const bus = new Vue();
In CompA, where the event is generated : you have to emit the event to bus.
methods: {
somethingHappened (){
bus.$emit('changedSomething', 'new data');
}
}
Now the task is to listen the emitted event, so, in CompB, you can listen like.
created (){
bus.$on('changedSomething', (newData) => {
console.log(newData);
})
}
Advantages:
Less & Clean code.
Parent should not involve in passing down data from 1 child comp to another ( as the number of children grows, it will become hard to maintain )
Follows pub-sub approach.
I've found a way to pass parent data to component scope in Vue, i think it's a little a bit of a hack but maybe this will help you.
1) Reference data in Vue Instance as an external object (data : dataObj)
2) Then in the data return function in the child component just return parentScope = dataObj and voila. Now you cann do things like {{ parentScope.prop }} and will work like a charm.
Good Luck!
I access main properties using $root.
Vue.component("example", {
template: `<div>$root.message</div>`
});
...
<example></example>
A global JS variable (object) can be used to pass data between components. Example: Passing data from Ammlogin.vue to Options.vue. In Ammlogin.vue rspData is set to the response from the server. In Options.vue the response from the server is made available via rspData.
index.html:
<script>
var rspData; // global - transfer data between components
</script>
Ammlogin.vue:
....
export default {
data: function() {return vueData},
methods: {
login: function(event){
event.preventDefault(); // otherwise the page is submitted...
vueData.errortxt = "";
axios.post('http://vueamm...../actions.php', { action: this.$data.action, user: this.$data.user, password: this.$data.password})
.then(function (response) {
vueData.user = '';
vueData.password = '';
// activate v-link via JS click...
// JSON.parse is not needed because it is already an object
if (response.data.result === "ok") {
rspData = response.data; // set global rspData
document.getElementById("loginid").click();
} else {
vueData.errortxt = "Felaktig avändare eller lösenord!"
}
})
.catch(function (error) {
// Wu oh! Something went wrong
vueData.errortxt = error.message;
});
},
....
Options.vue:
<template>
<main-layout>
<p>Alternativ</p>
<p>Resultat: {{rspData.result}}</p>
<p>Meddelande: {{rspData.data}}</p>
<v-link href='/'>Logga ut</v-link>
</main-layout>
</template>
<script>
import MainLayout from '../layouts/Main.vue'
import VLink from '../components/VLink.vue'
var optData = { rspData: rspData}; // rspData is global
export default {
data: function() {return optData},
components: {
MainLayout,
VLink
}
}
</script>

Categories

Resources