Array and Object Problems Vue - vuejs2

Hi this is my first post on here so be kind, Im new to vue and cant seem to get this to work and its slowly driving me nuts! I have a large array of data and cherry picking what i want into a table. The last col of the table is a drop down menu of reports. Now in current year reports the data is listed as so:- (I've removed a lot of data to shorten it)
...
"reports": {
"reports_entry": {
"report_title": "Tournament Entries",
"report_url": "some url"
}
}
},
In Previous years its as follows:-
...
reports": {
"reports_entry": [
{
"report_title": "Tournament Entries",
"report_url": "https://alps.ocs-ffg.com/tic/tmentry.cgi?tourn=QS19~season=2019~alphaorder~"
},
{
"report_title": "Round 1 Draw",
"report_url": "Some Url"
},
{
"report_title": "Round 1 Scores Only",
"report_url": "Some Url"
},
{
"report_title": "Round 2 Draw",
"report_url": "Some Url"
},
{
"report_title": "Round 2 Scores Only",
"report_url": "Some Url"
},
{
"report_title": "Round 2 Scoreboard",
"report_url": "Some Url"
},
{
"report_title": "Final Result",
"report_url": "Some Url"
},
{
"report_title": "Scoring Statistics",
"report_url": "Some Url"
},
{
"report_title": "Course Statistics",
"report_url": "Some Url"
}
]
},
When i display the dropdown menu i can either get this year working but not previous or the other way around I know the problem is with the v-if being and array or object and tried so many different ways and nothing is working please help! my code :-
<template>
<b-card-body class="text-center">
<b-table :items="data" :fields="fields">
<template #cell(reports)="row">
<b-nav card-header pills>
<b-dropdown v-if="!row.item.reports.length === null " text="report">
<b-dropdown-item
v-for="reports in row.item.reports"
:key="reports"
:title="reports"
>{{ reports.report_title }}</b-dropdown-item
>
</b-dropdown>
<b-dropdown v-else text="report">
<b-dropdown-item
v-for="reports in row.item.reports.reports_entry"
:key="reports"
:title="reports"
>{{ reports.report_title }}</b-dropdown-item
>
</b-dropdown>
</b-nav>
</template>
</b-table>
</b-card-body>
</template>
<script>
export default {
name: "scheduleTable",
props: ["id", "fields", "data", "currentSeason"],
};
</script>

Sorted the problem using Array.isArray(row.item.reports.reports_entry)

Related

Nesting in VueDraggable is not not working

here's my problem :
I'm using the VueDraggable library in order to drag and drop elements between a DragBoard.vue and a DropBoard.vue, and a specific type of element should allow to be nested when it is in the DropBoard.
I'm going to take this element as an example :
"Grouped Items"
To do that I've followed this example : https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/nested-example.vue
And this is what I get when I drop "Grouped Items" into the DropBoard.vue :
IMG
As you can see, the DropBoard appears a second time inside Grouped items for whatever reason. I've supposed that the nested-draggable tag also loop what is out of the draggable tag and I've no idea how to resolve that...
πŸ“„ dragItems.JSON (used in DragBoard.vue) :
1st object is a common element
2nd object is a nestable element
[
{
"type": "Simple list",
"title": "Simple list",
"id": 1,
"properties": "this is an item property"
},
...
{
"type": "Grouped items",
"title": "Grouped items",
"id": 10,
"properties": "this is an item property",
"tasks": []
},
...
]
🚩 DropBoard.vue template:
<template>
<div class="board">
<div class="head">Mock</div>
<div class="dd-container">
<draggable
:list="tasks"
v-model="dropItems"
item-key="title"
:group="{ name: 'items', put: true }"
#change="log"
>
<template #item="{ element }">
<div
class="item"
:key="element"
>
<div>
{{ element.title }}
</div>
<nested-draggable
v-if="element.tasks"
:tasks="element.tasks"
class="group-container"
/>
<div class="trashico" :key="index">
<i class="fas fa-trash" #click="deleteItem(index)"></i>
</div>
</div>
</template>
</draggable>
</div>
</div>
</template>
🚩 DropBoard.vue script
<script>
import draggable from "vuedraggable";
export default {
name: "nested-draggable",
components: {
draggable,
},
props: {
dropItems: {
type: Array,
required: true,
},
tasks: {
required: true,
type: Array,
},
},
data() {
return {
dropItems: [],
};
},
methods: {
deleteItem(id) {
this.dropItems.splice(id, 1);
},
},
};
</script>
Here is what I found while using the Vue DevTools, it's quit explicit about the problem.
See image: IMG

How to update a key of an object of an array element using v-model?

