Filter on nested (recursive) data ( Vue 2 ) - vue.js

Here is an example of my JSON data :
"data":[
{
"id":01,
"name":"test",
"parent_id":null,
"children":[
{
"id":15,
"name":"subChild",
"parent_id":21,
"children":[
{
"id":148,
"name":"subSubChild",
"parent_id":22,
"children":[
....
]
}
]
}
]
},
I would like to filter this level by level. I have made this method :
computed: {
filteredData: function () {
let filterData = this.filter.toLowerCase()
return _.pickBy(this.data, (value, key) => {
return _.startsWith(value.name.toLowerCase(), filterData)
})
},
This work for only the first "level" and I tried several solutions but none worked for children.
So, I would like to be able to filter by several levels.
If you have an idea!
Thank you

A recursive function could come in handy for this particular purpose.
Try the following approach, and for better view, click on Full page link next to the Run code snippet button down below.
new Vue({
el: '#app',
data() {
return {
filter: '',
maintainStructure: false,
data: [{
"id": 01,
"name": "test",
"parent_id": null,
"children": [{
"id": 15,
"name": "subChild",
"parent_id": 21,
"children": [
{
"id": 148,
"name": "subSubChild",
"parent_id": 22,
"children": []
},
{
"id": 150,
"name": "subSubChild3",
"parent_id": 24,
"children": []
}
]
}]
}]
}
},
methods: {
$_find(items, predicate) {
let matches = [];
for (let item of items) {
if (predicate(item)) {
matches.push(item);
}
else if (item.children.length) {
let subMatches = this.$_find(item.children, predicate);
if (subMatches.length) {
if (this.maintainStructure) {
matches.push({
...item,
children: subMatches
});
}
else {
matches.push(subMatches);
}
}
}
}
return matches;
},
filterBy(item) {
return item.name.toLowerCase().startsWith(this.filter.toLowerCase());
}
},
computed: {
filteredData() {
return this.$_find(this.data, this.filterBy);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<label>Filter by <code>item.name</code>:</label>
<input v-model.trim="filter" placeholder="e.g. subsub" />
</div>
<div>
<label>
<input type="checkbox" v-model="maintainStructure" /> Maintain structure
</label>
</div>
<hr />
<pre>{{filteredData}}</pre>
</div>
Note that I'm prefixing the function with $_ to sort of mark it as private function (as recommended in this Style Guide) since we're not going to invoke it anywhere else.

Related

Vuejs get dynamic form values

I have select and input component with made by buefy. Everything is ok till I realize how can I get the data.
I'm sort of new on vuejs. So I will be glad if you help me out.
I'm getting dynamic form from backend
So my question is how can get values these inputs and submit to backend again with getOffer() methot.
Here is my codes;
Input.vue
<template>
<b-field :label="fieldLabel">
<b-input
:name="inputName"
:type="inputType"
:maxlength="inputType == 'textarea' ? 200 : null"
></b-input>
</b-field>
</template>
<script>
export default {
name: "Input",
props: {
inputType: {
type: String,
required: true,
default: "text",
},
inputName: {
type: String,
required: true,
},
fieldLabel: {
type: String,
required: true,
}
}
};
</script>
Home.vue
<template>
<div class="container is-max-desktop wrapper">
<div v-for="element in offer" :key="element.id">
<Input
v-model="element.fieldValue"
:value="element.fieldValue"
:fieldLabel="element.fieldLabel"
:inputType="element.fieldType"
:inputName="element.fieldName"
v-if="element.fieldType != 'select'"
class="mb-3"
/>
<Select
v-model="element.fieldValue"
:fieldLabel="element.fieldLabel"
:options="element.infoRequestFormOptions"
:selectName="element.fieldName"
v-if="element.fieldType == 'select'"
class="mb-3"
/>
</div>
<b-button type="is-danger" #click="getOffer()">GET</b-button>
</div>
</template>
<script>
import axios from "axios";
import Select from "../components/Select.vue";
import Input from "../components/Input.vue";
export default {
name: "Home",
data() {
return {
offer: [],
};
},
components: {
Select,
Input,
},
methods: {
getOfferForm() {
axios({
method: "get",
url: `/GETDYNAMICFORM`,
})
.then((response) => {
this.offer = response.data;
})
.catch(() => {
this.$buefy.toast.open({
duration: 3000,
message: "oops",
position: "is-bottom",
type: "is-danger",
});
});
},
getOffer() {
console.log(this.offer);
},
},
created() {
this.getOfferForm();
},
};
</script>
Example Dynamic Form Response like;
[
{
"id": 58,
"fieldLabel": "Name Surname",
"providerLabel": "Name Surname",
"fieldName": "nmsrnm",
"fieldType": "text",
"fieldValue": null,
},
{
"id": 60,
"fieldLabel": "E-mail",
"providerLabel": "E-mail",
"fieldName": "e_mail_60",
"fieldType": "email",
"fieldValue": null,
},
{
"id": 2,
"fieldLabel": "Budget",
"providerLabel": "Budget",
"fieldName": "bdget",
"fieldType": "select",
"fieldValue": "",
"infoRequestFormOptions": [
{
"id": 1,
"orderNum": 0,
"optionValue": 0,
"optionText": "Select",
"minValue": null,
"maxValue": null
},
{
"id": 2,
"orderNum": 1,
"optionValue": 1,
"optionText": "10-30",
"minValue": 10,
"maxValue": 30
}
]
}
]

Why this $emit event is not working between component and parent in Vue.js?

Playing with an API and building a simple dropdown selector in Vue, I am stuck as of why events of a component do not reach the parent. I have looked on the documentation, with no success. I have tried both this.$emit and this.$parent.$emit with no success
Code sample here:
https://jsfiddle.net/behyfnxw/
The point is that method "updated" in the parent is never called when selecting a university from the dropdown menu.
Pretty new to Vue and frontend, any feedback is more than welcome. Thanks!
<!DOCTYPE html>
<div id="app">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<entity-selector :items="univs" :value="u0"></entity-selector>
<entity-selector :items="deps" :value="d0"></entity-selector>
<entity-selector :items="courses" :value="c0"></entity-selector>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
Vue.component('entity-selector', {
props: ['items', 'value'],
data: function () {
return {
selected: "u0",
}
},
created: function () {
console.log("entity selector created just now!");
},
mounted: function () {
console.log("mounted");
this.selected = this.value;
},
watch: {
selected: function (newValue) {
console.log("watch!", newValue);
this.selected = newValue;
console.log("to send event");
this.$emit("updated");
console.log("sent event");
}
},
methods: {
updated: function () {
console.log("selected item:", this.selected);
this.$emit("updated", "univ", this.selected);
console.log("selected item:", this.selected);
},
},
template:`
<p class="col-xs-12">
<select class="form-control" #click="updated" v-model="selected" v-cloak>
<option v-for="item in items" :value="item.id">{{item.fullname}}</option>
</select>
</p>
`
});</script>
<script>
var vm = new Vue({
el: '#app',
data: {
u0: "u0",
d0: "d0",
c0: "c0",
default_univ: {"id": "u0", "fullname": 'choose univ', "total": 0},
default_dep: {"id": "d0", "fullname": 'choose dep', "total": 0},
default_course: {"id": "c0", "fullname": 'choose course', "total": 0},
univs: [{"id": "u0", "fullname": 'choose univ', "total": 0}],
deps: [{"id": "d0", "fullname": 'choose dep', "total": 0}],
courses: [{"id": "c0", "fullname": 'choose course', "total": 0}],
},
created: function () {
console.log("main vue created just now!");
this.fetch_entities("gr", 1);
},
methods: {
fetch_entities: function (parent, filetypeid) {
var main_endpoint = "https://api.trelosfoititis.gr/v1";
var url = main_endpoint + "/list?parentid=" + parent + "&filetypeid=" + filetypeid;
var self = this;
axios.get(url)
.then(function (response) {
self.fetched(parent, filetypeid, response.data.response.data);
});
},
updated: function (entity_type, entity_id){
console.log("updated!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", entity_type, entity_id);
},
fetched: function (parent, filetypeid, results){
console.log("fetched", parent, filetypeid);
if(parent == "gr"){
this.univs = (results.length > 0) ? [].concat([this.default_univ], results) : [this.default_univ];
}
},
}
})
</script>
</html>

How to get the correct object in an nested array in Vue.js?

I use Axios to display a JSON data and I have succeeded. But I want to show an object based on date and time, it shows now all data and I need to filter it.
So I want to look at today's date and show the object based on that, so I want to show the next upcoming event. (24/05/2020)
What I currently have:
Json:
{
"doc": [
{
"data": {
"events": {
"18807612": {
"_dt": {
"_doc": "time",
"time": "18:45",
"date": "14/05/20",
"tz": "UTC",
"tzoffset": 0,
"uts": 1566067500
},
"week": 33,
"teams": {
"home": {
"name": "name 1",
"mediumname": "name1",
"uid": 3014
},
"away": {
"name": "name 2",
"mediumname": "name 2",
"uid": 3020
}
}
},
"18807618": {
"_dt": {
"_doc": "time",
"time": "18:45",
"date": "24/05/20",
"tz": "UTC",
"tzoffset": 0,
"uts": 1566067500
},
"week": 33,
"teams": {
"home": {
"name": "name 1",
"mediumname": "name1",
"uid": 3014
},
"away": {
"name": "name 2",
"mediumname": "name2",
"uid": 3020
}
}
}
}
}
}
]
}
Store:
async loadPosts({ commit }) {
// Define urls pages
const urlEvents = 'http://api.example.com/302020';
// Set pages
const [
responseEvents
] = await Promise.all([
// Responses pages
this.$axios.get(urlEvents)
]);
// variables pages
this.events = responseEvents.data.doc[0].data.events
// mutations pages
commit('SET_EVENTS', this.events)
}
},
mutations: {
SET_EVENTS (state, events) {
state.events = events;
}
}
And to show the data I use this:
import {mapState} from 'vuex';
export default {
name: 'NextMatch',
mounted() {
this.$store.dispatch('loadPosts')
},
computed: {
...mapState([
'events'
])
}
}
<h1>{{events}}</h1>
But this shows all data, and what I try to get is the first upcoming event for the object with the "uid": 3014.
So I want to show the date, time and names of the home and away team.
How can I get the correct data by filtering the data?
Something like this or similar to this should work:
In your Vue component's <template>:
`<h1>{{selectedEvent._dt.date}}</h1>`
In your Vue component's <script>:
props: {
eventID: {
type: Number,
default: 3014
},
},
computed: {
...mapState([
'events'
]),
eventsArr(){
if (!this.events) return {} //make sure Object.entries gets object.
return Object.entries(this.events)
},
selectedEvent(){
const selectedArr = this.eventsArr.find(([eID, e]) => e.teams.home.uid === this.eventID)
return selectedArr[1]
}
}

How to display nested cells in Element UI table?

I am using Element UI. I have nested data that I need to display in table. The problem that I can't understand how to display nested data.
Here is my code:
<el-table :data="tableData" stripe border>
<el-table-column width="170" prop="id"></el-table-column>
<el-table-column width="170">
<template slot-scope="scope">
<!-- -->
</template>
</el-table-column>
</el-table>
data section:
tableData: [
{
"id":1,
"nested": [{"name": "mike"}, {"name": "piter"}]
},
{
"id":2,
"nested": [{"name": "maria"}, {"name": "anna"}]
},
]
};
https://jsfiddle.net/3nhb79qc/
I want to display it's like:
One solution is using span-method in Element UI Table
First, flat your data structure by using computed method:
computed: {
expandData() {
return this.tableData.reduce((a, c) => {
const arr = c.nested.map(item => ({
id: c.id,
name: item.name
}))
a = a.concat(arr)
return a
}, [])
}
},
then your data will become:
[
{
"id": 1,
"name": "mike"
},
{
"id": 1,
"name": "piter"
},
{
"id": 2,
"name": "maria"
},
{
"id": 2,
"name": "anna"
}
]
After that define objectSpanMethod and use it in el-table
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
}
Demo on jsfiddle

Display following values in vue js?

{
"basic": {
"clanId": 3,
"earning": 0.0
},
"length": 2,
"members": [{
"data": {
"basic": {
"name": " ",
"userType": 2,
"username": "sobinmathew"
},
"wallet": {
"amount": 0.0,
"tds": 0.0
}
}
}, {
"data": {
"basic": {
"name": " ",
"userType": 2,
"username": "joselouis"
},
"wallet": {
"amount": 0.0,
"tds": 0.0
}
}
}],
"status": true,
"total_earnings": 0.0,
"clan_divsions": 1,
"checkout_total_listings": 0
}
How can I able to display members[] in my html file?
My current code is. This is my vue js code
<script>
dash = new Vue({
el: '#dash',
data: {
clans: {
basic: [],
members: {
data: {
basic: [],
},
},
},
},
mounted() {
var self = this;
data = {};
$.ajax({
url: "http://127.0.0.1:8000/alpha/get/my/clan/",
data: data,
type: "POST",
dataType: 'json',
success: function (e) {
if (e.status == 1) {
self.clans = e;
console.log(self.clans);
self.members = e.members;
}
},
});
},
})
</script>
My html code is as below. I am using v-for to display the values
<div id="dash">
<div v-for="ibn in clans.members">{{ibn.data.members.data.basic.name}}</div>
</div>
When I try this way, I am getting error? Can anybody please help me to display the following data? How can able to display the name of members from the data
Assuming e looks like the JSON at the top of your question, you should really only need
<div v-for="ibn in clans.members">{{ibn.data.basic.name}}</div>
Your code could do with some cleanup. You should initialise your data to look as much like the fetched data as possible, eg
data: {
clans: {
basic: {}, // an object, not array
members: [] // an array, not an object
}
}
Demo ~ https://jsfiddle.net/u13j8p2h/