Vue.js - Methods vs Computed. What is wrong here - vue.js

I've created a Codepen of very simplified version of an app that I'm trying to build (im new to Vue):
https://codepen.io/dagford/project/editor/XVaaBo
Problem 1 - I'd like to use methods rather than computed, however, as you can see what is happening if I use methods.
Problem 2 - the displayTotal function is not working at all (whether I use methods OR computed.

You should use computed for only total. From vue doc
means if any of its dependencies changes it will recalculate
computed properties are cached based on their dependencies.
var app1 = new Vue({
el: '#app1',
data: {
message: 'Hello Vue.js!',
num1: 1,
num2: 2
},
computed: {
displayTotal: function() {
return parseFloat(this.num1) + parseFloat(this.num2);
}
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta -->
<meta charset="UTF-8" />
<title>My New Pen!</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app1">
<p>{{ message }}</p>
<p>Display number one: {{ num1 }} : <input type="number" v-model="num1"></p>
<p>Display number two: {{ num2 }} : <input type="number" v-model="num2"></p>
<p>Display total: {{ displayTotal }}</p>
</div>
</body>
</html>

displayNum1 and displayNum2 are methods, but you address them as vars. Try placing () in your html
<p>Display number one: {{ displayNum1() }}</p>
<p>Display number two: {{ displayNum2() }}</p>
That also applies to your displayTotal();

Related

Vue js increment operator(++) not giving expected output

Why the below vue js code showing output 102.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
</head>
<body>
<div id = "intro" style = "text-align:center;">
<h1>{{ ++serial }}</h1>
</div>
<script type = "text/javascript">
var app = new Vue({
el: '#intro',
data: {
serial: 0
}
});
</script>
</body>
</html>
I need an explanation. My expected output is 1. How to fix that?
It's not allowed to run statements that update component's properties inside the template because this will make an infinite loop, you could achieve the desired behavior using a computed property as shown below:
var app = new Vue({
el: '#intro',
data: {
serial: 0
},
computed: {
incrementedSerial() {
return ++this.serial
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
<div id="intro" style="text-align:center;">
<h1>{{ incrementedSerial }}</h1>
</div>
That's an assignment statement, which isn't allowed in an interpolation.
From the docs:
These expressions will be evaluated as JavaScript in the data scope of the owner Vue instance. One restriction is that each binding can only contain one single expression, so the following will NOT work:
<!-- this is a statement, not an expression: -->
{{ var a = 1 }}
Your code is roughly the equivalent of:
{{ serial = serial + 1 }}
Here's how you could make a calculation with it in a loop:
new Vue({
el: "#app",
data() {
return {
serial: 0
}
}
});
<div id="app">
<div v-for="i in 5">
{{ i + serial }}
</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>

How to bind v-model from input in one div to another div or component

I have my input field in one div, and will have label in another div (as sidebar in my application). I want to update label in sidebar, as I type in input on first div.
I am happy to create second div a component if that's the way. I was reading online, and it was said we could use props to pass data to component. But I am not able to link input field to component. Please find my code as below:
var app = new Vue({
el: '#div1',
data: {
message: ''
}
})
Vue.component('testp', {
props: ['message'],
template: '<p>Message is: {{ message }}</p>'
})
var div2 = new Vue({
el: '#div2'
});
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="div1">
<input v-model="message" placeholder="edit me">
</div>
<div id="div2">
<testp></testp>
</div>
</body>
</html>
As Pointed in Comment You have no reason to have two separate Vue instance and the First Answer is correct. But in some cases where you really need to have multiple Vue instances, you can actually use them in the following manner.
var app = new Vue({
el: '#div1',
data: {
message: ''
}
})
Vue.component('testp', {
props: ['message'],
template: '<p>Message is: {{ message }}</p>'
})
var div2 = new Vue({
el: '#div2',
computed: {
newMessage() {
return app.message;
}
},
});
Html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="div1">
<input v-model="message" placeholder="edit me">
</div>
<div id="div2">
<testp :message="newMessage"></testp>
</div>
</body>
</html>
Please observe the computed value newMessage is actually getting its value form a different Vue instance (app) and it is also reactive. Therefore whenever the value in first Vue instance changes, it is updated in another Vue instance.
Codepen: https://codepen.io/ashwinbande/pen/xMgQQz
Like I have pointed out in my comments, there is no reason for you to use two separate Vue instances. What you can do is simply wrap everything within an app container, e.g. <div id="#app">, and then instantiate your VueJS instance on that element instead.
Then, you can use v-bind:message="message" on the <testp> component to pass in the message from the parent. In this sense #div1 and #div2 are used entirely for markup/decorative purposes and are not used as VueJS app containers in your code.
Vue.component('testp', {
props: ['message'],
template: '<p>Message is: {{ message }}</p>'
});
var app = new Vue({
el: '#app',
data: {
message: ''
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div id="div1">
<input v-model="message" placeholder="edit me">
</div>
<div id="div2">
<testp v-bind:message="message"></testp>
</div>
</div>

I have some question about Vue component structure

While studying Vue By self-taught, I faced some problem.
First, I bind some component by new Vue ({el:" # id "}).
And when I bind root component <div id = "app"> by new Vue ({el:" # app "}),
It ruin what already was binding there.
My function and data in new Vue ({el:" # id "}) didn't work anymore.
Am I doing the wrong design?
If so, how should I approach the problem?
<html>
<head>
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div id="comp-a">
<input type="text" v-model="message"/>
{{message}}
</div>
</div>
</body>
<script>
new Vue({
el : "#comp-a",
data : {
message : "message"
}
})
new Vue({
el : "#app"
})
</script>
You can use component.
reference: https://v2.vuejs.org/v2/guide/components.html
let comp_a=Vue.component('comp-a', {
data: function () {
return {
message: ""
}
},
template: ` <div><input type="text" v-model="message"/>
{{message}}</div>`
});
let app = new Vue({
el:"#app"
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<comp-a></comp-a>
</div>
If you want to component's html code in html area. template can point to by id. you can do following:
let comp_a=Vue.component('comp-a', {
data: function () {
return {
message: ""
}
},
template: "#comp-a"
});
let app = new Vue({
el:"#app"
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<comp-a></comp-a>
</div>
<template id="comp-a">
<div>
<input type="text" v-model="message"/>
{{message}}
</div>
</template>
VueJS does not work this way. You don't nest IDs. You could do this:
<html>
<head>
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<div id="comp-a">
{{message}}
</div>
</body>
<script>
new Vue({
el : "#app"
})
new Vue({
el : "#comp-a",
data : {
message : "message"
}
})
</script>
But even that approach has problems. You really should have only one matching VueJS area.
The only reason to have two is if you really have two applications running on the same html file. I have never seen a reason to do that.

Vue.js: where does this info come from?

I am following the Vue.js documentation and run this example.
So here is the index.html file:
<html>
<head>
<link rel="stylesheet" href="index.css">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>Bitcoin Price Index</h1>
<div v-for="currency in info" class="currency" >
{{ currency.description }}:
<span class="lighten">
<span v-html="currency.symbol"></span>
{{ currency.rate_float | currencydecimal }}
</span>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
And here is the index.js:
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(response => (this.info = response.data.bpi))
},
filters: {
currencydecimal (value) {
return value.toFixed(2)
}
},
})
Output:
(you can copy paste the above code into here)
Question: in index.html, I do not understand where {{ currency.description }} is coming from. currency is not even declared as in the data of the Vue() instance.
v-for="currency in info"
This property currency is one of elements from info array.
It is defined in your .html file as v-for="currency in info"
See that in the index.html file that has a v-for. In case he assigns to currency each element of info. An array of objects is assigned to info in mounted
See more about JavaScript for/in Statement here

Property Binding & Sync in vue.js

I'm having trouble syncing my properties in vue.js, I have an 'active' property that I want to set to the value of each instance 'plan', but at the same time I would like to sync the property with the parent, with no luck. What am I doing wrong ?
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Vue</title>
<link rel="stylesheet prefetch" href="http://bootswatch.com/paper/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
</head>
<body>
<div class="container">
<div id="app">
<pre>
#{{ $data | json}}
</pre>
<div v-for="plan in plans">
<plan :active.sync="active" :plan.sync="plan"></plan>
</div>
</div>
</div>
<template id="plan_template">
<div>
<span >#{{ plan.name }}</span>
<span >#{{ plan.price }}/month</span>
<button #click="setActivePlan" class="btn btn-primary btn-xs">UPGRADE</button>
</div>
</template>
<script>
new Vue({
el:'#app',
data:{
plans:[
{name:'Executive',price:100},
{name:'Professional',price:50},
{name:'Personal',price:30},
{name:'Free',price:0}
],
active:{},
},
components:{
plan:{
template:'#plan_template',
props:['plan', 'active'],
methods:{
setActivePlan:function(){
this.active=this.plan;
}
}
}
}
});
</script>
</body>
</html>
Note: This answer applies to V2 of Vue JS, < 2.3.0.
If you are using up V2.3.0+ then you can use .sync and .once modifiers: documentation here
You are using version 2 of Vue. The .sync and .once modifiers have been removed. From the docs:
Props are now always one-way down. To produce side effects in the parent scope, a component needs to explicitly emit an event instead of relying on implicit binding.
I have modified your code to use events here:
new Vue({
el:'#app',
data:{
plans:[
{name:'Executive',price:100},
{name:'Professional',price:50},
{name:'Personal',price:30},
{name:'Free',price:0}
],
active:{},
},
methods: {
setActivePlan: function(plan) {
this.active = plan;
}
},
components:{
plan:{
template:'#plan_template',
props:['plan', 'active'],
methods:{
setActivePlan:function(){
// emit an event to the parent indicating that this is the active plan
this.$emit('activate-plan');
}
}
}
}
});
<link rel="stylesheet prefetch" href="http://bootswatch.com/paper/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div class="container">
<div id="app">
<div v-for="plan in plans">
<plan :plan="plan"
:active="active"
#activate-plan="setActivePlan(plan)"
>
</plan>
</div>
<pre>
{{ JSON.stringify($data, null, 2) }}
</pre>
</div>
</div>
<template id="plan_template">
<div>
<span >{{ plan.name }}</span>
<span >{{ plan.price }}/month</span>
<button #click="setActivePlan" class="btn btn-primary btn-xs">UPGRADE</button>
</div>
</template>