I am creating a form whose questions are fetched from an api, so i want to grab there answers in different input fields for answers. For this I am creating a response array(length >=noOfQuestions) to hold response and prefilled the array with the format I want to store answers. Now I am using v-model to update the respective key like for Question1 update the response[0], for question2 update response[1], but when i am updating response for one index other index are also updating.
See the demo https://codesandbox.io/embed/vuetify-tutorial-forked-314r8?fontsize=14&hidenavigation=1&theme=dark (see the AppFooter.vue)
Can anyone tell what i am doing wrong here , or any other method to do this
using vue#2 and vuetify
<template>
<v-footer height="auto">
<v-card flat tile class="indigo white--text text-xs-center">
<v-flex v-for="(question, index) in questions" :key="index">
<span class="subheading"> Q{{ index + 1 }}. {{ question.text }} </span>
<v-text-field
v-if="question.answerType === 'text'"
v-model="response[index].answer"
class="mt-2"
placeholder="Enter Your Answer"
></v-text-field>
<v-radio-group
v-if="question.answerType === 'scq'"
:column="false"
v-model="response[index].answer"
>
<v-radio
v-for="item in question.options"
:key="item"
:label="item"
:value="item"
></v-radio>
</v-radio-group>
<v-flex d-flex v-if="question.answerType === 'mcq'">
<v-checkbox
v-for="(item, i) in question.options"
v-model="response[index].answer"
:key="i"
:label="item"
:value="item"
multiple
></v-checkbox>
</v-flex>
</v-flex>
</v-card>
</v-footer>
</template>
<script>
const format = {
answer: "",
options: "",
};
export default {
data: () => ({
questions: [
{
text: "Question 1",
answerType: "text",
details: "Question 1 details",
},
{
text: "Question 2",
answerType: "scq",
options: ["a", "b"],
details: "Question 1 details",
},
{
text: "Question 3",
answerType: "mcq",
options: ["aa", "bb"],
details: "Question 1 details",
},
],
response: Array.from(Array(3), () => format),
}),
watch: {
response: {
handler: function () {
console.log(this.response);
},
deep: true,
},
},
};
</script>
One option would be to make the response part of each question, rather than using a separate array. The expansion of format creates a new response object for each question:
data: () => ({
questions: [
{
text: "Question 1",
answerType: "text",
details: "Question 1 details",
response: { ...format },
},
{
text: "Question 2",
answerType: "scq",
options: ["a", "b"],
details: "Question 1 details",
response: { ...format },
},
{
text: "Question 3",
answerType: "mcq",
options: ["aa", "bb"],
details: "Question 1 details",
response: { ...format },
},
],
}),
In the HTML template, use v-model = "question.response.answer"

add icons in actions columns and in each row

This is a vue.js project and this project is for a car sales company.
I have a table and this table contains information about each car as in the picture.
And as we note that for the table there is a header and there are lines under the head and are the Items which are the information of each car for each column.
And there is a column called Action, and within this column I want to put two icons, the first is delete and the second is edit.
Note that the data for the car information comes from the backend, which is node.js.
The problem is that I did not explore adding the delete and edit buttons on every line in the action column.
The question is, how can I add the Delete and Modify icons within the action column and on each line
How can I do this?
ViewAllCars.vue:
<template>
<v-app class="bg">
<v-container>
<v-card
class="mx-auto mt-5 pa-3"
max-width="100%"
id="limited-products"
:style="'border: 0px solid #D50000;'"
>
<v-data-table
:headers="tableHeaders"
:items="loadedCarsGetter"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
#page-count="pageCount = $event"
>
</v-data-table>
<!-- pagination -->
<div class="text-center pt-2">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
<v-text-field
:value="itemsPerPage"
label="Items per page"
type="number"
min="-1"
max="15"
#input="itemsPerPage = parseInt($event, 10)"
class="pl-7 pr-7"
></v-text-field>
</div>
</v-card>
</v-container>
</v-app>
</template>
<script>
import { mapGetters } from "vuex";
import GettersTypes from "../../store/types/getters-types";
export default {
data() {
return {
page: 1,
pageCount: 0,
itemsPerPage: 10
};
},
created() {},
computed: {
...mapGetters({
loadedCarsGetter: GettersTypes.GET_CAR_FORM_GETTER,
tableHeaders: GettersTypes.GET_HEADERS_TABLE_GETTER
}),
}
};
</script>
Within the state there is the header in the table, and these values ​​were used in Getter
state.js:
const state = {
loadedCar: [],
reports: [],
headers: [
{
text: "Car name",
align: "start",
sortable: false,
value: "name",
class: "red accent-4 white--text",
},
{ text: "Price", value: "price", class: "red accent-4 white--text" },
{
text: "Number of Seats",
value: "numberofseats",
class: "red accent-4 white--text",
},
{ text: "Date", value: "date", class: "red accent-4 white--text" },
{
text: "selling price",
value: "sellingprice",
class: "red accent-4 white--text",
},
{
text: "The buyer name",
value: "Thebuyername",
class: "red accent-4 white--text",
},
{
text: "Actions",
value: "",
class: "red accent-4 white--text",
},
],
};
export default state;
Within Getter, the state was called that contains the header and other information coming from the backend.
getters.js:
import GettersTypes from '../types/getters-types'
const getters = {
[GettersTypes.GET_CAR_FORM_GETTER](state) {
return state.loadedCar;
} ,
[GettersTypes.GET_HEADERS_TABLE_GETTER](state){
return state.headers;
}
}
export default getters;
first give a value to Actions object in header:
const state = {
loadedCar: [],
reports: [],
headers: [
{
text: "Car name",
align: "start",
sortable: false,
value: "name",
class: "red accent-4 white--text",
},
{ text: "Price", value: "price", class: "red accent-4 white--text"},
{
text: "Number of Seats",
value: "numberofseats",
class: "red accent-4 white--text",
},
{ text: "Date", value: "date", class: "red accent-4 white--text" },
{
text: "selling price",
value: "sellingprice",
class: "red accent-4 white--text",
},
{
text: "The buyer name",
value: "Thebuyername",
class: "red accent-4 white--text",
},
{
text: "Actions",
value: "actions",
class: "red accent-4 white--text",
},
],
};
export default state;
then use slots in your v-data-table like this:
<v-data-table
:headers="tableHeaders"
:items="loadedCarsGetter"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
#page-count="pageCount = $event"
>
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)>
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete(item.id)>
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
notice that # is shorthand for v-slot and I assumed that each car has an id in its object that you can pass that to edit and delete methods to perform the proper action on that car, you can pass any other argument you want like this: item.name
also vuetify has a good example on its site, check the link below:
vuetify data table CRUD actions example

