a simple Vue.js addition app - vue.js

Im very new to Vue.js. I’m trying to build a simple app that totals a list of items. But it also, should have the option to ‘disable’ the individual items so that their value would not contribute to the total. (UPDATE: the button should toggle the disabled state)
Visually, the disabled items would simply be greyed-out, (i dont want their amounts showing zero)
I was imagining perhaps, targeting the parent item with a disabled attribute (when the btn is clicked). And then….a check would be made for the existence of this attribute, when the totalling is done (and any item without the attribute (class?) would be skipped)
But I have no idea how this would work in Vue World. Anyone willing to help out please? Or even point me in the right direction?
HTML
<div id="app">
<div id="item1">{{ item1.amt }} <button type="button">exclude me</button></div>
<div id="item2">{{ item2.amt }} <button type="button">exclude me</button></div>
<div id="item3">{{ item3.amt }} <button type="button">exclude me</button></div>
<br>
<div id="total">total: {{ item1.amt + item2.amt + item3.amt }}</div>
</div>
JS
var app = new Vue({
el: "#app",
data: {
item1: {
amt: 10
},
item2: {
amt: 20
},
item3: {
amt: 30
}
}
});
https://codepen.io/dagford/pen/oyKXYP?editors=1010
thank you

Welcome to the vuetiful world of Vue.
If you need the to see the working code, go here
But let me explain you what I actually did
JS
var app = new Vue({
el: "#app",
data: {
items:[
{
id:'item1',
included:'true',
amt: 10
},
{
id:'item2',
included:'true',
amt: 20
},
{
id:'item3',
included:'true',
amt: 30
}
]
},
computed:{
itemTotal(){
return this.items.reduce(function(sum, item){
if(item.included){
return item.amt + sum;
}
return sum;
},0)
}
}
});
It is very crucial to model your data in a correct way. As your items where having common properties, so it could come in an array. I have include the id and a flag know whether it is included or not.
I also have added a method to compute the total every time item changes.
Template:
<div id="app">
<div v-for="item in items"
:id="item.id"
:key="item.id">
<span>{{item.amt}}</span>
<button #click="item.included = false"> exclude me</button>
</div>
<br>
<div id="total">total: {{ itemTotal }}</div>
</div>
In template code I have added v-for to render the whole list, and at button click I am just changing the flag (included). Computation of the itemTotal is triggered automatically and smartly by vue. An at last I am rendering the itemTotal in separate div.
I hope this helps.

