Using a prop inside a filter? - vue.js

I wish to filter some text on my component:
<p :v-text="'hello' | prefix"></p>
My filter looks like:
prefix(string) {
return inputPrefix + string;
}
inputPrefix is just a prop....
<my-component input-prefix="some-prefix_" ....
But I get the error inputPrefix is not defined
How can I use a prop in a filter?

In filters you can not access this. Use the filter in this way,
filters:{
prefix: function(inputPrefix, string){
console.log(inputPrefix);
return inputPrefix + string;
}
and access it like:
<p :v-text="'hello' | inputPrefix"></p>
or a computed property.

You can read the docs or refer this for explanation.
Filters should be pure functions which gives output based upon input. In your case you can use a computed property.
Vue.component('mine', {
props: ['prefix'],
data() {
return {
value: ' component'
}
},
template: '<p>{{formattedString}}</p>',
computed: {
formattedString() {
return this.prefix + this.value
}
}
})
new Vue({
el: "#app",
components: ['mine'],
template: '<mine prefix="new" />'
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11"></script>
<div id="app"></div>
If you still wish to use a filter for your scenario, please find below
Vue.component('mine', {
props: ['prefix'],
data() {
return {
value: ' component'
}
},
template: '<p>{{value | format(prefix)}}</p>',
filters: {
format(value, prefix) {
return prefix + value
}
}
})
new Vue({
el: "#app",
components: ['mine'],
template: '<mine prefix="new" />'
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11"></script>
<div id="app"></div>

Related

vuejs get value from main model to object?

I have a basic ask. I am trying to get the data from the main area of my vue model to inside an object. I tried using app.$data.name i also tried this.name, but I cannot get it to work without error.
new Vue({
el: "#app",
data: {
name:"Bobby",
currentCard: {},
currentCard: {
author: this.name,
},
},
methods: {
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>
I think that is not possible, you could use a computed value.
new Vue({
el: "#app",
data() {
return {
name: "Bobby"
};
},
computed: {
currentCard() {
return {
author: this.name
};
}
},
methods: {}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>

Adding a "Default" image to my <img> (VUE)

I have an area where people can upload their own user-image. But if they do not, I would like to display a default one.
After some googling, I found I can do so by doing something like -
<img :src="creatorImage" #error="defaultAvatar">
But, I am not sure how to then created a method to pass the correct (default) image into it.
I did it with a computed property, like this:
<template>
<img :src="creatorImage" #error="imageError = true"/>
</template>
<script>
...
data() {
return {
imageError: false,
defaultImage: require("#/assets/imgs/default.jpg")
};
},
computed: {
creatorImage() {
return this.imageError ? this.defaultImage : "creator-image.jpg";
}
}
...
</script>
I would suggest creating a component for this, as it sounds like something that will be used on more places.
JsFiddle example
Component
Vue.component('img-with-default', {
props: ['defaultImg'],
data: function () {
return {
defaultAvatar: this.defaultImg || 'https://cdn0.iconfinder.com/data/icons/crime-protection-people/110/Ninja-128.png'
}
},
computed:{
userImage: function() {
if(this.uploadedImg != null) {
return this.uploadedImg;
} else {
return this.defaultAvatar;
}
}
},
template: '<img :src="userImage">'
})
And using the commponent would be
HTML
<div id="editor">
<img-with-default></img-with-default>
<img-with-default default-img="https://cdn3.iconfinder.com/data/icons/avatars-15/64/_Ninja-2-128.png" ></img-with-default>
</div>
JS
new Vue({
el: '#editor'
})
With this you have the default image.
If you want you can create a component that would display passed img src or the default one.
Component
Vue.component('img-with-default', {
props: ['imgSrc'],
data: function () {
return {
imageSource: this.imgSrc || 'https://cdn0.iconfinder.com/data/icons/crime-protection-people/110/Ninja-128.png'
}
},
template: '<img :src="imageSource">'
})
and to use it
HTML
<div id="editor">
<img-with-default></img-with-default>
<img-with-default img-src="https://cdn3.iconfinder.com/data/icons/avatars-15/64/_Ninja-2-128.png" ></img-with-default>
</div>

Getting a Vue.js computed to run without being part of the DOM

I can't figure out how to get Vue.js to always evaluate a computed regardless of if I'm actually using it in the page. A simplified version of what I'm trying to accomplish is to have a couple input fields which I want to influence the value of another field when either has been updated. I also want this field to be manually editable too. Example jsfiddle.
html:
<div id="app">
<p v-if="updateUsername">Just here to get the darn thing to run</p>
<div>
yourName:<input v-model="yourName">
</div>
<div>
dogsName:<input v-model="dogName">
</div>
<div>
username:<input v-model="userName">
</div>
</div>
js:
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
methods: {
},
computed: {
updateUsername: function(){
this.userName = this.yourName + this.dogName;
}
}
});
This works exactly as I want it to but requires I BS the use of "updateUsername" in the html. I'm sure there's a better way.
You could add a watch:
watch: { updateUsername() {} }
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/2/
But it seems what you want are two watchers instead:
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
watch: {
yourName: {
handler() {
this.userName = this.yourName + this.dogName;
},
immediate: true
},
dogName() {
this.userName = this.yourName + this.dogName;
}
}
});
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/6/
Another option (watching two or more properties simultaneously):
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
mounted() {
this.$watch(vm => [vm.yourName, vm.dogName].join(), val => {
this.userName = this.yourName + this.dogName;
}, {immediate: true})
}
});
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/11/
For Vue2, computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed.
In your example, computed property=updateUserName will be re-evaluate when either dogName or yourName is changed.
And I think it is not a good idea to update other data in computed property, you will remeber you update userName in computed property=updateUserName now, but after a long time, you may meet some problems why my username is updated accidentally, then you don't remember you update it in one of the computed properties.
Finally, based on your example, I think watch should be better.
You can define three watcher for userName, dogName, yourName, then execute your own logic at there.
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: 'Adam Barkster'
},
methods: {
},
computed: {
updateUsername: function(){
return this.yourName + this.dogName;
}
},
watch: {
dogName: function(newValue){
this.userName = this.yourName + ' ' + newValue
},
yourName: function(newValue){
this.userName = newValue + ' '+this.dogName
},
userName: function(newValue) {
// below is one sample, it will update dogName and yourName
// when end user type in something in the <input>.
let temp = newValue.split(' ')
this.yourName = temp[0]
this.dogName = temp[1]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div id="app">
<p v-if="updateUsername">Just here to get the darn thing to run</p>
<div>
yourName:<input v-model="yourName">
</div>
<div>
dogsName:<input v-model="dogName">
</div>
<div>
username:<input v-model="userName">
</div>
</div>
</div>

How to display data in template from Vue.extend?

Here is my code:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
`,
data: function () {
return {
qs: getQuestionsContent(); // here I would like to get in qs data from function
}
}
});
Here is my App.js:
App = new Vue ({ /
el: '#app',
data: {
topMenuView: "guestmenu",
contentView: "guestcontent",
}
});
I need to display qs instead of Guest content and do for example v-for or some kind of other iteration. How can I do it?
Why:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
<p>{{foo}}</p> // foo not display
`,
data: function () {
foo: "dddddddddd";
}
});
Why is this not working?
Try something like this:
html
<body>
<guest-content></guest-content>
</body>
<template id="guest-content-template">
Guest Content
<ul>
<li v-for="question in questions">
{{question}}
</li>
</ul>
</template>
js
var guestContent = Vue.extend({
template: "#guest-content-template",
data: function (){
return {
questions: []
}
},
ready(){
this.getQuestionsContent();
},
methods:{
getQuestionsContent(){
this.questions = [
"Why?",
"What?",
"When?"
];
}
}
});
Vue.component('guest-content',guestContent);
new Vue({
el:'body'
});
Anything in a {{}} is assumed to be within the current Vue scope, so you just use foo or questions not guestContent.foo.
The data attribute is a function that has to return the data for the app. If you don't return anything, the component wont have any data.

Vue.js permanently add filter to component

I'm wanted to force the running of a filter on a component everytime it's used. I know I can add a filter to a component in it's markup, but in this instance the filter is to be considered "required", or "core" functionality of the component.
For example, the below component and filter can be used like: <my-component v-model="amount | custom-currency" name="my-field"></my-component>
What I'm ultimately wanting to achieve is the same behavior, but with the markup only looking like: <my-component v-model="amount" name="my-field"></my-component>
The examples are based on the currency filter example outlined here: http://vuejs.org/guide/custom-filter.html#Two-way_Filters
Any help or suggestions greatly appreciated
Component and filter for reference:
var CurrencyComponent = Vue.extend({
props: {
name: {
type: String,
required: true
}
},
filter: 'customCurrency',
template: '<input type="text" name="{{ name }}" >'
});
Vue.filter('customCurrency', {
read: function(val, symbol) {
if (typeof val !== 'undefined') {
return symbol + val.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
return symbol + '0';
},
write: function(val, oldVal) {
var number = +val.replace(/[^\d.]/g, '');
return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
}
});
EDIT:
In reference to Dewey's answer:
var CurrencyComponent = Vue.extend({
props: {
name: {
type: String,
required: true
}
},
computed: {
'hehe2': function() {
return this.$eval('hehe | custom-currency');
}
},
template: '<input type="text" name="{{ name }}" >'
});
You can use computed properties.
Make 2 variables: one variable as input, and the other as the filtered input. Something like:
HTML:
<div id="app">
<input v-model="asInput" />
<h1>{{ asOutput }}</h1>
</div>
JS:
new Vue({
el: '#app',
data: {
asInput: ''
},
computed: {
'asOutput': function() {
return this.$eval('asInput | yourCustomFilter');
}
}
});
See the working example here: https://jsfiddle.net/7ae9t9wv/
Hope this helps