A small app where user needs to select either Yes or No for each task. The user should NOT be allowed to leave the page unless they have answered all questions. I have added a button at the bottom of the form which simulates beforeRouterLeave behaviour.
My question is about the simulateBeforeRouteLeave() function. How can do I check if all questions have been answered to allow user to leave?
Ps. This is a snipped from a much larger project. The original data comes from a API and its NOT hard coded like in this example.
CodeSandbox
App.vue
<template>
<parent-component
v-for="task in tasks.data"
:key="task.id"
:taskData="task"
#update-form-data="handleChange"
>
</parent-component>
<button
class="my-2 border-2 border-red-500"
#click="simulateBeforeRouteLeave"
>
simulateBeforeRouteLeave
</button>
</template>
<script>
import parentComponent from "./components/parentComponent.vue";
export default {
components: {
parentComponent,
},
methods: {
simulateBeforeRouteLeave() {
//Need HELP Here! How do I write this IF statement?
if ("Options_NOT_Selected") {
alert("Please select all options");
} else {
console.log("Allow user to leave 'next()'");
}
},
handleChange(e) {
const objIndex = this.tasks.data.findIndex((obj) => obj.id === e.id);
this.tasks.data[objIndex].status = e.status;
},
},
data() {
return {
markAll: false,
tasks: {
data: [
{
id: 1,
name: "Task 1",
status: null,
},
{
id: 2,
name: "Task 2",
status: null,
},
{
id: 3,
name: "Task 3",
status: null,
},
],
},
};
},
};
</script>
parentComponent.vue
<template>
<div class="py-2">
<div>
<input
type="radio"
:id="taskData.id + 'Yes'"
:name="taskData.name"
:value="taskData.value"
#change="updateData"
/>
<label :for="taskData.id">{{ taskData.name }} Yes</label>
</div>
<div>
<input
type="radio"
:id="taskData.id + 'No'"
:name="taskData.name"
:value="taskData.value"
/>
<label :for="taskData.id">{{ taskData.name }} No</label>
</div>
</div>
</template>
<script>
export default {
props: ["taskData"],
methods: {
updateData(e) {
this.$emit("update-form-data", {
id: this.taskData.id,
status: 1,
});
},
},
data() {
return {};
},
};
</script>
Related
I have a code, when you click on a new task, the first one is crossed out, I can’t understand why.
When you click on a new task, the first one is crossed out, and the one I click on should be
<template>
<h4>Today Tasks</h4>
<div class="tasks">
<div
class="task"
:class="{ completed: task.isCompleted }"
v-for="task in tasks"
:key="task._id"
#click="completedHandler(task._id)"
>
<div
class="round"
:class="{
business: task.type === 'business',
personal: task.type === 'personal',
}"
></div>
<span>{{task.name}}</span>
</div>
</div>
<div class="add-task">
<input type="text" placeholder="Название задачи" v-model="taskName" />
<button #click="addTask">+</button>
</div>
</template>
<script>
// import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
data(){
return {
taskName: "",
tasks: [{
_id: "wadawdawdwa",
name: "Create to do app in Vue js",
isCompleted: false,
type: "personal",
}
]
}
},
methods:{
completedHandler:function(taskid){
const currentTask=this.tasks.find((t)=> t._id=taskid);
currentTask.isCompleted = !currentTask.isCompleted;
},
addTask: function(){
this.tasks.push({
_id: Math.random().toString(36).substring(2, 7),
name: this.taskName,
isCompleted: false,
type: "personal",
});
},
},
};
</script>
HelloWorld.vue
<template>
<div>
<div v-for="box in boxes" :key="box.sname">
<BaseAccordian>
<template v-slot:title>{{ box.sname }}</template>
<template v-slot:content>
<div v-for="paint in paints" :key="paint.tname" class="line">
<List :content="matchingdata" :sname="box.sname" />
</div>
</template>
</BaseAccordian>
</div>
</div>
</template>
<script>
import BaseAccordian from "./BaseAccordian.vue";
import List from "./List.vue";
export default {
name: "HelloWorld",
components: {
BaseAccordian,
List,
},
data() {
return {
boxes: [
{
sname: "apple",
},
{
sname: "bananna",
},
{
sname: "grapes",
},
{
sname: "choc",
},
],
paints: [
{
tname: "a",
},
{
tname: "b",
},
{
tname: "c",
},
{
tname: "d",
},
{
tname: "e",
},
],
matchingdata: [
{
matchid: "1",
OverallStatus: "ok",
sname: "choc",
},
{
matchid: "2",
OverallStatus: "notok",
sname: "grapes",
},
],
};
},
};
</script>
BaseAccordion.vue
<template>
<div class="wrapper">
<div class="accordion">
<input type="checkbox" #click="toggleItem" />
<h2 class="title">
<slot name="title"></slot>
</h2>
</div>
<div v-show="show" class="content">
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {
components: {},
data: function () {
return {
show: false,
};
},
methods: {
toggleItem: function () {
this.show = !this.show;
},
},
};
</script>
List.vue
<template>
<div class="">
<div
v-for="match in matchingData"
:key="match.matchid"
:class="{
green: match.OverallStatus === 'ok',
red: match.OverallStatus === 'notok',
}"
>
{{ match.OverallStatus }}
</div>
</div>
</template>
<script>
export default {
components: {},
props: {
content: {
type: Array,
required: true,
},
sname: {
type: String,
required: true,
},
},
data: function () {
return {};
},
methods: {},
computed: {
matchingData() {
return this.content.filter((a) => {
if (a.sname === this.sname) {
return true;
} else {
return false;
}
});
},
},
};
</script>
<style scoped>
</style>
I three arrays called matchingdata,boxes,paints array based on this three arrays, i am trying to filter the array.(nested v-for)
Now, I want to iterate the matchingdata array by comparing it with sname in boxes array. and Common value between matchingdata and boxes array is ""sname""
I tried above logic, and struck with computed property.
Expected Output:-
In List.vue component , i have
{{ match.OverallStatus }} where that field , i want to show,(from the matchingdata array) when user clicked on checkbox.
Taking the ""sname"" the common value from the matchingdata array and the boxes array
code:- https://codesandbox.io/s/damp-pine-27s2kn?file=/src/components/List.vue
As you're passing the sname property as a string via a prop to your List.vue component, you'll just need to use that string in your filter function.
matchingData() {
return this.content.filter((a) => a.sname === this.sname)
},
I've tried this in your codesandbox link and it is giving some output - but I'm not clear enough on what you're trying to achieve to know if this is the intended outcome.
Just incase you're not aware the 'filter' function returns a new array. It's not going to return a 'true/false' which I feel you may be trying to do.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I'm trying to have a component representing a shopping item.
I'll have one of this component for every item in my shopping list.
I don't know how to update the parent data (the shopping list) when the child is edited (the shopping item)
Shopping List
<template>
<div id="app">
<shopping-item
v-for="(item, index) in shoppingList"
:key="index"
:propsName="item.name"
:propsQuantity="item.quantity"
#shoppingItemEdited="handleEdit"
></shopping-item>
</div>
</template>
<script>
import ShoppingItem from "./components/ShoppingItem.vue";
export default {
name: "App",
components: {
ShoppingItem,
},
data() {
return {
shoppingList: [
{ name: "apple", quantity: 8 },
{ name: "banana", quantity: 3 },
{ name: "kiwi", quantity: 7 },
{ name: "peach", quantity: 5 },
],
};
},
methods: {
handleEdit(itemEdited) {
// How to get the index of the shopping-item that has been updated ?
// shoppingList[???] = itemEdited
console.log(itemEdited);
// => {name: "white peach", quantity: "6"}
},
},
};
</script>
Shopping Item
<template>
<div>
<input v-model="name" placeholder="ex: banana" #change="updateParent" />
<input
v-model="quantity"
type="number"
placeholder="ex: 3"
#change="updateParent"
/>
</div>
</template>
<script>
export default {
data() {
return {
name: "",
quantity: null,
};
},
props: {
propsName: String,
propsQuantity: Number,
},
created() {
this.name = this.propsName;
this.quantity = this.propsQuantity;
},
methods: {
updateParent() {
this.$emit("shoppingItemEdited", {
name: this.name,
quantity: this.quantity,
});
},
},
};
</script>
So I have few questions:
How can I know witch component emited the event 'shoppingItemEdited' ? If I knew it, I could find out which shoppingList item I should update.
I red I should not update props in the child, so I create data based on props, is that a standard way of doing that ?
this.name = this.propsName;
this.quantity = this.propsQuantity;
Just pass an index to a handler: #shoppingItemEdited="handleEdit(index, $event)"
No it's not "standard" - created hook is called only once when component is created, so if value of prop changes later (from parent), data will not update. It's probably not a problem in your case but usually its better to use computed:
computed: {
name: {
get() { return this.propsName },
set(value) {
this.$emit("shoppingItemEdited", {
name: value,
quantity: this.quantity,
});
}
}
}
...handle event in parent and the change will propagate (by props) to a child
I am trying to make a custom list filter on input using computed property. In one file I create a widget, in another I use it. Here is the code from the widget creation file:
<template>
<input value="Гарантийный случай"
v-model="searchText"
:class="{'w-autocomplete__input_completed': completed}"
ref="input"
#click="areOptionsVisible = !areOptionsVisible"/>
<div v-if="areOptionsVisible"
:style="{maxHeight: maxHeight, overflow: 'auto', zIndex: zIndex}"
class="w-autocomplete__items">
<div v-for="option in options" class="w-autocomplete__item_first" >
{{ option.name }}
<div v-for="item in option.children" class="w-autocomplete__item"
:class="{'w-autocomplete__item_active': currentIndex === item}"
#mouseenter="setActive(item)"
#keyup.up="changeCurrent('up', item)"
#keyup.down="changeCurrent('down', item)"
#click="doChoose(item)">
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'dropdown',
props: {
placeholder: {
type: String,
required: false,
default: '',
},
options: {
type: Array,
default(){
return []
}
},
},
data: function () {
return {
counter: 0,
currentIndex: null,
completed: false,
chosenItem: null,
areOptionsVisible: false,
searchText: '',
data: [],
}
},
computed: {
options(){
return this.props.options.filter(elem => {
return elem.name.toLowerCase().includes(this.searchText.toLowerCase());
});
},
},
.......
}
</script>
This is how I pass the array to this list in another file:
<template>
........
<div class="complaint-form__line-item">
<div class="form-group">
<label>Гарантийный случай</label>
<dropdown :options="options" />
</div>
</div>
........
</template>
<script>
........
export default {
name: 'complaint-form',
components: {LockedImport, UploadFiles, CarNumberInput, Autocomplete, Dropdown},
props: {
......
}
},
data() {
const complaint = new Complaint();
return {
........
options: [
{name: 'Выход детали из строя в процессе эксплуатации', value: null,
children: [{name: 'Увеличение зазора, люфт (дробь/стуки)', value: 53},
{name: 'Обрыв детали', value: 54}]},
{name: 'Поломка при установке', value: null},
{name: 'Брак до установки', value: null,
children: [{name: 'Недокомплект', value: 55},
{name: 'Заводской брак (замятия, отсутствие резьбы, пробой пыльника и т.д.)',
value: 56}]},
],
}
},
Tell me please why computed doesn't work? Only I add computed and the list is not displayed at all when clicking on the field, but should. That is, it breaks down completely. I want to be filtered when entering text in input
Vue.js cannot have more than one element inside a <template> tag, so I would suggest that you enclose the whole code of the dropdown component within a <div> tag or something of the sort.
Also, and this is just a comment, I would suggest that you use the focus event for the input because with click it will still be showing even if you aren't focusing on the input.
I have a problem with binding checkboxes using Vuex. On checkbox I use v-model with variable which has getter and setter to set or get value in store, the problem is that I get wrong data in store and I don't understand what cause the problem. Checkboxes bind to store property and this property must contain array of id's from checkboxes, but when I click checkbox more than one time it rewrite or remove store values. Can anyone help me to understand why does this happens? Link to jsFiddle.
The code
const store = new Vuex.Store({
state: {
checkboxes: {},
checked: {}
},
mutations: {
setCheckboxes(state, dataObj){
console.log(dataObj);
state.checkboxes = dataObj.data;
let firstElem = dataObj.data[Object.keys(dataObj.data)[0]];
state.checked[firstElem.parent_id] = [firstElem.id];
console.log(state.checked);
},
setTreeState(state, dataObj){
state.checked[dataObj.id] = dataObj.value;
console.log(state.checked);
}
}
});
Vue.component('checkboxTree', {
template: "#checkboxTree",
});
Vue.component('checkboxToggle', {
template: "#checkboxToggle",
data(){
return {
store
}
},
computed: {
value:{
get(){
return store.state.checked[this.checkbox.parent_id];
},
set(val){
store.commit({
type: 'setTreeState',
id: this.checkbox.parent_id,
value: val
});
},
},
},
props: ['checkbox']
});
const app = new Vue({
el: "#app",
store,
data: {
checkboxData: {
...
},
},
mounted(){
this.$store.commit({
type: 'setCheckboxes',
data: this.checkboxData
});
}
})
Template
<div id="app">
<checkbox-tree :checkboxData="checkboxData"></checkbox-tree>
</div>
<template id="checkboxTree">
<div>
<p>checkbox tree</p>
<form>
<ul>
<li v-for="checkbox in $store.state.checkboxes">
<checkbox-toggle :checkbox="checkbox"></checkbox-toggle>
</li>
</ul>
</form>
</div>
</template>
<template id="checkboxToggle">
<div>
<label>{{ checkbox.id }}</label>
<input type="checkbox"
:value="checkbox.id"
:id="'checkbox-' + checkbox.id"
:name="'checkbox-' + checkbox.id"
v-model="value"
>
</div>
</template>
Okay, assuming you want checked to contain ids of selected objects, I had to restructure your code significantly:
const removeFromArray = (array, value) => {
const newArray = [...array];
const index = newArray.indexOf(value);
if (index > -1) {
newArray.splice(index, 1);
return newArray;
}
return array;
}
const store = new Vuex.Store({
state: {
checkboxes: {},
checked: [],
},
mutations: {
addToChecked(state, id) {
state.checked.push(id);
},
removeFromChecked(state, id) {
const newArray = removeFromArray(state.checked, id);
state.checked = newArray;
},
setCheckboxes(state, data) {
state.checkboxes = data;
},
}
});
Vue.component('checkboxTree', {
template: "#checkboxTree",
computed: {
checkboxes() {
return this.$store.state.checkboxes;
},
},
});
Vue.component('checkboxToggle', {
template: "#checkboxToggle",
computed: {
value:{
get(){
return this.$store.state.checked.indexOf(this.checkbox.id) > -1;
},
set(val){
const mutation = val ? 'addToChecked' : 'removeFromChecked';
this.$store.commit(mutation, this.checkbox.id);
},
},
},
props: ['checkbox'],
});
const app = new Vue({
el: "#app",
store,
data: {
checkboxData: {
"5479": {
"id": 5479,
"title": "Место оказания услуг",
"type": "checkbox",
"dependencies": "",
"description": "",
"parent_id": 5478,
"npas": ""
},
"5480": {
"id": 5480,
"title": "Способы оказания услуг",
"type": "checkbox",
"dependencies": "",
"description": "",
"parent_id": 5478,
"npas": "50"
},
"5481": {
"id": 5481,
"title": "Объем и порядок содействия Заказчика в оказании услуг",
"type": "checkbox",
"dependencies": "",
"description": "",
"parent_id": 5478,
"npas": "54"
},
}
},
computed: {
stateRaw() {
return JSON.stringify(this.$store.state, null, 2);
},
},
mounted() {
this.$store.commit('setCheckboxes', this.checkboxData);
const firstElementKey = Object.keys(this.checkboxData)[0];
const firstElement = this.checkboxData[firstElementKey];
this.$store.commit('addToChecked', firstElement.id);
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<div id="app">
<checkbox-tree :checkboxData="checkboxData"></checkbox-tree>
<pre v-text="stateRaw"></pre>
</div>
<template id="checkboxTree">
<div>
<p>checkbox tree</p>
<form>
<ul>
<li v-for="checkbox in checkboxes">
<checkbox-toggle :checkbox="checkbox"></checkbox-toggle>
</li>
</ul>
</form>
</div>
</template>
<template id="checkboxToggle">
<div>
<label>{{ checkbox.id }}</label>
<input
type="checkbox"
:value="checkbox.id"
:id="'checkbox-' + checkbox.id"
:name="'checkbox-' + checkbox.id"
v-model="value">
{{value}}
</div>
</template>
Using this code as an example, you can populate checked however you want to.
Also, a jsfiddle link for you: https://jsfiddle.net/oniondomes/ckj7mgny/