I'm new to Vue and having trouble with when / how to assign a prop value calculated using the a store object. I want to grab the id from the url (e.g. /location?locationid) and compare that against the locations object in the store to find the correct location (by location_id) and pass that matching object as prop to the child location component.
I'm not sure when to do the curLoc calculation currently in created(). I've tried doing it as a computed prop, with no luck. I've tried putting the calculation into a method and calling it on created() and update() and it works with a webpack update but not a page refresh.
<template>
<div class="location-wrap">
<main id="main" aria-label="content">
<h2 class="c-section__title">{{ this.curLoc.location_name // get get location_name of undefined }}</h2>
<div class="location">
<Location v-bind:loc="curLoc" /> // I want curLoc to be accessible to this child Location component.
</div>
</main>
</div>
</template>
<script>
import Location from "#/components/Location";
import { mapState } from "vuex";
export default {
name: "location",
components: {
Location,
},
computed: mapState(["locations"]),
data() {
return {
curLoc: {},
locationId: "",
locationName: ""
};
},
created() {
// curLoc calculation
let ref = location.href;
this.locationId = ref.substring(ref.indexOf("?") + 1);
this.locations.forEach(loc => {
if (loc.location_id === this.locationId) {
this.curLoc = loc;
console.log(this.curLoc); // nope
}
});
},
updated() {},
methods: {}
};
</script>
You can create a computed property like this
currLoc() {
/* remove currLoc from data */
let currLoc = {};
let ref = location.href;
this.locationId = ref.substring(ref.indexOf("?") + 1);
/* put check when initially locations isn't defined and can even use break */
this.locations.forEach(loc => {
if (loc.location_id === this.locationId) {
curLoc = loc;
console.log(curLoc); // should print your currLoc
}
});
return currLoc;
}
By making currrLoc a computed property we've ensured that it runs everytime the location changes. You can now use computed property in the template (currLoc and pass it as a prop)
Related
I have an app with plain JS and Vue in one file. I need to pass a variable value from JS to Vue.
0. All code in one file:
<script>
plain js
var plainJS = 100;
</script>
<script>
var app = new Vue({
el: '#vue-app',
....
</script>
main functionality of the app is on plain js. Vue does a small part with UI.
with js I can catch if one of my elements changed position (a dot on the screen)
I need fire popup(some alert) if checkBox is selected but the Dot wasn't moved.
checkBox is a Vue element
I can pass data from Django to Vue
this.vueVar = {{ djangoVar|safe }}
So how to pass
*var plainJS = 100;*
to vue app from plain JS part of the code?
Can you give me a simple way to set vueVar = plainJS?
UPDATE:
function from plain JS
function isDotMoved(length){
if(length != 0){
console.log(length)
return true;
}
return false;
};
so this function works when I grab and move my dot on the screen.
As well, I have a function in Vue part:
isDotsMoved(){
this.dotMoved = isDotMoved(length); // function from plain JS
console.log('moved', this.dotMoved)
if(!this.dotMoved){
toastr.info('Dot Moved');
}
},
I call this function onClick. It should fire Alert if dots were moved.
I use another function the same way:
function videoPause() {
inputVideo.pause();
};
And called it inside of my Vue part:
videoPauseVue() {
videoPause(); //function from plain JS
};
How can I do the same for isDotsMoved()?
First, i add isDotMoved function in the index.html script tag and declare it on window variable that can access anywhere in your code because it is global scope.
<script>
window.plainFunc = (function () {
return {
isDotMoved: function (length) {
if (length != 0) {
console.log(length);
return true;
}
return false;
}
};
})();
</script>
Then in vue I access it throught window variable
<template>
<div id="app">
<h1>Use Function from plainJS</h1>
<button #click="handleClick">Click here to invoke function isDotMove from script</script></button>
</div>
</template>
<script>
function isDotsMoved() {
let length = 10;
let dotMoved = window.plainFunc.isDotMoved(length); // function from plain JS
console.log("moved", dotMoved);
if (!dotMoved) {
alert("Dot Moved");
}
}
export default {
name: "App",
methods: {
handleClick: function () {
isDotsMoved();
},
},
};
</script>
<style>
</style>
Check my example on codebox: https://codesandbox.io/embed/vue-js-font-awesome-1--getting-started-forked-d8xist?fontsize=14&hidenavigation=1&theme=dark
You can access a ref on the root component if you store a variable of what createApp returns. Then each time you would update your plainJS var, also reassign a matching property (ref) on the "app" object. For the initial value you may use a "root prop" which is the 2nd param of the createApp function.
main.js
import { createApp } from "vue";
import App from "./App.vue";
var plainJS = 100;
const myApp = createApp(App, { plainJS: plainJS }).mount("#app");
setInterval(() => {
//interval used here to simulate a value that changes at arbitrary times
plainJS++;
myApp.varFromOutsideVue = plainJS; // 👀 this updates the ref
}, 500);
App.vue
<template>
<h1>{{ varFromOutsideVue }}</h1>
</template>
<script>
import { onMounted, onUnmounted, ref } from "vue";
export default {
name: "App",
props: {
plainJS: { type: Number },
},
setup(props) {
const varFromOutsideVue = ref(props.plainJS);
return {
varFromOutsideVue,
};
},
};
</script>
https://codesandbox.io/s/eager-rubin-6fv7p7?file=/src/main.js
Another option (see my other answer for a more direct solution) is to use the browser's native event system to "subscribe" to changes to the variable from within your vue app. Each time the value changes you emit a custom event and there is an event listener within your vue app set up to listen to those changes and update a reactive ref.
main.js
import { createApp } from "vue";
import App from "./App.vue";
var plainJS = 100;
function fireVarChangeEvent() {
const newEvent = new CustomEvent("varchanged", {
detail: plainJS
});
window.dispatchEvent(newEvent);
}
setInterval(() => {
//interval used here to simulate a value that changes at arbitrary times
plainJS++;
fireVarChangeEvent(); // call this function after each time plainJs var is updated
}, 500);
createApp(App, { plainJS: plainJS }).mount("#app"); //pass in the first value of plainJS as a prop, this will not stay reactive, hence the custom event
App.vue
<template>
<h1>{{ varFromOutsideVue }}</h1>
</template>
<script>
import { onMounted, onUnmounted, ref } from "vue";
export default {
name: "App",
props: {
plainJS: { type: Number },
},
setup(props) {
const varFromOutsideVue = ref(props.plainJS);
function updateVar(e) {
varFromOutsideVue.value = e.detail;
}
onMounted(() => {
window.addEventListener("varchanged", updateVar);
});
onUnmounted(() => {
window.removeEventListener("varchanged", updateVar);
});
return {
varFromOutsideVue,
};
},
};
</script>
I created a new component that will render a chart using Chart.js. I would like to access this component via a reference.
<kpis-linechart name="inv-speed" ref="inv-speed-chart" :information="team.invoice_details">
A chart with chart.js is created using <canvas id="chart-id"></canvas> in the html.
I would like assign the chart id the same value as the ref value.
Is there a way to get the ref. name from the component ?
Example : this.$ref_name
This is my code so far :
Vue.component('kpis-linechart',{
template : '#kpis-linechart-template',
props : {
information : Object,
name : String
},
// I WANT TO REPLACE THIS CODE IN ORDER TO GET THE REF NAME AS ID //
// I WANT TO REPLACE THIS CODE IN ORDER TO GET THE REF NAME AS ID //
computed : {
id_chart(){
return this.name + '-chart'
}
},
methods : {
updateChart(){
this.chart = new Chart(document.getElementById(this.id_chart),{
type : 'line',
data : {
labels : ['hola','como','estas'],
datasets : [
{
label : 'Queso',
data : [1,2,4]
}
]
}
})
}
},
mounted(){
this.updateChart()
console.log('CREATING CHART')
// console.log(this.information)
console.log(this)
}
})
The best way would be to add a that name as a prop (as ref is not passed as a prop):
<my-component ref="something" name="something" />
If you don't want this, you can loop through the parent's $refs and check which one refers to this. But... this is only available when the component is already mounted, so it cannot be a computed.
Demo:
Vue.component('kpis-linechart', {
template: '#kpis-linechart-template',
data() {
return {
id_chart: 'default-id'
};
},
mounted() {
const entry = Object.entries(this.$parent.$refs)
.find(([key, value]) => value === this);
if (entry) {
this.id_chart = entry[0];
}
}
});
var vm = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<div id="app">
<kpis-linechart ref="something"></kpis-linechart>
</div>
<template id="kpis-linechart-template">
<p :id="id_chart">I'm a chart, and my id is "<b>{{id_chart}}</b>"</p>
</template>
Hello and Happy holidays !!
I need advice on how I could get data from an external function that generate a zipfile into my vuejs component, to create a progress bar for JSZip plugin: https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html
I import my file :
import { generateZIP } from "#/utils/downloader.js";
and call it inside vuejs from a method trigger by a button:
<template>
...
<div v-for="result of results" :key="result.item.refID">
<section class="row" #click="selectByRow(result.item)">
<input
type="checkbox"
:id="result.item.refID"
:value="result.item.refID"
v-model="checkedItems"
class="checkbox"
/>
</div>
<!-- FOUND RESULTS -->
<div class="name">{{ result.item.marketingName }}</div>
</section>
</div>
<!-- Download all checked items -->
<div>
<button
v-if="checkedItems.length > 1"
#click="downloadAll(checkedItems)"
class="button"
>
Download Selection
</button>
</template>
...
<script>
import { mapState, mapActions, mapMutations, mapGetters } from "vuex";
import { generateZIP } from "#/utils/downloader.js";
...
export default {
data() {
return {
// Path to point to pictures storage
domainName: this.$domainName,
// Array to gather search results
searchArray: [],
checkedItems: [],
// make a special array for row selection
checkedRow: []
};
},
methods:
downloadAll(files) {
// Prepare path
const fullPath = `${this.domainName}/files/${this.reqPath}/`;
const filesArray = [];
files.forEach(fileID => {
let obj = this.results.find(value => value.item.refID == fileID);
if (obj.item.images !== undefined) {
filesArray.push(obj.item.images);
}
});
generateZIP(filesArray.flat(), fullPath);
},
selectByRow(resultID) {
// Check if select resultID.refID is already in checkedItems and store it in variable if its present.
const isInArray = this.checkedItems.find(name => name === resultID.refID);
// if the ref not in array, add it
if (!isInArray) {
this.checkedItems.push(resultID.refID);
// Add checkedRow full information object
this.checkedRow.push(resultID);
} else {
// if already in array, remove it
this.checkedItems = this.checkedItems.filter(
name => name !== resultID.refID
);
this.checkedRow = this.checkedRow.filter(
name => name.refID !== resultID.refID
);
}
...
Everything working, now I add some feedback showing the zip progress. There is an available callback function "updateCallback" that I'll call in my downloader.js
zip.generateAsync({type:"blob"}, function updateCallback(metadata) {
console.log("progression: " + metadata.percent.toFixed(2) + " %");
if(metadata.currentFile) {
console.log("current file = " + metadata.currentFile);
}
})
...
export {
generateZIP
}
Cool it displays progression in my console log.
But HOW could I import this metadata object into vue to display it in my app ?
Thanks a lot !
Use data properties in your vue component.
Inside the callback, link your instance (this) to local var, to pass value between callback data and reactive property. For example: let var=this
Ok so I have the following prop that I get from the parent component
props: {
selectedExchange: {
default: 'acx',
}
},
And i try to use it in the following method
methods: {
getMarkets() {
const ccxt = require('ccxt')
const exchanges = ccxt.exchanges;
let marketPair = new ccxt[this.selectedExchange]()
let markets = marketPair.load_markets()
return markets
}
},
The expected result should be an array of markets for my project but i get an error in the console
[Vue warn]: Error in mounted hook: "TypeError: ccxt[this.selectedExchange] is not a constructor"
Now i thought it might be a problem with ccxt but it's not! I have tried the following code
methods: {
getMarkets() {
const ccxt = require('ccxt')
const exchanges = ccxt.exchanges;
let acx = 'acx'
let marketPair = new ccxt[acx]()
let markets = marketPair.load_markets()
return markets
}
},
If you don't see the change I have made a variable that contains 'acx' inside, exactly the same like the prop but this time it's created inside the method, and with this code I get the expected result, It has been bugging me for days and I can't seem to find an answer to it, did I initialize the default value wrong? When i look inside vue dev tools the value for my prop is array[0], only after I pass a value to that prop it gets updated, shouldn't i see the default value of acx in devtools? Any help is much appreciated!
Edit 1: Added parent component code
This is how i use the methods inside the parent and how my components are related to each other,
<div id="exchange">
<exchange v-on:returnExchange="updateExchange($event)"></exchange>
</div>
<div id="pair">
<pair :selectedExchange="this.selectedExchange"></pair>
</div>
And this is the code inside the script tags, i didn't include the import tag cause i don't think it would be useful
export default {
name: 'App',
components: { exchange, pair, trades },
data(){
return{
selectedExchange: ''
}
},
methods: {
updateExchange(updatedExchange){
this.selectedExchange = updatedExchange
}
},
};
In this case you will inherit the default value:
<pair></pair>
In this case you will always inherit the value of selectedExchange, even if it's null or undefined:
<pair :selectedExchange="this.selectedExchange"></pair>
So, in your case, you have to handle the default value on parent component.
This should work:
export default {
name: 'App',
components: { exchange, pair, trades },
data(){
return{
selectedExchange: 'acx' // default value
}
},
methods: {
updateExchange(updatedExchange){
this.selectedExchange = updatedExchange
}
},
};
I'm trying to access a props of a component when this last triggers an event. This component is coming from http://element.eleme.io/#/en-US/component/switch.
It has several props such as name, value and so on. I would like to be able to get the name or the value fo the switch when change is triggered.
Even more, how to access any props of the switch that triggered the change event ?
I tried this but I get undefined.
<div v-for="organizer in organizers>
<el-switch #change="changeOrganizers($event.target.name, $event.target.value)" :name="organizer.name">
</el-switch>
</div>
var Main = {
data() {
return {
value1: true,
}
},
methods : {
changeSwitch(name) {
console.log(name)
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
http://jsfiddle.net/2hr6y79h/2/
Thank you
Solution
<div v-for="organizer in organizers>
<el-switch #change="changeOrganizers()" :name="organizer.name">
</el-switch>
</div>
var Main = {
data() {
return {
value1: true,
}
},
methods : {
changeSwitch() {
console.log(event.currentTarget.checked);
console.log(event.currentTarget.name)
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Simply try
#change="changeSwitch"
http://jsfiddle.net/2hr6y79h/3/
That will give you the value.
The component will (probably) use
this.$emit('change', this.value)
which passes the name as the sole argument to your bound "change" event handler.
If you want the name as passed via props, I doubt it changes so just save a reference to it in your Vue instance / component, eg
data () {
return {
value1: true,
name: 'test-name'
}
}
and use
<el-switch ... :name="name"
Then you can always access it via this.name.
<div v-for="organizer in organizers>
<el-switch #change="changeOrganizers()" :name="organizer.name">
</el-switch>
</div>
var Main = {
data() {
return {
value1: true,
}
},
methods : {
changeSwitch() {
console.log(event.currentTarget.checked);
console.log(event.currentTarget.name)
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')