Search categories with input field in VUE.js - vue.js

I have a little problem, if someone could help me I would be very grateful.
I have to make a list of categories and subcategories for each category. I have to put an input field which will search through the titles of categories and subcategories, but I do not know how to do that in vue.js
For example if I have something like this
Category 1 Fruit with subcategories Banana, Apple.
Category 2 Books with subcategories Drama, Romance
If I write fruit in the search field, it should return Fruit (Banana, Apple). If I write Romance in the search field, it should return Books (Romance)
Thanks in advance!
<table>
<input type="text" v-model="search" placeholder="Search">
<div> <p>Fruit</p> </div>
<tr v-for="fruit in fruits" :key="fruit.id">
<div v-if="fruit.id == 1" >
{{fruit.name}}
</div>
<div v-if="fruit.id == 2" >
{{fruit.name}} </div>
</tr>
<br>
<div> <p>Books</p> </div>
<tr v-for="book in books" :key="book.id">
<div v-if="book.id == 1" >
{{book.name}}
</div>
<div v-if="fruit.id == 2" >
{{book.name}} </div>
</tr>
</table>
data() {
return {
search:'',
fruititems: [
{name:Banana, id:1},
{name:Apple}, id:2}
],
bookitems: [
{name:Drama, id:1},
{name:Romance}, id:2}
],
}}
computed:{
fruits() {
return this.fruits.filter(fruit =>
fruit.name.toLowerCase().includes(this.search.toLowerCase()))},
books() {
return this.books.filter(book =>
fruit.name.toLowerCase().includes(this.search.toLowerCase()))},
}

I belive something like this ?
https://stackblitz.com/edit/vue2-vue-cli-pvbeex?file=src%2FApp.vue
to keep code history
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div>
<input type="text" v-model="search" />
<div v-for="category in getfiltered">
<h2>{{ category.name }}</h2>
<ul>
<li v-for="sub in category.subCategories">
{{ sub }}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data: () => ({
search: '',
categories: [
{
name: 'fruits',
subCategories: ['Apple', 'Banana'],
},
{
name: 'books',
subCategories: ['Drama', 'Romance'],
},
],
}),
computed: {
getfiltered: function () {
return this.categories.filter((category) => {
return (
this.search.length < 2 ||
category.name.includes(this.search) ||
category.subCategories.find((sub) => sub.includes(this.search))
)
})
},
},
}
</script>

Related

nesting an object inside an object

Looking for some tips on how to nest objects inside objects using a form. My form currently changes the key and value of an object. However, I'm now wanting a second button to be able to create a child (correct termanology?)form input. below you can see an example. I've spent the morning looking at props but I'm unsure if this is the correct way to go, any suggestions are greatly appriciated
{
"color": "black",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,255,1],
"hex": "#000"
}
},
<form id="app">
<h1>
Title goes here
</h1>
<hr>
<div class="row">
<div class="col-xs-2">
<button type="button" v-on:click="addNewObject" class="btn btn-block btn-success">
(Add +) Parent
</button>
</div>
<div class="col-xs-10 text_info">
Click 'Add +' to add an object
</div>
</div>
<div v-for="(object, index) in objects">
<div class="row">
<div class="col-xs-1">
<label> </label>
<button type="button" v-on:click="removeObject(index)" class="btn btn-rem btn-block btn-danger">
Delete
</button>
<button type="button" v-on:click="addNewChildObject()" class="btn btn-rem btn-block btn-success btn-suc">
add { }
</button>
</div>
<div class="form-group col-xs-7">
<div class="test">
<select v-model="object.type" class="selectBox classic">
<option value="" disabled selected hidden>Choose Datatype</option>
<option v-for="type in types"> {{ type }}</option>
</select>
<input v-model="object.name" :name="'objects[' + index + '][name]'" type="string" class="form-control" placeholder="Enter key">
<input v-model="object.dataValue" :name="'objects[' + index + '][dataValue]'" type="string" class="form-control" placeholder="Enter value">
</div>
</div>
</div>
</div>
<hr>
<div>
<pre v-if="seen">{{ mappedObjects }}</pre>
</div>
<button type="button" class="btn-primary" v-on:click="seen = !seen">{{ seen ? 'Click to Hide the JSON' : 'Click to Show the JSON' }}</button>
</form>
const getDefaultObject = () => ({
name: '',
dataValue: '',
type: ''
})
const app = new Vue({
el: '#app',
computed: {
mappedObjects() {
return this.objects.map(({
name,
dataValue,
type
}) => ({
[name]: dataValue,
type
}))
}
},
props: {
},
data() {
return {
seen: false,
types: ['string', 'character', 'number', 'int', 'floating-point', 'boolean', 'date;'],
objects: []
}
},
methods: {
addNewObject: function() {
this.objects.push(getDefaultObject())
},
removeObject: function(index) {
Vue.delete(this.objects, index);
},
addNewChildObject: function () {
}
}
})
If you want n forms with n children just create a model for it following that same structure and pass props to the form.
parent = {
...props,
children: []
}
child = {
...props
}
If the forms are too complex (or a little complex really), split them in separate components and pass children as props.
If you want to use the same form both in parent and children take a look at slots, they will allow you to create flexible layouts.

