Looking at the Vue Documentation, I can't understand how to call a function with arguments in Vue, using data already in the template.
For example,
JavaScript
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
methods: {
reverse: function (word) {
return word.split('').reverse().join('');
}
}
})
HTML
<div id="example">
<p> {{ message }} </p>
<p> {{ reverse( {{ message }} ) }} </p>
</div>
I know the HTML is wrong, but this is similar to what I'm looking to achieve.
Code between {{ }} is interpreted as javascript, so you can pass the variable directly to the function:
<p> {{ reverse(message) }} </p>
#Jerodev answer is correct, and it's what you were looking for.
However, for the code snippet you pasted, a computed property is the way to go:
JS:
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
reverse(){
return this.message.split('').reverse().join('');
}
}
})
HTML:
<div id="example">
<p> {{ message }} </p>
<p> {{ reverse }} </p>
</div>
In this way, the code is more performant, because the expression is cached, and arguably more clear, since you don't need to call the method with the argument in the html.
If you like typescript, you can write it like this
import { Vue, Component } from 'vue-property-decorator';
#Component({name: 'test-component'})
export default class TestComponent extends Vue {
private message: string = 'Hello';
get reverse(): string {
return this.message.split('').reverse().join('');
}
}
And in your template
<div id="example">
<p> {{ message }} </p>
<p> {{ reverse }} </p>
</div>
Related
I have a component that displays rows of data which I want to toggle to show or hide details. This is how this should look:
This is done by making the mapping the data to a new array and adding a opened property. Full working code:
<script setup>
import { defineProps, reactive } from 'vue';
const props = defineProps({
data: {
type: Array,
required: true,
},
dataKey: {
type: String,
required: true,
},
});
const rows = reactive(props.data.map(value => {
return {
value,
opened: false,
};
}));
function toggleDetails(row) {
row.opened = !row.opened;
}
</script>
<template>
<div>
<template v-for="row in rows" :key="row.value[dataKey]">
<div>
<!-- Toggle Details -->
<a #click.prevent="() => toggleDetails(row)">
{{ row.value.key }}: {{ row.opened ? 'Hide' : 'Show' }} details
</a>
<!-- Details -->
<div v-if="row.opened" style="border: 1px solid #ccc">
<div>opened: <pre>{{ row.opened }}</pre></div>
<div>value: </div>
<pre>{{ row.value }}</pre>
</div>
</div>
</template>
</div>
</template>
However, I do not want to make the Array deeply reactive, so i tried working with ref to only make opened reactive:
const rows = props.data.map(value => {
return {
value,
opened: ref(false),
};
});
function toggleDetails(row) {
row.opened.value = !row.opened.value;
}
The property opened is now fully reactive, but the toggle doesn't work anymore:
How can I make this toggle work without making the entire value reactive?
The problem seems to come from Vue replacing the ref with its value.
When row.opened is a ref initialized as ref(false), a template expression like this:
{{ row.opened ? 'Hide' : 'Show' }}
seems to be interpreted as (literally)
{{ false ? 'Hide' : 'Show' }}
and not as expected as (figuratively):
{{ row.opened.value ? 'Hide' : 'Show' }}
But if I write it as above (with the .value), it works.
Same with the if, it works if I do:
<div v-if="row.opened.value">
It is interesting that the behavior occurs in v-if and ternaries, but not on direct access, i.e. {{ rows[0].opened }} is reactive but {{ rows[0].opened ? "true" : "false" }} is not. This seems to be an issue with Vue's expression parser. There is a similar problem here.
I have done my research trying to figure out how to achieve what I am describing below, however I had no luck.
In my Algolia index, some records have nested objects.
For example, title and subtitle attributes are of the following format:
title:
{
"en": "English title",
"gr": "Greek title"
}
I would like to execute queries only for a specific subset (in our example "en" or "gr") of these attributes, withoute "exposing" any facet in the UI — language selection would ideally be done “automatically” based on a variable (lang) passed to the Vue component with props. I am using Laravel Scout package with default Vue implementation, as described in documentation here.
My InstantSearch implementation is pretty simple, I am not defining anything specific regarding queries and searchable attributes, I am currently using all the default functionality of Algolia.
<template>
<ais-instant-search
:search-client="searchClient"
index-name="posts_index"
>
<div class="search-box">
<ais-search-box placeholder="Search posts..."></ais-search-box>
</div>
<ais-hits>
<template
slot="item"
slot-scope="{ item }"
>
<div class="list-image">
<img :src="'/images/' + item.image" />
</div>
<div class="list-text">
<h2">
{{ item.title }}
</h2>
<h3>
{{ item.subtitle }}
</h3>
</div>
</template>
</ais-hits>
</ais-instant-search>
</template>
<script>
import algoliasearch from 'algoliasearch/lite';
export default {
data() {
return {
searchClient: algoliasearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_SEARCH
),
route: route,
};
},
props: ['lang'],
computed: {
computedItem() {
// computed_item = this.item;
}
}
};
</script>
I would like to somehow pass an option to query “title.en” and “subtitle.en” when variable lang is set to “en”. All this, without the user having to select “title.en” or “subtitle.en” in the UI.
Update
Maybe computed properties is the path to go, however I cannot find how to reference search results/hits attributes (eg item.title) within computed property. It is the code I have commented out.
I think, you can use computed property. Just transform current item according to the current language variable.
new Vue({
template: "<div>{{ computedItem.title }}</div>",
data: {
langFromCookie: "en",
item: {
title: {
en: "Hello",
ru: "Привет"
}
}
},
computed: {
computedItem() {
const item = JSON.parse(JSON.stringify(this.item));
for (value in item) {
if (typeof item[value] === "object" && Object.keys(item[value]).includes(this.langFromCookie))
item[value] = item[value][this.langFromCookie];
}
return item;
}
}
}).$mount("#app")
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
If lang variable is available via props, you can check that inside list-text class and return {{title.en}} or {{title.gr}} accordingly by passing a dynamic lang value title[lang] like below
...
<div class="list-text">
<h2>
{{ item.title[lang] }}
</h2>
<h3>
{{ item.subtitle[lang] }}
</h3>
</div>
If you want to make a request according to lang prop when component mounts ,then you can make a request inside mounted() method then query like below
mounted() {
axios.get(`/getSomethingWithLang/:${this.item.title[this.lang]}`)
...
}
I am trying to get the selected value to update in my vue component, but it doesn't change and stays empty instead, i can select things but i can't do anything with it.
I have tried adding :value to the v-select component, but it just doesn't update the value whenever i select something.
Since i am still trying this with the example from their documentation, i made a fork on codepen here: https://codepen.io/andyftp/pen/GweKpP so you can try it out.
Anyone can help me here?
HTML:
<div id="app">
<h1>Vue Select - Ajax</h1>
<v-select label="name"
:filterable="false"
:options="options"
:selected="selected"
#search="onSearch"
><template slot="no-options">
type to search GitHub repositories..
</template>
<template slot="option" slot-scope="option">
<div class="d-center">
<img :src='option.owner.avatar_url'/>
{{ option.full_name }}
</div>
</template>
<template slot="selected-option" scope="option">
<div class="selected d-center">
<img :src='option.owner.avatar_url'/>
{{ option.full_name }}
</div>
</template>
</v-select>
Selected: {{ selected }}
</div>
JS:
Vue.component("v-select", VueSelect.VueSelect);
new Vue({
el: "#app",
data: {
selected: null, // this needs to be filled with the selected value (id or object), but it stays empty
options: []
},
methods: {
onSearch(search, loading) {
loading(true);
this.search(loading, search, this);
},
search: _.debounce((loading, search, vm) => {
fetch(
`https://api.github.com/search/repositories?q=${escape(search)}`
).then(res => {
res.json().then(json => (vm.options = json.items));
loading(false);
});
}, 350)
}
});
I've made a customizable flash message using Vue.js. This is working great but the next step is allow a dynamic class to be added to the component.
Flash.vue
<template>
<transition name="fade">
<div v-if="showMessage" :class="flash-container {{ styleClass }}">
<p>{{ message }}</p>
<p>{{ styleClass }}</p>
</div>
</transition>
</template>
<script>
export default{
methods: {
clearMessage () {
this.$store.commit("CLEAR_MESSAGE")
}
},
computed: {
message () {
return this.$store.getters.renderMessage
},
showMessage () {
return this.$store.getters.showMessage
},
styleClass () {
return this.$store.getters.styleClass
}
},
}
</script>
If I try to add it like this I get this error:
- invalid expression: Unexpected token { in
flash-container {{ styleClass }}
Raw expression: v-bind:class="flash-container {{ styleClass }}"
What am I missing here?
Change it to this and it will work:
:class="[styleClass, 'flash-container']"
Another option would be to split the declarations between the dynamic and static ones:
class="flash-container" :class="styleClass"
Under the hood, the separate ones are joined on render.
This this link for more info: https://v2.vuejs.org/v2/guide/class-and-style.html
If you use v-bind, you can't use mustache {{}}.
So you can do something like this:
<div class="flash-container" :class="styleClass">
</div>
or
<div :class="`flash-container ${styleClass}`">
</div>
or
<div class="flash-container" :class={'styleClass': true}>
</div>
Read this https://v2.vuejs.org/v2/guide/class-and-style.html#Binding-HTML-Classes
I'm mixed in my head : I don't know why I see somewhere that we can use this in Vue.js template. Now I don't know which I must use.
I test some case here :
new Vue({
el: "#app",
data: function() {
return {
myVar: 'test'
}
},
methods: {
returnText: function() {
console.log('methods returnText !');
return 'return text from methods !';
}
},
computed: {
computedProp: function() {
return 'computed !';
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="app">
{{ this.myVar }}<br><!-- this works -->
{{ myVar }}<br><!-- this works -->
<button #click="myVar = 'test without this !'" type="button">
Change text</button><!-- this works --><br>
<button #click="this.myVar = 'test with this !'" type="button">
Change text (not working because of 'this')</button><!-- this NOT works -->
<br><br>
{{ computedProp }} <!-- this works -->
<br>
{{ this.computedProp }} <!-- this works -->
<br><br>
{{ returnText() }} <!-- this works -->
<br>
{{ this.returnText() }} <!-- this works -->
</div>
What is the recommendation ?
In template you don't need to use this, you use this in functions like mounted(), created(), or in methods, etc. All lifecycle hooks are called in this context pointing to the invoking Vue instance.
https://v2.vuejs.org/v2/guide/instance.html
I assume v-bind acts like js native 'bind' function, e.g. binds function to another object, different than our context ( which is Vue instanse in other cases).