How can I separate values from reusable checkbox in vuejs? - javascript

My question might not match to the problem that I am facing, So, I will explain it in detail here.
This is code for SeparateTechnology.vue. I have removed a few methods to make code shorter.
<template>
<div>
<div v-for="mss in ms" :key="mss.name">
<section class="container shadow-box techno-box" v-if="mss.name==selected">
<p>Review the software criteria below and ensure that you complete each section</p>
<h4>{{mss.name}}</h4>
<div class="row shadow-box">
<div class="col-sm-12 col-lg-6">
<p>Select all that apply</p>
<div class="dropdown">
<input
v-model.trim="inputValue"
class="dropdown-input"
type="text"
placeholder="Search for your Solution"
/>
<div class="dropdown-list" v-show="selected">
<div
v-show="itemVisible(item)"
v-for="item in mss.subMenu"
:key="item"
class="dropdown-item"
>
{{ item }}
<button
class="button button-mini button-dark button-rounded itemLabel"
#click="selectSolution(item,mss)"
>
<i class="icon-line-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-lg-6 rightList">
<div class="dropdown-list" v-show="selected" v-if="mss.selected_solutions!=''">
<div v-for="item in mss.selected_solutions" :key="item" class="dropdown-item">
{{ item }}
<button
class="button button-mini button-dark button-rounded deleteLabel"
#click="deleteSelectedItem(item,mss)"
>
<i class="icon-line-minus"></i>
</button>
</div>
</div>
<div v-else>
<div class="style-msg errormsg">
<div class="sb-msg">
<i class="icon-remove"></i>You have not selected any solutions.
</div>
</div>
</div>
<button
class="button button-mini"
#click="clearUserOptions(mss)"
v-if="mss.selected_solutions.length > 1"
>
<i class="icon-line-cross"></i>Clear all selection
</button>
</div>
<div style="padding:20px;"></div>
</div>
<div class="row">
<div class="col-sm-12 col-md-3 inputTitle">
<h5>Don't see it in the list above:</h5>
</div>
<input
class="col-sm-12 col-md-6"
type="text"
v-model="value"
#keydown.enter="getUserSolution(value,mss)"
placeholder="Enter your solution here.. "
/>
</div>
<div style="padding:20px;"></div>
<div class="row shadow-box">
<h5
class="col-sm-12"
>Identify how the software solution is leveraged within your organization</h5>
<div
v-for="item in mss.selected_solutions"
:key="item"
class="clearfix col-sm-12 col-md-6"
style="padding:20px"
>
<span v-if="mss.isDifferent=='campaign'">
<div class="card">
<h5 class="card-header">{{item}}</h5>
<CheckBox
:groups="campaignMangment"
name="campaignMangment"
:value="item"
classProp="col-sm-12"
#clicked-show-detail="clickedShowDetailModal"
/>
{{item}} and {{productSelected}}
</div>
</span>
</div>
</div>
<button class="btn btn-primary" #click="postUserDetails(mss)">Submit</button>
</section>
</div>
</div>
</template>
<script>
import CheckBox from "../../../components/checkbox/Checkbox";
export default {
name: "technology",
data() {
return {
usageValue: "",
value: "",
campainCheckedNames: [],
checked: "",
productSelected: [],
checkedValues: "",
exists: null,
inputValue: "",
campaignMangment: [
"Business to Customer (B2C)",
"Business to Business (B2B)",
"Customer to Customer (C2C)",
"Customer to Business (C2B)",
"E-Government",
"M-Commerce",
],
};
},
props: ["ms", "selected"],
//method to show all the solutions that contains the user
methods: {
clickedShowDetailModal: function (campainCheckedNames) {
console.log(campainCheckedNames);
this.productSelected.push(campainCheckedNames);
},
},
components: {
CheckBox,
},
};
</script>
This is CheckBox.vue
<template>
<div class="row col-mb-0">
<div
:class="classProp + ' checkbox-margin'"
v-for="singleSelector in groups"
:key="singleSelector"
>
<div>
<input
:id="singleSelector+groupId"
:value="singleSelector"
class="checkbox-style"
type="checkbox"
v-model="checkedValues"
#change="showDetailModal"
/>
<label :for="singleSelector +groupId" class="checkbox-style-3-label">{{singleSelector}}</label>
</div>
</div>
</div>
</template>
<script>
let groupId = 0;
export default {
props: {
groups: Array,
name: String,
classProp: String,
value: String,
},
data() {
return {
groupId: groupId++,
checkedValues: [],
inputs: {},
};
},
methods: {
showDetailModal: function (e) {
this.$set(this.inputs, this.value, e.target.value);
this.$emit("clicked-show-detail", this.inputs);
},
},
};
</script>
<style scoped>
.checkbox-margin {
margin-bottom: 10px;
}
</style>
Right Now, the line {{item}} and {{productSelected}} prints output like below as shown in screenshot
.
Problem: On every click/unclick of checkbox it adds an item to an array and not in the format I want. and if I select the checkbox on left only, it adds that item on right as well as shown in the screenshot above. It is due to the same array declaration, but I couldn't think more than that.
Expected Output: For every selected item, I want to print the list of selected checkboxes in an array format like below.
"selected_solutions": [{
"name": "Adobe Campaign",
"usage": [ "Business to Customer",...]
}, {
"name": "Marin Software",
"usage": ["M-Commerce",...]
}]
Even small hints or tips would be more than appreciated. I don't mind posting code on gist if required. Thank you.