Array of Dynamic Dependent Select Box in Vue.js

I have an array of Depend select box which contains Universities and courses. Each university has its own course. and I have built the course dropdown which depends on the university. I can successfully get university course from server request but the problem is when I change the select university it's changing all course fields. How I can get rid of the problem please give me some ideas. Thanks
<template>
<form #submit.prevent="handleSubmit">
<div class="col col-md-12">
<div v-for="(interest, index) in interests" :key="index" class="row">
<div class="col col-md-6">
<div class="form-group mb-4">
<label for="select-ins">Interested Universities </label>
<select
v-model="interest.institute_id"
class="form-control"
#change="onChangeUniversity($event)"
>
<option disabled value="">Select a University</option>
<option
v-for="institute in institutes"
:key="institute.id"
:value="institute.id"
>
{{ institute.institute_name }}
</option>
</select>
</div>
</div>
<div class="col col-md-6">
<div class="form-group mb-4">
<label>Interested Course</label>
<select
v-model="interest.course_id"
class="form-control"
#change="onChangeCourse($event)"
>
<option disabled value="">Select a Course</option>
<option
v-for="course in courses"
:key="course.id"
:value="course.id"
>
{{ course.course_name }}
</option>
</select>
</div>
</div>
<div class="col col-md-12 text-right">
<div class="row ml-4">
<div v-show="index == interests.length - 1">
<button
class="btn btn-warning mb-2 mr-2 btn-rounded"
#click.prevent="add"
>
Add
</button>
</div>
<div v-show="index || (!index && interests.length > 1)">
<button
class="btn btn-danger mb-2 mr-2 btn-rounded"
#click.prevent="remove"
>
Remove
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</template>
<script>
export default {
data() {
return {
institutes: [],
courses: [],
interests: [
{
institute_id: "",
course_id: "",
},
],
};
},
mounted() {
axios.get("/institues").then((res) => {
this.institutes = res.data;
});
},
methods: {
onChangeUniversity(event) {
let universityId = event.target.value;
axios.get(`/institute-course/${universityId}`).then((res) => {
this.courses = res.data;
});
},
add() {
this.interests.push({
institute_id: "",
course_id: "",
});
},
remove(index) {
this.interests.splice(index, 1);
},
},
};
</script>
check screenshot
http://prntscr.com/115mkn5
lets start at your mounted hook.
you call for a API to receive all available institutes. so far so good. each institute got is own ID, this is important for later, lets keep that in mind.
now your using a function which will then call on a "change" event, like onChangeUniversity this is a good way on preventing to overload data in a page, nice idea just to fetch data only when they are needed.
then comes the tricky part which makes it difficult for you and everyone else reading your code.
you have this courses array in your data which normally belongs to the related institute. this array should not be handled as a second array apart from institutes, it should be a child of it.
like check this data structur:
institutes: [
{
institute_name: "WhatEver1",
id: 0,
courses: [{ course: 1 }, { course: 2 }, { course: 3 }],
}
]
instead of this:
institutes: [
{
institute_name: "WhatEver1",
id: 0,
},
],
courses: [{ course: 1 }, { course: 2 }, { course: 3 }],
the first option above is a good nested way to display your data as loop inside a loop.
which means you have to push your courses inside your institute of choice with the belonged id.
your onChangeUniversity function should than do something like this:
onChangeUniversity(event) {
let universityId = event.target.value;
axios.get(`/institute-course/${universityId}`).then((res) => {
const foundedId = this.institutes.findIndex(institute => institute.id === universityId)
this.institutes[foundedId].courses = res.data
});
},
after that its much easier to iterate over and display the data inside the options. and i am sure you will not have that issue anymore.
just try it like that first and give feedback.
Update
<div class="form-group mb-4">
<label>Interested Course</label>
<select
v-model="interest.course_id"
v-if="institute.courses.length !== 0" <-------HERE
class="form-control"
#change="onChangeCourse($event)"
>
<option disabled value="">Select a Course</option>
<option
v-for="course in institute.courses"
:key="course.id"
:value="course.id"
>
{{ course.course_name }}
</option>
</select>
</div>
you need a render condition to stop your courses loop from being iterated when there is no data inside.
also make sure you await till the fetching of courses is completed.
async onChangeUniversity(event) {
let universityId = event.target.value;
await axios.get(`/institute-course/${universityId}`).then((res) => {
this.courses = res.data;
});
},
and also in your mounted hook
async mounted() {
await axios.get("/institues").then((res) => {
this.institutes = res.data;
});
},
if you still struggle please give me a CodeSandbox of your current code.

