VueJS V-For and bootstrap Cards in a Deck - javascript

Good Evening,
I've been working on a issue for about an hour or so, I am kinda new to VueJS (and front end Dev in general).
I am trying to have a deck of cards looping through and object (using Bootstrap-Vue).
When I manually post the cards in they align correctly in groups of 3. the issue is when I iterate through the json object it seems to create one solid column.
Ill post my code (dont mind some of the names this is in a test section, im going to refactor it into a comp once its complete) in case someone wants to take a look. I am going to poke at it some more and Update in case I figure it out before someone.
Thank you in advance
<div class="about">
<h1>Dawning Recipes</h1>
<br />
<b-container>
<b-card-group deck v-for="recipes in data.recipes" :key="recipes">
<b-card
bg-variant="dark"
:header="recipes.name"
class="text-center"
style="max-width: 23rem;"
>
<b-row>
<b-col sm="auto">
<b-img thumbnail fluid :src="recipes.icon" :alt="recipes.name" />
</b-col>
<b-col class="text-left">
<b-list-group>
<b-list-group-item
variant="dark"
v-for="ingredient in recipes.ingredients"
:key="ingredient"
>{{ingredient}}</b-list-group-item>
</b-list-group>
</b-col>
</b-row>
<b-row>
<b-row>
<b-col>
<b-card-text class="text-left ml-2 mt-3 mr-2">{{recipes.delivery}}</b-card-text>
</b-col>
</b-row>
</b-row>
</b-card>
</b-card-group>
</b-container>
</div>
</template>
<script>
import data from "../data/destiny/dawning.json";
export default {
data() {
return {
data: data
};
}
};
</script>```

Hard to say if this is the only issue, since I don't have your files, but one thing immediately sticks out:
<b-card-group deck v-for="recipes in data.recipes" :key="recipes">
:key has to be a unique string value. Since you're using recipes.name below that line, I assume recipes is an object. Vue will call toString() on this, which will always result in a String of "[object Object]", for any object, no matter the contents.
I bet the same key of [object Object] is being used, so Vue is only creating one object there from that loop.
Try using a unique value like :key="recipes.name" (assuming that is unique, ideally you have an id field)
At the very minimum to get it working, you could use index as a key, but that is essentially the same as not using a key at all:
<b-card-group deck v-for="(recipes, index) in data.recipes" :key="index">
Edit:
Just peeking at the Vue source code, it looks like you should be seeing a warning about this. It might be good to work through other warnings in there, if any.

Related

How can I properly format a Vue component?

I would have thought the below would work but it doesn't seem to lay the data out nicely and in a grid like fashion according to this website;
https://bootstrap-vue.org/docs/components/layout
<template>
<div>
<div v-for="i in items">
<b-container class="bv-example-row">
<b-row>
<b-col>Home</b-col>
<b-col>Road</b-col>
<b-col>Home ATS</b-col>
<div class="w-500"></div>
<b-col>{{i.Home_Neutral}}</b-col>
<b-col>{{i.Visitor_Neutral}}</b-col>
<b-col>{{i.ATS}}</b-col>
</b-row>
</b-container>
</div>
</div>
</template>
Can anyone give me some tips on how I can nicely format this data as it doesn't appear to be working for me? I basically just want to get the data spread out more and into a box like this;
example

get specific element of an table

hello every one I am trying to get a specific element in my table and add a class to it to make the red buttons green when an image is uploaded. But i can't get the specific row in my table. Any one a solution for this?
My code:
<b-table hover
:filter="filter"
id="my-table"
:bordered="bordered"
:fields="fields"
:items="items"
:per-page="perPage"
:current-page="currentPage"
responsive
striped
small>
<template v-slot:cell(actions)="row">
<b-button id="btnImage" size="sm" #click="info(row.item, $event.target)" class="addModal mr-2" ref="addModal">
<font-awesome-icon :icon="['fas', 'image' ]" />
</b-button>
</template>
</b-table>
If i do this: var tr = document.getElementById("my-table").getElementsByTagName("tr"); I get the following output:
inside every tr is an element thats called innerText that reference to the id that you can see here in my table:
But how do i get the specific row in my table and add an class to the button of that specific row?
Use scoped field slots
<template v-slot:cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
Refer https://bootstrap-vue.org/docs/components/table#scoped-field-slots. I think it's well documented.
For every one that needs it. I fixed the problem to give row.item as a parameter to my method very simple but is works great

Vue.js - hide a text on a page passed from App.vue

I am really new to Vue. I have multiple pages such as App.vue, Waiting.vue, Reference.vue, Switch.vue and so on. The text defined on **App.vue** is passed along to other pages correctly. But now I dont want to display WELCOME text passed from App.vue to Waiting.vue page. Is there a way that I can hide WELCOME from Waiting.Vue page only?
App.vue
<div id="app">
<b-container>
<b-col lg="5">
WELCOME
</b-col>
</b-container>
</div>
Waiting.vue
<div id="app">
<p class="teamTitle">TEAMS ON DECK</p>
</div>
Ouput for Waiting.vue
WELCOME
TEAMS ON DECK
Let suppose that the Waiting page path is /waiting so you should do :
<div id="app">
<b-container>
<b-col lg="5" v-if="$route.path!=='/waiting'">
WELCOME
</b-col>
</b-container>
</div>
I am assuming that App.vue is acting as parent component and has content that is visible in the page. That being the case, there are multiple ways of hiding information.
If you use a router and Waiting.vue is on a distinct route you can check for route params to selectively hide text. For e.g. in App.vue.
<b-col lg="5" v-if="!this.$route.fullPath === '/waiting'">
WELCOME
</b-col>
If you use global storage like Vuex, just set a variable within Vuex when Waiting.vue is loaded and set the variable to something else in other components
If you are having everything on a single page for any reason, pass a variable as prop and selectively hide the element (similar to [1])
App.vue
<Waiting :hide="true"/>
Waiting.vue
<b-col lg="5" v-if="!hide">
WELCOME
</b-col>
<!-- other code -->
<script>
export default {
props: {
default: false,
type: Boolean,
required: false
}
}
</script>

What is the purpose of <template> usage in Vuetify?

I want to use Vuetify 2.0 in my project and currently reading about v-stepper component which is used to display progress through numbered steps.
In the playground example provided I see that they are using <template> element to wrap content of v-stepper component. HTML looks something like this (note: I removed unnecessary details):
<v-stepper>
<template v-for="n in steps">
<v-stepper-content
:key="`${n}-content`"
:step="n"
>
</v-stepper-content>
</template>
</v-stepper>
Notice the <template> tag used. What is the purpose of it? When should I use it as opposed to just putting the <v-stepper-content> directly inside of <v-stepper>?
I've read a bit about element on MDN but I am not sure how to use it specifically with Vuetify (or more generally with Vue.Js or just pure HTML/CSS/JS for that matter).
a <template> in the context of a v-for loop is an organizational item.
It does not get rendered by the browser. It is there to help with more complex rendering situations, where you don't want to limit yourself to a single element
In most cases you have a pretty straight forward mapping of items, each item in an array gets a <li> element. If this is the case, you're not likely to use this.
Here is an example of a problem where it might help...
Let's say you want to loop through an array of objects, and render a v-btn if the object is a button, and a v-image if the object is an image.
without template...
<span v-for="item in items">
<v-btn v-if="item.isBtn"></v-btn>
<v-img v-else-if="item.isImg"></v-img>
</span>
The problem is that each item will be wrapped in the span.
<span>
<v-btn/>
</span>
<span>
<v-img/>
</span>
<span>
<v-btn/>
</span>
If you, however, use the template element, the wrapping element is no longer there.
<template v-for="item in items">
<v-btn v-if="item.isBtn"></v-btn>
<v-img v-else-if="item.isImg"></v-img>
</template>
and you will get...
<v-btn/>
<v-img/>
<v-btn/>
You can also have it return multiple items in one instance of the loop.
in the vue docs at https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
it shows an example of rendering more than one item per iteration:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
There are some other cases where this might be helpful but not likely something you come across on a daily basis.
TL;DR;
Vue doesn't render the <template> element. It helps organize code chunk without the need of a single child element when looping
part 2.
When should I use it as opposed to just putting the directly inside of ?
Because the structure of vertical and horizontal steppers is different, the vuetify authors used it in the playground to allow users to toggle it. The first level of template (<template v-if="vertical">) is used do determine whether the next level should render the v-stepper-step elements as vertical or as horizontal. The second level is used to do the iterating of items.
example:
vertical (step and content are siblings):
<template>
<v-stepper v-model="e6" vertical>
<v-stepper-step :complete="e6 > 1" step="1">
Select an app
<small>Summarize if needed</small>
</v-stepper-step>
<v-stepper-content step="1">
<v-card color="grey lighten-1" class="mb-12" height="200px"></v-card>
<v-btn color="primary" #click="e6 = 2">Continue</v-btn>
<v-btn text>Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="2">...</v-stepper-content>
<v-stepper-step :complete="e6 > 3" step="3">...</v-stepper-step>
</v-stepper>
</template>
horizontal (each step is separate):
<template>
<div>
<v-stepper>
<v-stepper-header>
<v-stepper-step step="1">Select campaign settings</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="2">Create an ad group</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="3">Create an ad</v-stepper-step>
</v-stepper-header>
</v-stepper>
<v-stepper value="2" class="mt-12">
...
</v-stepper>
<v-stepper value="3" class="mt-12">
...
</v-stepper>
</div>
</template>

How to enforce render table dynamically

I'm using bootstrap-vue b-table and b-pagination component. But since there two many records, so I'm using pagination call to backed, which means pass the paging information to the backend, only fetch corresponding records for each page.
Good thing:
1. Data are passing correctly between backend and frontend.
2. Debug the frontend page through VUE debug tool, the properties of the b-table component are correct.
The problem:
The table is not rendering, it looks blank in the page, if I check the elements through chrome debugging tool. The elements (table body) for table container is empty.
A brief sample code :
<b-container>
<div v-if="guards_for_asyc_fetch_data_call">
<b-row><b-col><h2>Title</h2></b-col></b-row>
<b-row>
<b-col>
<b-table
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:stacked="true"
small
striped
fixed
hover/>
</b-col>
</b-row>
<b-row>
<b-col>
<b-pagination
:total-rows="totalRows"
:per-page="perPage"
:number-of-pages="totalPages"
v-model="currentPage"
align="center"
/>
</b-col>
</b-row>
</div>
</b-container>
</template>
<script>
export default {
computed: {
totalRows() {
return {dynamic_udpate};
},
perPage() {
return {dynamic_udpate};
},
currentPage: {
get: function() {
return {dynamic_udpate};
},
set: function (newPage) {
{asyc_call_fetch_data_from_backend}
}},
totalPages() {
return {dynamic_udpate};
},
items() {
return {dynamic_udpate};
}
}
}
</script>
Expectation:
Table content is updated dynamically when the data is refreshed after the backend call.
Actual result is:
When I click the page navigation except the 1st page, the container just show blank.
For vue debugger you can see the elements are the new fetched elements, id started from 6 since I set to present 5 items per page. Here's a link!
If I go check the general page elements in Chrome debugger, the table content is empty. Here's a link!
Anyone gets an idea how I can solve this problem?

Categories

Resources