With checkbox-components you want to emit "event.target.checked" likeso:
this.$emit('input', event.target.checked)
That way it'll behave like a checkbox and you can use a v-model on the parent:
<CheckBox v-model="item.checked" ...>
I do not use v-model inside the component and instead just bind :checked="value", but that might be up to preference. But using both :value and v-model could (should?) cause problems because v-model is actually a :value + emitter under the hood (or so I heard).
Anyway, here is my minimal reusable checkbox-wrapper:
<template>
<input type="checkbox" :checked="value" #change="handleChange" />
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'MyCheckbox',
props: {
value: { type: Boolean, default: false }
},
methods: {
handleChange(event) {
this.$emit('input', event.target.checked)
}
}
})
</script>
After that is done you can just filter on checked items:
computed: {
checkedSolutions(){
return this.ms[0].selected_solutions
.filter( solution => solution.checked );
}
}

Related

Create an element dynamically in Vue.js

I want to create a dynamic HTML template on click of a canvas.
This is what I tried:
var btn = document.createElement("div");
btn.classList.add("custom-signature-btn-row");
btn.classList.add("d-flex");
btn.innerHTML = `
<div class="btn-grid d-flex mr-2">
<span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
<button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
Signature
</button>
<div class="signature-dropdow" v-bind:class="{ active: isActive }">
<ul>
<li>
<div class="md-layout-item">
<div class="md-field">
<label for="movie">Assigned to Anyone</label>
<select name="movie" class="md-select" id="movie">
<option class="md-option" value="fight-club">Fight Club</option>
<option class="md-option" value="godfather">Godfather</option>
</select>
</div>
</div>
</li>
</ul>
</div>
</div>
`;
document.addEventListener('click', function(event) {
if (event.target && event.target.classList.contains("pdf-canvas")) {
console.log(event.target.parentNode);
event.target.parentNode.appendChild(btn);
}
});
The problem with above code is v-bind and v-on won't work with this approach.
You can programmatically render the component.
First, move the element as a component, e.g:
Button.vue
<template>
<div class="custom-signature-btn-row d-flex btn-grid d-flex mr-2">
<span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
<button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
Signature
</button>
<div class="signature-dropdow" v-bind:class="{ active: isActive }">
<ul>
<li>
<div class="md-layout-item">
<div class="md-field">
<label for="movie">Assigned to Anyone</label>
<select name="movie" class="md-select" id="movie">
<option class="md-option" value="fight-club">Fight Club</option>
<option class="md-option" value="godfather">Godfather</option>
</select>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: {
active: false,
toggleSignature: {
type: Function,
default: () => {}
}
}
}
</script>
On the canvas click handler, programmatically create a vue Button instance, render, and mount it into the target element.
import Vue from 'vue';
import Button from './Button.vue';
...
document.addEventListener('click', function(event) {
if (event.target && event.target.classList.contains("pdf-canvas")) {
const ButtonClass = Vue.extend(Button);
const buttonInstance = new ButtonClass({
propsData: {
isActive: this.isActive, // pass any data you need here
toggleSignature: this.toggleSignature, // callback
}
});
buttonInstance.$mount();
event.target.parentNode.appendChild(buttonInstance.$el);
}
});
You can refactor your code into an ordinary Vue component:
Vue.component('dynamic-btn', {
data: () => ({
isActive: true
}),
methods: {
toggleSignature() {
console.log('Clicked!')
}
},
template: `
<div class="custom-signature-btn-row d-flex">
<div class="btn-grid d-flex mr-2">
<span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
<button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
Signature
</button>
<div class="signature-dropdow" v-bind:class="{ active: isActive }">
<ul>
<li>
<div class="md-layout-item">
<div class="md-field">
<label for="movie">Assigned to Anyone</label>
<select name="movie" class="md-select" id="movie">
<option class="md-option" value="fight-club">Fight Club</option>
<option class="md-option" value="godfather">Godfather</option>
</select>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
`
});
new Vue({
el: '#app',
data: () => ({
btnCount: 1,
}),
});
Vue.config.productionTip = false;
Vue.config.devtools = false;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="btnCount++">Create Component</button>
<dynamic-btn v-for="(btn, i) in btnCount" :key="i" />
</div>
This way v-bind and v-on will work as normal.
Using SFC syntax:
<template>
<div class="custom-signature-btn-row d-flex">
<div class="btn-grid d-flex mr-2">
<span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
<button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
Signature
</button>
<div class="signature-dropdow" v-bind:class="{ active: isActive }">
<ul>
<li>
<div class="md-layout-item">
<div class="md-field">
<label for="movie">Assigned to Anyone</label>
<select name="movie" class="md-select" id="movie">
<option class="md-option" value="fight-club">Fight Club</option>
<option class="md-option" value="godfather">Godfather</option>
</select>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DynamicBtn',
data: () => ({ isActive: true }),
methods: {
toggleSignature() {
console.log('Clicked!')
}
}
}
</script>
<template>
<div>
<button #click="btnCount++">Create Component</button>
<DynamicBtn v-for="(btn, i) in btnCount" :key="i" />
</div>
</template>
<script>
import DynamicBtn from './DynamicBtn';
export default {
name: 'Parent',
components: { DynamicBtn },
data: () => ({
btnCount: 1,
}),
}
</script>
Vue internally pre-compiles all the templates into "render functions", so they can build the Virtual DOM tree. During this process all the "v-on" and "v-bind" magic happens - that's why it doesn't work when you simply add new node to the DOM directly
What you can do is write your own render function - Vue provides you with the "createElement" function(AKA "h" function, so-called by convention). There you can specify all the Children, Classes, select the HTML tag, handle events etc.
Or a bit simpler solution will be the Dynamic components (<component v-bind:is="myCompName">), probably in combination with "v-html" to handle the "dynamic" part

