Having issues with getting VueJS to accept dynamic image string - javascript

I have seen a few questions similar to this but all revolve around webpack, which I am not currently using.
I have a Vue template:
var question = Vue.component('question', {
props: {
scenario: { type: Object }
},
beforeMount: function () {
this.retrieveScenario();
},
methods: {
postChoice: function () {
Post("Study", "PostScenarioChoice")
},
retrieveScenario: function () {
Get("ScenariosVue", "GetScenario", 1, this,
(c, data) => { this.scenario = data; },
(c, errors) => { console.log(errors); }
);
}
},
template:
`<div class="visible">
<div class="row justify-content-center">
<div class="col-lg-6">
<a v-on:click="this.postChoice">
<img class="img-fluid" v-bind:src="scenario.scenarioLeftImg" />
</a>
</div>
<div class="col-lg-6">
<a v-on:click="this.postChoice">
<img class="img-fluid" v-bind:src="scenario.scenarioLeftImg" />
</a>
</div>
</div>
</div >`
});
The Ajax retrieval returns an object with the following:
returned
Object {
scenarioId: 1,
description: "Dog vs Bolard",
scenarioLeftImg: "images\\Scenarios\\bolard_dog_Left.png",
scenarioRightImg: "images\\Scenarios\\bolard_dog_Right.png",
participantScenarios: [],
scenarioTypesScenarios: []
}
However, the Html, doesn't add the src tag to the tag and I'm really not sure why because the data's clearly available.
Much Help would be greatly appreciated.

This is happening because Vue's template system doesn't "watch" properties (or nested properties) of an object for changes. If you need to do this, then you can either use the computed property with a watch on the computed property, or you can just create two props instead of the single prop. Here is what I would do to change your code:
var question = Vue.component('question', {
props: {
// Replace this prop with the two below.
// scenario: { type: Object }
scenarioLeftImg: { type: String },
scenarioRightImg: { type: String }
},
beforeMount: function () {
this.retrieveScenario();
},
methods: {
postChoice: function () {
Post("Study", "PostScenarioChoice")
},
retrieveScenario: function () {
Get("ScenariosVue", "GetScenario", 1, this,
(c, data) => { this.scenario = data; },
(c, errors) => { console.log(errors); }
);
}
},
template:
`<div class="visible">
<div class="row justify-content-center">
<div class="col-lg-6">
<a v-on:click="this.postChoice">
<img class="img-fluid" v-bind:src="scenarioLeftImg" />
</a>
</div>
<div class="col-lg-6">
<a v-on:click="this.postChoice">
<img class="img-fluid" v-bind:src="scenarioLeftImg" />
</a>
</div>
</div>
</div >`
});

Your main issue is that due to the limitations of Javascript, Vue cannot detect property additions in Objects.
Change to
retrieveScenario: function () {
Get("ScenariosVue", "GetScenario", 1, this,
(c, data) => { Vue.set(this.data, 'scenarios', data); },
(c, errors) => { console.log(errors); }
);
}
Please be aware that it is considered really bad practice to have components modify their props. Rather mirror those props into your vm's data() function in the created() hook of your component, then modify that. If you need the "modified" props outside of your component, emit an event telling those components props has changed.
Also replace the backslashes in your path by slashes:
scenarioLeftImg: "images\\Scenarios\\bolard_dog_Left.png",
scenarioRightImg: "images\\Scenarios\\bolard_dog_Right.png",
has to be
scenarioLeftImg: "images/Scenarios/bolard_dog_Left.png",
scenarioRightImg: "images/Scenarios/bolard_dog_Right.png",

Related

Bind this for load event in v-for template