if you want to button toggles the 'included' state and exclude the value
try it use v-on:click and a unclick boolean varible
simple demo looks like:
var app = new Vue({
el: "#app",
data: {
item1: {
amt: 10,
unclick:true
},
item2: {
amt: 30,
unclick:true
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div id="item1">{{ item1.amt }}
<button v-on:click="item1.unclick = !item1.unclick" type="button">
{{ (item1.unclick?'exclude me':'include me') }}
</button>
</div>
<div id="item2">{{ item2.amt }}
<button v-on:click="item2.unclick = !item2.unclick" type="button">
{{ (item2.unclick?'exclude me':'include me') }}
</button>
</div>
<br>
<div id="total">total: {{
(item1.unclick?item1.amt:0)
+ (item2.unclick?item2.amt:0)
}}
</div>
</div>

Related

Show child of this parent click vue

I want to show the immediate child after clicking on its parent. Its easy on jquery, hard on vue, how can I do it?
template:
<boxes inline-template>
<div class="white-box" #click="toggleTick">Purchase only
<div v-if="showTick"><i class="fas fa-check"></i></div>
</div>
</boxes>
js
Vue.component('boxes', {
data: function () {
return {
showTick: false
}
},
methods: {
toggleTick () {
this.showTick = !this.showTick
}
}
})
var app = new Vue({
el: '#app',
data: {
}
})
At the moment I have multiple "white-box" div, it shows the child div for all of them, I just want to show the div for the clicked parent's child.
You should have behavior that you expect :
Vue.component('boxes', {
data: function () {
return {
showTick: false
}
}
})
var app = new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<boxes inline-template>
<div class="white-box" #click="showTick = !showTick">
<span>
Purchase only
</span>
<div v-if="showTick">Component 1</div>
</div>
</boxes>
<boxes inline-template>
<div class="white-box" #click="showTick = !showTick">
<span>
Purchase only
</span>
<div v-if="showTick">Component 2</div>
</div>
</boxes>
</div>
Your white-boxes are sharing the same showTick variable, so clicking on one fo them changes the value for all of them.
There are 2 available solutions to this:
Have multiple boxes components and not multiple white-boxes under the same boxes component. Something take ends up looking like this in the DOM
<boxes>...</boxes>
<boxes>...</boxes>
<boxes>...</boxes>
Use an array for showTick and use an index when calling toggleClick. Also note that for the changes in the array to be reactive you need to use Vue.set.
I recommend the former solution.

Vuejs multiple active buttons

I'm trying to create a list where every list item contains a button and i want a user to be able to click multiple button. I'm generating my list like so:
<ul>
<li v-for="item in items" :key="item.id">
<button type="button">{{item.title}}</button>
</li>
</ul>
but the problem with my code is whenever a user click a button, it turns the rest of the buttons to "unclicked". been trying to play with the focus and active stats but even with just css i cant get to enable multiple select .
i did manage to change the look of the current selected button:
button:focus {
outline: none;
background-color: #6acddf;
color: #fff;
}
any idea how can i allow multiple buttons to be clicked?
to make things a bit clearer, i am going to create an AJAX call later and pass the item.id of each item where it's button is clicked
I would much rather avoid changing the data structure if possible
Well you have to store somewhere that you clicked on the clicked item.
If you can't edit the items array then you can always create a new one, like isClicked where you store those values.
new Vue({
el: '#app',
data: {
items: [{
id: 1,
title: 'foo'
},
{
id: 2,
title: 'bar'
},
{
id: 3,
title: 'baz'
}
],
isClicked: []
},
beforeMount() {
// set all values to false
this.items.forEach((item, index) => this.$set(this.isClicked, index, false))
},
methods: {
clicked(index) {
// toggle the active class
this.$set(this.isClicked, index, !this.isClicked[index])
}
}
})
.active {
background: red
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div v-for="(item, index) in items" :key="index">
<button #click="clicked(index)" :class="{'active': isClicked[index]}">{{item.title}}</button>
</div>
</div>
Or you can use vuex for storing those values.
However you can just use Vues event to manipulate the classList property, like:
new Vue({
el: '#app',
data: {
items: [1, 2, 3, 4, 5, 6, 7]
}
})
.active {
color: red
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button v-for="i in items" #click="e => e.target.classList.toggle('active')">{{ i }}</button>
</div>
But it doesn't feel right, IMHO.
Also you can use cookies or localStorage to store those states. So it's really up to you.
Use id attribute for list items to make it unique.
<ul>
<li v-for="item in items" :key="item.id" :id="item.id">
<button type="button" #click="doThis">{{item.title}}</button>
</li>
</ul>
new Vue({
el: '#app',
data: {},
methods: {
doThis() {
// Use this to access current object
}
}
});

How to get the value with v-select, options loaded from ajax using slot templates

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)
}
});

Vue.JS Passing object to child for use inside slot

I am trying to pass an object that is loaded within DataContainer into a slot, so that the user can customise the view.
<data-container silo-id="5">
<div slot="content"> <!-- I tried :data="siloData" here but no luck -->
Your current balance is {{data.balance}}
</div>
</data-container>
So DataContainer loads the resource via http and sets the value to its 'siloData' property.
DataContainer's template has no content of its own just a placeholder for the slot.
<template>
<div>
<slot name="content"></slot>
</div>
</template>
When I try this the text is not interpolated and just remains as {{siloData.balance}} to the browser.
I have tried some examples from Vue.JS site like the todo list, but I must admit utterly confused, maybe because this is not a collection, but just a single (albeit complex) object.
Hopefully someone can point me in the right direction.
Many thanks
Phil
You can use a $emit event
Vue.component('data-container', {
template: '#data-container',
data() {
return {
siloData: {}
}
},
mounted() {
this.siloData = { name: "Silo", balance: 10 } // loading data
this.$emit('silo-loaded', this.siloData)
}
})
new Vue({
el: '#app',
data() {
return {
data: {}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<data-container class="card" #silo-loaded="val => data = val">
<div slot="content">
Your current balance is {{ data.balance }}
</div>
</data-container>
</div>
<template id="data-container">
<div>
<slot name="content"></slot>
</div>
</template>

vue.js - Dynamically render items on scroll when visible

I have list of 100+ items and rendering takes too much time. I want to show just the once that are visible, and rest on scroll.
What's the best approach?
I have this snippet below, but the vue.set() isn't working.
var dbItems = [{name: 'New item'}, {name:'Another'}, {name:'Third'}];
var app = new Vue({
el: '#app',
data: {
// if I put items : dbItems, then for some reason the Vue.set() doesn't work!!
items : [],
},
methods: {
init: function () {
this.items = dbItems; // we add all items
},
makeItemVisible : function(id) {
console.log("Making visible #"+id);
this.items[id].show = 1;
Vue.set(this.items, id, this.items[id]);
}
}
});
app.init();
app.makeItemVisible(1); // this works
$(document).on('scroll', function(){
// function to show elements when visible
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app" v-cloak>
<button v-on:click="makeItemVisible(0)">MAKE VISIBLE - This button doesn't work</button>
<div class="items" v-show="items.length">
<!-- I dont know why, but (key, item) had to be switched compared to VUE documentation! -->
<div v-for="(key, item) in items">
<div v-if="item.show" style="border:2px solid green;height:700px">
You can see me: {{ item.name }} | ID: {{ key }}
</div>
<div class="item-blank" data-id="{{ key }}" v-else style="border:2px solid red;height:700px">
{{ item.name }} invisible {{ key }}
</div>
</div>
</div>
</div>
Solved.
Edit: This Vue.js is only useable in Chrome... otherwise it is incredibly slow (Firefox is slowest), it works better when loading the whole document in HTML at once.
var dbItems = [{name: 'New item'}, {name:'Another'}, {name:'Third'}];
var app = new Vue({
el: '#app',
data: {
items : dbItems
},
methods: {
makeItemVisible : function(id) {
console.log("Making visible #"+id);
Vue.set(this.items[id], 'show', 1);
}
}
});
function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return (elemTop <= docViewBottom && elemTop >= docViewTop) || (elemBottom >= docViewTop && elemBottom <= docViewBottom);
}
var fn = function(){
$('.item-blank').each(function(){
if(isScrolledIntoView(this)) {
app.makeItemVisible($(this).attr('data-id'));
}
});
};
$(window).scroll(fn);
fn(); // because trigger() doesn't work
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app" v-cloak>
<div class="items" v-show="items.length">
<div v-for="(item, index) in items">
<div v-if="item.show" style="border:2px solid green;height:700px">
You can see me: {{ item.name }} | ID: {{ index }}
</div>
<div class="item-blank" :data-id="index" v-else style="border:2px solid red;height:700px;position:relative;">
{{ item.name }} invisible {{ index }}
</div>
</div>
</div>
</div>