Vue interpolation does not work in the text obtained by API - vue.js

Get text by API, in text have a entities in {{}}, like a:
Some text {{rules}} other text
Have in data values:
rules: "some text"
but this values dont interpolated, displays:
Some text {{rules}} other text
thanks for answer

Considering the comment above:
I have: template: {{someText}} have data: data() { return {
someText: "some text {{ myValue }} some text", myValue: "300", } },
How display data from myValue in template?
You don't do interpolation like that in Vue. Double braces ({{}}) are for the template part, not the scripts.
Look at this snippet:
new Vue({
el: "#app",
data() {
return {
someText: "some text {{ myValue }} some text",
myValue: "300"
}
},
computed: {
parsedSomeText() {
let ret = ''
if (/(\w+)(?= }})/g.test(this.someText)) {
let key = String(this.someText).match(/(\w+)(?= }})/g)[0]
if (Object.keys(this.$data).includes(key)) {
ret = this.someText.replace(/{{ (\w+) }}/g, this.$data[key])
}
}
return ret
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
myValue: {{myValue}}<br /> someText: {{someText}}<br />parsedSomeText: {{parsedSomeText}}
</div>
I created a small parser in parsedSomeText() computed property, that very crudely replaces the double braces in the someText data property and returns the modified string, so to get it working in the data property.
I advise you to look over the data you receive, and think of another solution - or utilize some rock-solid parsing technique to use it in production.
If you cannot avoid getting data in such a way, then you should look into dynamic (run-time) compilation (like: https://alligator.io/vuejs/v-runtime-template/) and render functions (https://v2.vuejs.org/v2/guide/render-function.html). With these, your Vue app becomes more versatile but more complex.

Related

vue JS - Append data (strings) to a placeholder

I am a absolut beginner with vue.js. So I did a little app for learning. But I stuck a little bit with append-Data to a placeholder, if that is the right term, for
{{ message1 }}
Here is my code:
<script>
Vue.createApp({
data() {
return {
bheight: '',
bwidth: '',
}
},
methods: {
domath(event) {
alert(`Hello!`)
}
}
}).mount('#app')
</script>
The "domath" method is triggred by a button. That work (the allert show up). I have this placeholder "message1. {{ message1 }}
What I want is this: If the button is clicked, I want the data from "bheight" and "bwidth" appends to the placeholder {{ message1 }}
Later I want to do a math with this both variables. And append the result in the placeholder.
Id do not figured out how I can to this. Can some help me please?
You need to have an additional variable:
data() {
return {
bheight: '',
bwidth: '',
message1: ''
}
And then:
domath(event) {
this.message1 += this.bwidth + ', ' + this.bheight;
}
Using this is crucial iin this context.
Try something like this:
Add computed Property
computed: {
placeholder() {
return this.errors.has('end_date') ? 'Your placeholder text' : ''
}
}
Next Steps:
Bind to your computed placeholder property with v-bind:placeholder="placeholder"

How to properly watch an object in Vue?

I am trying to use a Vue watcher on a computed object and it is not working at all. It works properly if it's just a string, but not if it's an object. I followed the Vue documentation but I am still getting nothing logged to the console when the object changes. The properties of the object and computed property are changing, as I have confirmed in Vue Tools. What am I doing wrong? Thanks
<v-btn small dark #click="test1.asdf = 'blah'">Test</v-btn>
data() {
return {
test1: {},
}
},
computed: {
test() {
return this.test1
}
},
watch: {
test: {
handler: function(val, oldVal) {
console.log(oldVal, val);
},
deep: true
}
}
Try this code, its works fine
<template>
<div id="app">
{{ test1 }}
<hr />
<button #click="test1.asdf = 'blah'">Click</button>
</div>
</template>
<script >
export default {
data() {
return {
test1: { asdf: "HEY" },
};
},
computed: {
test() {
return this.test1;
},
},
watch: {
test: {
handler: function (val, oldVal) {
console.log(oldVal, val);
},
deep: true,
},
},
};
</script>
I'd guess in your case you should add .native at end of your click event like this #click.native="test1.asdf = 'blah'"
Just tried by replacing the whole object. It works pretty well, and there is no need to initialize your object with the asdf property in your data:
<v-btn small dark #click="test1 = { ...test1, asdf: 'blah'}">Test</v-btn>
The spread syntax ...test1 helps keeping other properties in the target object if there is any, so you can safely add the asdf property without worrying about losing anything.
JSFiddle: https://jsfiddle.net/vh72a3bs/22/
#BadCoder Objects and Arrays are pass by reference in JavaScript, not pass by value. This means that when you add a key to the object as you are you doing in your question, you're just adding to the same Object. It's contents have changed but your variable test1 is still referencing the original object and unaware that its contents have updated. The watcher doesn't pick this change up. You can add deep: true to your watcher as another answerer has suggested, but this only watches for a couple of levels deep, so not ideal if you have a large object with lots of nested data. The most reliable way to trigger a watcher when dealing with arrays or objects is to create a new instance of that object. You can achieve this with object destructing.
Something like,
<v-btn small dark #click="test1 = { ...test1, asdf: 'blah'}">Test</v-btn>
works because you're creating a new object (using the previous objects attributes, plus anything new), triggering the watcher.

Vue search while typing

I have search field and I wish to have the results in real-time,
I have no issue with returning data or showing data but I need a way to send input value to back-end while user is typing it.
Code
HTML
<el-input placeholder="Type something" v-model="search">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
Script
data() {
return {
search: '' // getting data of input field
}
},
I've tried to compute it but it didn't return data
computed : {
searchSubmit: {
get: function () {
console.log(this.search);
}
}
},
Any idea?
For side effects as calling backend you can use Watchers.
watch: {
search(value) {
yourApiCall(value)
}
}

How can I change the whole data of a vue object?

Simply, I just want to change the entire data of a Vue object, like this:
vueobj.$data = newdata;
but the official document says it's not allowed:
VM156 vue.js:597 [Vue warn]: Avoid replacing instance root $data. Use
nested data properties instead.
(found in )
So I tried another way: first destroy the the vueobj by $destroy(), then create a new vueobj to bind new data object to the same the UI element, but after that, the UI element still uses the old data. So how could I solve this problem? thanks!
this works for me:
<div id="app">
{{name}}: {{age}}
</div>
const app = new Vue({
el: "#app",
data: {
name: "Alice",
age: 30
},
methods: {
refresh(data) {
Object.assign(this.$data, data); // <--
}
}
});
const newData = {
name: "Bob",
age: 22
};
app.refresh(newData);
It is tricky, but you could iterate over the data properties and:
First: remove all data properties;
Second: add each property of the newdata object to the data object.
Demo below:
new Vue({
el: '#app',
data: {
name: 'Alice',
age: 30
},
methods: {
changeWholeData() {
let newdata = {name: 'Bob', age: 40};
// erase all current keys from data
Object.keys(this.$data).forEach(key => this.$data[key] = null);
// set all properties from newdata into data
Object.entries(newdata).forEach(entry => Vue.set(this.$data, entry[0], entry[1]));
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>Name: {{ name }}</p>
<p>Age: {{ age }}</p>
<button #click="changeWholeData">Change Whole Data</button>
</div>
As it says, you need to use a nested property to wrap the data you want to replace so it just takes a little planning up front.
If you create your Vue instance like this,
var vueobj = new Vue({
el: '#app',
data: {
dataSource: {
name: 'Alice',
age: 30
}
}
})
then you can replace your data like this
vueobj.dataSource = newdata;
The dataSource wrapper property can be any valid property name. The downside is you can no longer access your inner properties directly on the Vue instance. E.G. you have to use vueobj.dataSource.name instead of vueobj.name, but I guess that's the trade off for all the other ease of use Vue is providing. You could create a name computed to solve this on a case-by-case basis but it would be tedious to do if there are many root properties.

Programmatically create binding expression

I have a scenario where an expression I wish to execute is dynamically loaded into a component. I am unable to set the expression into v-if as it is a literal string, not the actual binding expression.
I had a look at using vm.$watch however the expressions are only allowed to be dot notation paths, rather than single javascript expressions.
vm.$watch usage: Watch an expression or a computed function on the Vue instance for changes. The callback gets called with the new value and the old value. The expression only accepts simple dot-delimited paths. For more complex expression, use a function instead.
Is there some part of vuejs that I can use to achieve this? I assume that the binding expressions for the v-if directive etc are ultimately strings that are being parsed and evaluated in a given context, it just just whether these functions are available for use in components?
Hopefully this example below shows a more complete picture of what I am trying to achieve:
<template>
<div>
<div v-if="expression"></div>
</div>
</template>
<script>
export default {
name: 'mycomponent'
data: function() {
var1: 5,
var2: 7,
expression: null
},
created: function() {
this.$http.get('...').then((response) => {
// Sample response:
// {
// 'expression' : 'var1 > var2'
// }
// TODO: Not this!!
this.expression= response.expression;
});
}
}
</script>
You could create a method for this, e.g. :
<div v-if="testExpression"></div>
And add methods in your component config :
methods: {
testExpression() {
return eval(this.expression)
}
}
Be aware that evaluating expressions requires you to use things that are Not Generally Good JavaScript, such as eval and with. Take care to ensure that you control what can get in to any use of eval.
I recommend using a computed rather than a method for this; it seems more well-suited (and avoids requiring invocation parentheses).
new Vue({
el: '#app',
data: {
var1: 5,
var2: 7,
expression: null
},
computed: {
evaluatedExpression: function() {
with(this) {
try {
return eval(expression);
} catch (e) {
console.warn('Eval', expression, e);
}
}
}
},
created: function() {
setTimeout(() => {
this.expression = 'var1 < var2';
}, 800);
setTimeout(() => {
this.expression = 'var1 > var2';
}, 2500);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<div id="app">
{{expression}}, {{evaluatedExpression}}
<div v-if="evaluatedExpression">
Yep
</div>
<div v-else>
Nope
</div>
</div>
Tidying up a question I have left open for over a year, the answer I have ended up using is to use the Angular Expression library Angular Expressions for my purposes, but will likely be moving to using the Friendly Enough Expression Language library in future as both my client and server will evaluate similar expressions.