Bind element inside a for loop Vue not working properly

In the following Vue Component I want to loop through dwarfs array. And as long as I am in the current component, everything is fine (TEST) and also all the following properties are correct.
Currenct_Component.vue :
<template>
<div>
<h2>Stamm: {{ tribeName }}</h2>
<div class="card-container">
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
<!-- TEST -->
<p>{{dwarf}}</p>
<!-- CHILD COMPONENT -->
<app-modal
:showModal="showModal"
:targetDwarf="dwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<!-- <img class="card-img-top" src="" alt="Card image cap">-->
<div class="card-body">
<h3 class="card-title" ref="dwarfName">{{ dwarf.name }}</h3>
<hr>
<ul class="dwarf-details">
<li><strong>Alter:</strong> {{ dwarf.age }}</li>
<li><strong>Waffen:</strong>
<ul v-for="weapon in dwarf.weapons">
<li><span>Name: {{ weapon.name }} | Magischer Wert: {{ weapon.magicValue }}</span></li>
</ul>
</li>
<li><strong>Powerfactor:</strong> {{ dwarf.weapons.map(weapon => weapon.magicValue).reduce((accumulator, currentValue) => accumulator + currentValue) }}</li>
</ul>
<button class="card-button" #click="showModal = true"><span class="plus-sign">+</span> Waffe</button>
</div>
</div>
</div>
<button id="backBtn" #click="onClick">Zurück</button>
</div>
</template>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
onClick() {
this.$emit('backBtn')
},
notifyApp() {
this.showModal = false;
this.$emit('weaponAdded');
}
},
}
</script>
But when I bind the element dwarf to the Child Component <app-modal/> it changes to the next dwarf in the array dwarfs (TEST) - (So as the result when i add a new weapon in the modal-form it gets added to the second dwarf...):
Child_Component.vue :
<template>
<div>
<div class="myModal" v-show="showModal">
<div class="modal-content">
<span #click="$emit('close')" class="close">×</span>
<h3>Neue Waffe</h3>
<!-- TEST -->
<p>{{ targetDwarf }}</p>
<form>
<input
type="text"
placeholder="Name..."
v-model="weaponName"
required
/>
<input
type="number"
placeholder="Magischer Wert..."
v-model="magicValue"
required
/>
<button #click.prevent="onClick">bestätigen</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
weaponName: '',
magicValue: '',
}
},
props: ['showModal', 'targetDwarf'],
methods: {
onClick() {
if(this.weaponName !== '' &&
Number.isInteger(+this.magicValue)) {
let newData = {...this.dwarf};
newData['weapons'] = [
...this.dwarf['weapons'],
{
"name": this.weaponName,
"magicValue": Number.parseInt(this.magicValue)
},
];
this.$http.post("https://localhost:5019/api", newData)
.then(data => data.text())
.then(text => console.log(text))
.catch(err => console.log(err));
this.$emit('weaponAdded');
} else {
alert('You should fill all fields validly')
}
},
}
}
</script>
It looks like you have the <app-modal/> component inside of the v-for="dwarf in dwarfs" loop, but then the control for showing all of the modal components created by that loop is just in one variable: showModal. So when showModal is true, the modal will show each of the dwarfs, and I'm guessing the second dwarf's modal is just covering up the first one's.
To fix this, you could move the <app-modal /> outside of that v-for loop, so there's only one instance on the page, then as part of the logic that shows the modal, populate the props of the modal with the correct dwarf's info.
Something like this:
<div class="card-container">
<div class="card" v-for="dwarf in dwarfs" :key="dwarf.name">
<p>{{dwarf}}</p>
<div class="card-body">
<button
class="card-button"
#click="() => setModalDwarf(dwarf)"
>
Waffe
</button>
</div>
</div>
<!-- Move outside of v-for loop -->
<app-modal
:showModal="!!modalDwarfId"
:targetDwarf="modalDwarfId"
#close="modalDwarfId = null"
#weaponAdded="onDwarfWeaponAdd"
/>
</div>
export default {
//....
data: () => ({
modalDwarfId: null,
)},
methods: {
setModalDwarf(dwarf) {
this.modalDwarfId = drawf.id;
},
onDwarfWeaponAdd() {
//...
}
},
}
You could then grab the correct dwarf data within the modal, from the ID passed as a prop, or pass in more granular data to the modal so it's more "dumb", which is the better practice so that the component isn't dependent on a specific data structure. Hope that helps
Courtesy of #Joe Dalton's answer, a bit alternated for my case:
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
...
<button class="card-button" #click="setModalDwarf(dwarf)"><span class="plus-sign">+</span> Waffe</button>
<div>
<app-modal
:showModal="showModal"
:targetDwarf="currentDwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
currentDwarf: null,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
setModalDwarf(dwarf) {
this.currentDwarf = dwarf;
this.showModal = true;
},
...
}
</script>