Using ternary operator to render correct template in component

I'm trying to use conditional (ternary) operator to decide upon two template strings inside component's template property.
I am passing down list of jobs to my component:
<jobs :title="this.data.titles['experience']" :data="this.data.jobs"></jobs>
and here is my job and jobs component:
Vue.component('jobs', {
props: ['title', 'data'],
template: `<section id='jobs'>
<header>{{ title }}</header>
<job v-for="(job, index) in data" :job="job" :key="index"></job>
</section>`
})
Vue.component('job', {
props: ['job'],
template: job.hasOwnProperty('phases') && job.phases.length > 0
? `<p>A</p>`
: `<p>B</p>`
})
For some weird reason loop prints paragraph A even when list of jobs are:
[
{
"phases": [
{
"title": "Title 2"
},
{
"title": "Title 1"
}
],
"workPlace": "Company B"
},
{
"title": "Title 1",
"workPlace": "Company A"
}
]
I haven't conditionally rendered templates like that before, so not sure if it's good practice.
If you do it like this, does it work?
Vue.component('job', {
props: ['job'],
template: `
<p v-if="job.hasOwnProperty('phases') && job.phases.length > 0">A</p>
<p v-else>B</p>
`
})

How to read deep JSON data using Vuejs and Axios

How would I read deep JSON data nested deep inside a file? I've tried different methods and can't seem to get this to work.
<template>
<div>
<div v-for="edu in info" :key="edu">
<div>{{ edu.section.title }}</div> // this is what im trying to get to work
</div>
<div class="card container">
{{ info }}
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
info: null
}
},
mounted() {
axios
.get('./calculus.json') // Data Below
.then(response => (this.info = response.data.edu))
.catch(error => console.log(error))
}
}
</script>
My JSON looks like this:
{
"edu": {
"1": {
"title": "Title One",
"subtitle": "Subtitle One",
"description": "Description One",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
},
"2": {
"title": "Title Two",
"subtitle": "Subtitle Two",
"description": "Description Two",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
}
}
}
How can I use vue-for and get the data inside the section to get it to display under the title? For example: title, section>title, section>subtitle, etc.
Given each section is also an object with weird numeric keys, you can iterate them in the same way you do info.
I would also advise you to use identifiable values instead of the entire edu object in your :key bindings.
<div v-for="(edu, eduId) in info" :key="eduId">
<div v-for="(section, sectionId) in edu.section" :key="sectionId">
{{ section.title }}
</div>
</div>
If possible, I would alter the format of your JSON data to use actual arrays instead of objects with numeric keys.
One way to browse your object deeply is to cumulate v-for on your object (and children) entries.
ie:
<div v-for="([category, books], catkey) in Object.entries(info)" :key="`category-${catkey}`">
<div>{{ category }} :</div>
<div v-for="([num, book], numkey) in Object.entries(books)" :key=`book-${catkey}-${numkey}`>
<div v-for="([field, value], valkey) in Object.entries(book)" :key=`field-${catkey}-${numkey}-${valkey}`>
{{ field }} : {{ value }}
</div>
</div>
</div>
If you find it too verbose, you may try to flatten your computed data to have the following structure:
[
{
"category": "edu",
"id": "1",
"title": "Title One",
"subtitle": "Subtitle One",
"description": "Description One",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
}
]