Is there a way to bind a given data prop in template while looping over array?
<div v-for="(page, pageIndex) in pages" :key="pageIndex">
<img
:src=""
#load="onImageLoaded.bind(this, someDataVar)"
/>
</div>
So if in the meantime (until the image gets loaded) someDataVar will gonna be changed, I still want to output in onImageLoaded the original value of someDataVar at the time the image was added to DOM by the for-loop.
PS: I've tried with IIFE but it didn't worked
define a 'directive'.
try this.
new Vue({
el: '#app',
data () {
return {
pages: [1,2,3],
someDataVar: 'someData'
}
},
mounted () {
setTimeout(() => {
this.someDataVar = 'otherData'
}, 1000)
},
directives: {
'init-val': {
inserted: (el, binding) => {
el.dataset.initVal = binding.value
}
}
},
methods: {
onImageLoaded (event) {
console.log(event.target.dataset)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(page, pageIndex) in pages" :key="pageIndex">
<div
v-init-val="someDataVar"
#click="onImageLoaded"
>click me!</div>
</div>
</div>

Nuxtjs pages are not updated even if the vuex store is updated

Here my code :
~/store/state.js
export default () => ({
selectLanguage: 'fr'
})
~/store/actions.js
export default {
switchToFr (context) {
context.commit('switchToFr')
},
switchToEn (context) {
context.commit('switchToEn')
}
}
~/store/mutations.js
export default {
switchToFr (state) {
state.selectLanguage = 'fr'
},
switchToEn (state) {
state.selectLanguage = 'en'
}
}
~/layouts/inside.js
<b-dropdown-item :value="'fr'" #click="$store.dispatch('switchToFr')" aria-role="listitem">
<div class="media">
<img width='30px' height='30px' src="~/assets/img/icons8-france-48.png"/>
<div class="media-content">
<h3>Français</h3>
</div>
</div>
</b-dropdown-item>
<b-dropdown-item :value="'en'" #click="$store.dispatch('switchToEn')" aria-role="listitem">
<div class="media">
<img width='30px' height='30px' src="~/assets/img/icons8-great-britain-48.png"/>
<img width='30px' height='30px' src="~/assets/img/icons8-usa-48.png"/>
<div class="media-content">
<h3>English</h3>
</div>
</div>
</b-dropdown-item>
data () {
return {
activeLanguage: this.$store.state.selectLanguage,
}
},
watch: {
activeLanguage: function() {
console.log(this.activeLanguage)
}
},
~/pages/projects.js
data () {
return {
activeLanguage: this.$store.state.selectLanguage,
}
},
watch: {
activeLanguage: function() {
console.log(this.activeLanguage)
}
},
The problem :
In layout.js, when i switch language, the data activeLanguage change and the watch do a console.log of the new value.
-> it's okay
In project.js, it does not work, i have to change the page and come back to it to have the new store value in my data.
-> it's bad
Anyone know how to do with project.js to have the same comportment that layout.js ?
Thank's !
I'm surprised that activeLanguage did change for you in layout.js. The data function only gets run once when the component gets created and strings are immutable, so I wouldn't have expected activeLanguage in layout.js to pick up when that the selectLanguage value in the store changed.
You should be getting state values from a computed function instead as recommended by the Vuex docs.
Something like this should do the trick:
computed: {
activeLanguage () {
return this.$store.state.selectLanguage
}
}
For a short version, look at mapState.

How to use better scroll the element when the parameter is a letter

In my webApp project, I want to click a letter, and the relative content will be scrolled to display.
Here is js code:
<script>
import Bscroll from 'better-scroll'
export default {
name: 'CityList',
props: {
cities: Object,
hotCities: Array,
letter: String
},
watch: {
letter () {
if (this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
},
mounted () {
this.scroll = new Bscroll(this.$refs.wrapper)
}
}
</script>
I used vue 2.9.3
But I got a error:
[Vue warn]: Error in callback for watcher "letter": "TypeError: Cannot read property '0' of undefined"
who can help me?
<div
class="area"
v-for="(item, key) of cities"
:key="key"
>
<div class="title border-topbottom">{{key}}</div>
<div class="item-list">
<div
class="item border-bottom"
v-for="innerItem of item"
:key="innerItem.id"
>
{{innerItem.name}}
</div>
</div>
</div>
I should add :ref="key"
<div
class="area"
v-for="(item, key) of cities"
:key="key"
:ref="key"
>
I think there is a time when watch function run, this.$refs[this.letter] is not available yet. So you might want to check:
watch: {
letter () {
if (this.letter && this.$refs[this.letter]) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
Your cities has different ref, you don't need to access [0] element
watch: {
letter () {
if (this.letter && this.$refs[this.letter]) {
const element = this.$refs[this.letter]
this.scroll.scrollToElement(element)
}
}
}

Vue.js conditionals

I have a project with a front-end written in vue.js by someone else. I need to add a conditional to display links when more than one link is present when the component loads. I apologize if some of my terminology is not correct, I have no prior experience with vue.js or anything similar and my knowledgable of JavaScript is limited.
Component
This is what I have tried so far. Where I get lost is how to trigger it on page load.
var ConceptListItem = {
props: ['concept'],
template: `<div class="list-group-item concept-item clearfix" id="concept-{{ concept.uri }}">
<div id="conceptDiv">
<a v-if="ident" v-on:click="select" style="cursor: pointer;">{{ concept.label }} ({{ concept.authority.name }}) {{ concept.identities[0].concepts[0] }}</a>
<a v-else v-on:click="select" style="cursor: pointer;">{{ concept.label }} ({{ concept.authority.name }})</a>
</div>
<div class="text text-muted">{{ concept.description }}</div>
</div>`,
data: function() {
return {
ident: false,
}
},
methods: {
select: function() {
this.$emit('selectconcept', this.concept);
},
}
}
I have then tried adding a function to created in the vue template
created () {
window.addEventListener('scroll', this.handleScroll);
window.addEventListener('resize', this.handleScroll);
var self = this;
document.getElementById('graphContainer').onmouseup = function() {
self.updateSwimRef();
self.handleScroll();
},
document.getElementById('conceptDiv')il. = function() {
self.ident = true;
}
},
Lets keep all your links into an array say links[], add the following code in your html
<div v-if="links.length>1">
// your code with condition
</div>
<div v-else>
// else part if any
</div>
In vue part you need to add the array in the following way,
data: function () {
return {
links: [],
}
}

Polymer template.render() is unreliable

I use the following code, in simplified form.
I have a template that shows a list of stores, coming from the _stores array. Depending on some other properties that are set in the app, the _sort and _filter function do their respective work well and show the list I want to see.
When I recalculate distances for each store (running _stores trough calculateDistance) I want to re-render the storelist by calling _search(), which calls the Polymer .render() function.
This call is not reliable: sometimes it renders, sometimes not. It happens on desktop and mobile, safari and android, so it seems like it is a Polymer issue. I cannot find what is causing this. Any idea?
<template id="storeList" is="dom-repeat" items="{{_stores}}" sort="_sort" filter="_filter">
<div class="search_result">
<div class="picture">
<div class="logo" hidden$="{{!item.logo.length}}">
<div class="centered">
<img src$="{{item.logo}}" alt="logo" />
</div>
</div>....
</template>
<script>
Polymer({
is: 'user-store-search-page',
properties: {
...
_stores: Array
...
},
_calculateDistance: function () {
this._stores.forEach(s) {
//do stuff per store
}
},
_filter: function (store) {
//do filtering stuff
},
_sort: function (a, b) {
//sort stuff
},
_search: function () {
this.$.storeList.render();
}
I would use compute property advantage for rerender your list on any action performed on _stores Array :)
<template id="storeList" is="dom-repeat" items="[[_displayStores]]" sort="_sort" filter="_filter">
<div class="search_result">
<div class="picture">
<div class="logo" hidden$="{{!item.logo.length}}">
<div class="centered">
<img src$="{{item.logo}}" alt="logo" />
</div>
</div>....
</template>
<script>
Polymer({
is: 'user-store-search-page',
properties: {
...
_stores: Array,
_displayStores: {
type: Array,
computed: '_computeStoresToDisplay(_stores.*)'
}
...
},
_computeStoresToDisplay: function(stores) {
return stores.value;
},
_calculateDistance: function () {
this._stores.forEach(s) {
//do stuff per store
}
},
_filter: function (store) {
//do filtering stuff
},
_sort: function (a, b) {
//sort stuff
},
_search: function () {
this.$.storeList.render();
}
</script>

Categories