Vue js send image blob between child components and show preview of image

I have Three Component with this structure
1.ParrentComponent(
-1.ChildComponent
-2.Child
)
1.Child component is loading image via file input and making preview in that controller. after i click submit button i need to send this blob image to second child and show that preview there.
My problem is when i sent image to second child url in image is the same but image not show just alt is showing
1.Child:
<template>
<div id="insert-component">
<div id="insert-new" >
<h2 class="text-md-left">Nová kategória</h2>
<div class="mt-2 text-left">
<a href="#" id="img-button" class=" d-flex flex-wrap" v-on:click.stop="loadImage()">
<img v-bind:src="category_img" alt="logo" id="preview">
<input type="file" class="d-none" id="load-category-image" v-on:input="handleFileSelected">
<button class="btn btn-dark btn-block" >Pridať obrázok</button>
</a>
<small class="text-danger d-none error" id="img-error">Súbor musí byť png, jpg alebo jpeg</small>
</div>
<div class="form-group mt-2 text-left">
<div>
<label for="category_name">Názov kategórie:</label>
<input type="text" required name="name" class="form-control" v-model="category_name" id="category_name">
<small class="text-danger d-none error" id="name-error">*Názov je povinný</small>
</div>
<label for="category_description" class="mt-2">Popis kategórie:</label>
<textarea name="description" class="form-control" rows="4" v-model="category_description" id="category_description">
</textarea>
</div>
</div>
<button class="btn btn-success btn-block my-2" v-on:click.prevent="submit()">Pridať kategóriu</button>
</div>
</template>
<script>
export default {
name: "InsertComponent",
props: [ 'updateTableData' ],
data: function () {
return {
category_name: "",
category_description: "",
category_img:"/storage/images/no_image.png",
file:null
}
},
methods: {
loadImage(){
document.getElementById('load-category-image').click();
},
submit(){
if(this.checkIfEmptyById('category_name')){
this.showErrors('name-error');
return
}
let item = this.createNewItem();
this.updateTableData(item);
this.clearInputs();
},
createNewItem(){
return {
category_img: this.category_img,
category_name: this.category_name,
category_description: this.category_description,
created_at: null,
updated_at: null,
id: null,
file:this.file
};
},
clearInputs(){
this.category_name="";
this.category_description="";
this.category_img="/storage/images/no_image.png";
},
handleFileSelected() {
let loadedFile = document.getElementById('load-category-image').files[0];
if(this.checkIfFileIsImage(loadedFile))
{
this.file = loadedFile;
//this.category_img="/storage/images/"+loadedFile.name;
this.changeImagePreview();
}
else{
//show image error
let imgError = document.getElementById('img-error');
imgError.classList.remove('d-none');
}
},
checkIfFileIsImage(file){
const acceptedImageTypes = ['image/jpg', 'image/jpeg', 'image/png'];
return acceptedImageTypes.includes(file['type']);
},
changeImagePreview(){
let loadedFile = document.getElementById('load-category-image').files;
this.category_img = URL.createObjectURL(loadedFile[0]);
},
},
}
</script>
Second child:
<template>
<div class="text-center">
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(category_img)="data">
<img v-bind:src="'/storage/images/'+data.item.category_img" alt="img" width="50px" height="50px" class="rounded-circle">
</template>
<template v-slot:cell(category_name)="data">
{{data.item.category_name | capitalize}}
</template>
<template v-slot:cell(category_description)="data">
{{data.item.category_description | capitalize}}
</template>
<template v-slot:cell(actions)="data">
<div class="d-flex justify-content-center">
<i class="fas fa-edit"></i>
<i class="fas fa-times-circle"></i>
</div>
</template>
</b-table>
</div>
</template>
<script>
export default {
name:"TableComponent",
props: ['tableData'],
data() {
return {
fields: [
{
key: 'category_img',
label:'Img',
sortable: false
},
{
key: 'category_name',
label:'Name',
tdClass: 'capitalize-first-letter',
sortable: true,
variant: 'dark'
},
{
key: 'category_description',
thClass: 'd-none d-md-block',
tdClass: 'd-none d-md-block text-left',
label:'Description',
sortable: false,
},
{
key: 'actions',
sortable: false,
}
],
items: this.tableData
}
},
Parrent component just passing data between these component it is not important data are passing good. Problem is just that image. Thx for help
This is looks like: (right side child 1, left side child 2)
Correct if Im wrong but in your Child you are passing a function as a prop which in vue is an anti pattern. You should always follow the props down event up design pattern.
Anyway, continue to your problem. On your Child 2 you have the following line
items: this.tableData
This line will assign the value of this.tableData to items. This is only assigned on the created hook part of the component. If this table data changes (which I'm fairly sure it does) the item variable won't be updated. You should have a watch watching the prop and re-assign to item
watch: {
tableData: (value) => {
this.item = value;
}
}

Computed prop in VueJS not updated until input lost focus

same as title says. A computed property from a object it´s not reactive until the input box lost the focus.
But if the object is in data, the value changes as soon as changed in the input.
I created a codepen with the example: https://codepen.io/Albvadi/pen/QWwMOQV
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
}
},
computed: {
awayTeam: function() {
return {
name: "GER",
desc: "Germany",
score: 0
};
},
},
mounted() {
console.log("Component mounted.");
}
});
If you change the first input, the data value is changed inmediate.
If you change the second input, the computed value if changed only when input lost focus.
How can I solve this?
== EDIT ==
Thanks for your answers!
I know that putting awayteam in the data section solves the problem, it was just an example to simplify my problem.
My problem was that along with the two results, I wanted to have a computed property to be able to issue to another component.
I edited the codepen with the final result: https://codepen.io/Albvadi/pen/jOELYwN and it´s working correctly.
But #Sami Kuhmonen comments that computed properties are only read properties but in my checkFinalResult I´m setting the computedMatch..
It´s the right approach??
Computed properties should be considered read only. Every time there's a change in the properties they reference their value is computed from other values and all changes are lost (or at least should be thought that way, there may be cases where some values remain). Even though sometimes changing their properties seems to work it's not guaranteed and they may change whenever.
In the codepen you already have the computed property referencing other properties from the object, so you'll have to add all the things you want to change similarly and then change that value. This will cause the computed value to be re-evaluated and the changes will be visible and persisted.
Note that building a computed object from a lot of separate things might not be the most performant way and it depends on the situation how it should be handled. If the values are only used within the component then it's easy to just use them directly but if you need an object of specific form for something and need it reactive then computed property might be the way to go.
In your case, why this computed property does not update DOM element instantly,because it has no data reference with Vue instance.Here, it return some static value only,which was not the computed property's purpose. computed property came to purpose,when you need to calculate or compute some decision on the basis of your data property of your vue instance.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Spain",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Why do you use a computed property for this task? I would simply do it this way.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Germany",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});