Vue-formulate - Group item collapsible / toggle collapse

Is there a possibility to make group item collapsible?
<FormulateInput type="group" name="employments" :repeatable="true" label="Employments"
add-label="+ Add Employment" #default="groupProps">
<!-- Clickable area -->
<div class="group text-sm font-semibold py-2 cursor-pointer relative" #click="groupProps.showForm">
....
</div>
<!-- Nested form: must be collapsible accordion -->
<div class="nested-form" v-show="groupProps.showForm">
....
</div>
</FormulateInput>
I thought to add showForm property to the group context.
For this I need to do Custom input types or is there some other way?
If anyone has any other ideas?
Thanks
I figured it out with the gist of #jpschroeder.
CollapsableGroupItem.vue:
<template>
<div class="group-item" :data-is-open="itemId === showIndex">
<div class="group group-item-title text-sm font-semibold py-2 cursor-pointer relative hover:text-blue-400" #click="toggleBody">
<slot name="title" v-bind="groupItem">#{{ context.index }}</slot>
</div>
<div class="group-item-body" v-show="itemId === showIndex">
<slot name="body">
<FormulateInput type="pelleditor" name="description" label="Description"/>
</slot>
</div>
</div>
</template>
<script>
export default {
name: "CollapsableGroupItem",
props: {
context: {
type: Object,
required: true,
},
showIndex: {
type: [Number, String],
required: true,
},
groupItem: {
type: Object,
required: true,
},
},
data () {
return {
itemId: this.context.name + this.context.index
}
},
created: function () {
// show current item
this.$emit("open", this.itemId);
},
methods: {
toggleBody() {
if (this.itemId === this.showIndex) {
// dont show anything
this.$emit("open", -1);
} else {
// show this one
this.$emit("open", this.itemId);
}
},
}
};
FormTemplate.vue:
<CollapsableGroupItem
:context="context"
:show-index="showIndex"
:group-item="educations[context.index]"
#open="showIndex = $event"
>
<template v-slot:title="education">
<span v-if="education.institution || education.degree"
>
{{ education.institution }}
<span v-if="education.institution && education.degree">at</span>
{{ education.degree }}
</span>
...
</template>
<template v-slot:body>
...
</template>
</CollapsableGroupItem>
Maybe it will help someone else or will be useful 😀

Populate text value when option selected from dropdown in vue

I have a vue app and new to vue. I have a dropdown which is populated via an axios endpoint. This returns 2 items. What I'm trying to do is, if 'APC' is selected, then populate a text value with an attribute value returned in my array but this is where I may be overthinking.
My thinking is that I need to iterate over the items again but if a condition is met display the value.
Below is my whole page code
<template>
<div>
<div class="form-group row">
<label class="col-sm-3 col-form-label" for="courierList">Courier <span class="text-danger">*</span></label>
<div class="col-sm-7 shipping-options">
<select id="courierList" class="form-control" v-model="selectedCourier">
<option value='courierDefault'>Please select a courier</option>
<option :value="courier.name.toLowerCase()" v-for="(courier, index) in couriers" :key="courier.index">
{{ courier.name }}
</option>
</select>
</div>
</div>
<span v-if="selectedCourier != 'courierDefault'">
<div class="form-group row">
<b class="col-sm-3" for="cutOff">Order cut-off</b>
<div class="col-sm-7 shipping-options" v-for="(cutOff, index) in couriers" :key="cutOff.index">
{{ cutOff.cut_off }}
</div>
</div>
</span>
</div>
</template>
<script>
export default {
name: 'CourierSelect',
data() {
return {
couriers: [],
selectedCourier: 'courierDefault'
}
},
mounted() {
this.fetchCouriers();
},
methods: {
fetchCouriers() {
axios.get('/CHANGED_FOR_SECURITY')
.then((response) => {
this.couriers = response.data.couriers;
console.log('axios_couriers', this.couriers)
})
.catch((error) => {
VueEvent.$emit('show-error-modal', 'cartFethchCouriers');
console.log(error);
});
}
}
}
</script>
My console.log for 'axios_couriers' gives
Then when I select 'APC' my page displays as
But what I need is for the 'cut_off' value (displayed in the console screenshot) for the 'APC' Array object to display only. The value should be 16:30
Is there a way to do this as a Computed prop or something?
As you suggested a computed should indeed work.
One way would be:
currentCutOff() {
return this.couriers.find(c => c.name == this.selectedCourier).cut_off;
}
This tries to find the courier from your array which equals the name of the currently selectedCourier.
There is a much simplier solution with vuejs data binding.
Check this code:
const vm = new Vue({
el: '#app',
data() {
return {
items: [{
id: 1,
name: 'AAA',
time: '14:00'
},
{
id: 2,
name: 'BBB',
time: '18:00'
}
],
selected: null
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<select v-model="selected">
<option disabled value="null">Please select one</option>
<option v-for="item in items" v-bind:value="item">
{{ item.name }}
</option>
</select>
<div>Selected: {{ selected? selected.time : 'nothing selected' }}</div>
</div>

Input binding in v-for

I want to bind values from input radio button generated by v-for.
I have tried use v-model to bind them with variables question_1, question_2, question_3 in data().
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="question_obj.variable" v-model="question_obj.variable" value="yes" >
<span>Yes</span>
</label>
<label>
<input type="radio" :name="question_obj.variable" v-model="question_obj.variable" value="no" >
<span>No</span>
</label>
</div>
</div>
</template>
<script>
export default {
name: 'radioButtons',
data () {
return {
question_1: '',
question_2: '',
question_3: '',
questions: [
{ question: 'Question 1', variable: 'question_1'},
{ question: 'Question 2', variable: 'question_2'},
{ question: 'Question 3', variable: 'question_3'},
]
}
}
}
</script>
I would like the value to be saved in data () after selecting the radio button.
Use index in v-for for v-model changes so it changes question object properties, not their instance:
new Vue({
el: '#app',
data () {
return {
//question_1: '', //don't need question_1, 2 and 3
// question_2: '',
// question_3: '',
questions: [
{ question: 'Question 1', variable: 'no'}, //notice that I have stored default values in question. Also consider renaming variable to answer to preserve good semantics.
{ question: 'Question 2', variable: 'yes'},
{ question: 'Question 3', variable: 'no'},
]
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</div>
Note: There is no need to store answers in question_1, question_2, question_3, since you might have more than 3 questions, and it won't be efficient.
Better way is to store answer in variable property value.
So in component it will look like this:
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</template>
<script>
export default {
name: 'radioButtons',
data () {
return {
//question_1: '', //don't need question_1, 2 and 3
// question_2: '',
// question_3: '',
questions: [
{ question: 'Question 1', variable: 'no'}, //notice that I have stored default values in question. Also consider renaming variable to answer to preserve good semantics.
{ question: 'Question 2', variable: 'yes'},
{ question: 'Question 3', variable: 'no'},
]
}
},
}
</script>
<style>
</style>
Try this code.
export default {
name: 'radioButtons',
data () {
return {
radio_data: {
'question_1': '',
'question_2': '',
'question_3': ''
},
questions: [
{ question: 'Question 1', variable: 'question_1'},
{ question: 'Question 2', variable: 'question_2'},
{ question: 'Question 3', variable: 'question_3'}
]
}
},
}
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="radio_data[questions[index].variable]" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="radio_data[questions[index].variable]" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</template>
Store your key, question, and answer in the same object in your array of questions. If you want to get the selected values as an object, use a method to reduce them into an appropriate value. In the example code below I've included such a method, as well as live JSON output to see the result.
<template>
<div id="radioButtons">
<div
v-for="row in questions"
:key="row.key"
class="form-group form-radio"
>
<span>{{ row.question }} {{ row.key }}</span>
<br />
<label>
<input
type="radio"
:name="row.key"
v-model="row.answer"
value="yes"
/>
<span>Yes</span>
</label>
<label>
<input
type="radio"
:name="row.key"
v-model="row.answer"
value="no"
/>
<span>No</span>
</label>
</div>
<pre>{{ JSON.stringify(getAnswers(), null, 2) }}</pre>
</div>
</template>
<script>
export default {
name: "radioButtons",
data() {
return {
questions: [
{ key: "question_1", question: "Question 1", answer: null },
{ key: "question_2", question: "Question 2", answer: null },
{ key: "question_3", question: "Question 3", answer: null },
],
};
},
methods: {
getAnswers() {
return this.questions.reduce((acc, cur) => {
acc[cur.key] = cur.answer;
return acc;
}, {});
},
},
};
</script>