How do you implement the "double click to edit todo task" in VueJS?

I've been making a simple To Do app with VueJS. I have the add new todo, delete todo and mark as done functionalities done, but I'm struggling with the "Double Click to edit a task" feature.
I've added an input field which should appear when the user double clicks on the task to edit it but nothing seems to happen? Any help would be awesome :)
App.vue:
<template>
<div id="app">
<div class="container">
<div class="row">
<h1>VueJS To Do Manager:</h1>
</div>
</div>
<div class="container">
<div class="row">
<input class="new-todo input-group col-xs-12"
placeholder="Enter a task and press enter. Use the checkbox to mark them as done."
v-model="newTodo"
#keyup.enter="addTodo">
</div>
</div>
<TodoCard v-for="(todo, key) in todos"
:todo="todo"
:key="key"
#remove="removeTodo(key)"/>
</div>
</template>
<script>
import TodoCard from './components/TodoCard'
export default {
data () {
return {
todos: [],
newTodo: ''
}
},
components: {
TodoCard
},
methods: {
addTodo: function () {
// Store the input value in a variable
let inputValue = this.newTodo && this.newTodo.trim()
// Check to see if inputed value was entered
if (!inputValue) {
return
}
// Add the new task to the todos array
this.todos.push(
{
text: inputValue,
done: false
}
)
// Set input field to empty
this.newTodo = ''
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
}
}
</script>
TodoCard.vue component:
<template>
<div id="todo">
<div class="container">
<div class="row">
<input class="check" type="checkbox" />
<h3 class="col strikethrough"
#dblclick="editTodo(todo)">{{ todo.text }}</h3>
<div v-show="todo.edit == false">
<input v-show="todo.edit == true"
v-model="todo.title"
v-on:blur="todo.edit=false; $emit('update')"
#keyup.enter="todo.edit=false; $emit('update')">
</div>
<hr>
<button #click="removeTodo"
type="button"
class="btn btn-danger btn-sm">Delete</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['todo'],
methods: {
removeTodo: function (todo) {
this.$emit('remove')
},
editTodo: function (todo) {
this.editedTodo = todo
}
}
}
</script>
I think you don't set todo.edit to true when double click todo description. Moreover the div that contains the todo edit input has v-show="todo.edit == false" while it should be v-show="todo.edit == true" or just v-show="todo.edit" if you are sure that todo.edit is always a boolean.

Categories

Resources