I have made a DOM-repeat that recieves multiple types of data.
Now I want the DOM-repeat to only show the items related to that type.
Is it possible to do this and how would I be able to achieve this?
When the item.Type is Zone I want it to display the item.Name and item.ZoneID
when the item.Type is Building I want it to display the item.BuildingID, item.Address1 and item.Address2
This is my code:
<template is="dom-repeat" items="[[data]]">
<paper-card id="departmentspaperContainer2">
<div class="card-content">
<h3>Type: {{item.Type}}</h3>
<hr/>
<h4>Name: {{item.Name}}</h4>
<h4>BuildingID: {{item.BuildingID}}</h4>
<h4>ZoneID: {{item.ZoneID}}</h4>
<h4>Address1: {{item.Address1}}</h4>
<h4>Address2: {{item.Address2}}</h4>
</div>
</paper-card>
</template>
Something similar to what 'HakanC' is proposing, but differently, you can use the dom-if and bind the response of your filter function :
Here is a working jsfiddle (to use in chrome)
<dom-module id="os-test">
<template>
<template is="dom-repeat" items="{{data}}">
<paper-card id="departmentspaperContainer2">
<div class="cardContent" type="{{item.Type}}">
<h3>Type: [[item.Type]]</h3>
<hr/>
<template is='dom-if' if='{{showZone(item.Type)}}'>
<h4>Name: [[item.Name]]</h4>
<h4>ZoneID: [[item.ZoneID]]</h4>
</template>
<template is='dom-if' if='{{showBuilding(item.Type)}}'>
<h4>BuildingID:[[item.BuildingID]]</h4>
<h4>Address1: [[item.Address1]]</h4>
<h4>Address2: [[item.Address2]]</h4>
</template>
</div>
</paper-card>
</template>
</template>
</dom-module>
<script>
class OsTestElement extends Polymer.Element {
static get is() {
return 'os-test';
}
static get properties() {
return {
data: {
type: Array,
value: () => {
return [
{
Type:"Zone",
Name:"valueName1",
BuildingID:"valueBuildingID1",
ZoneID:"valueZoneID1",
Address1:"valueAddress11",
Address2:"valueAddress21",
},
{
Type:"Zone",
Name:"valueName2",
BuildingID:"valueBuildingID2",
ZoneID:"valueZoneID2",
Address1:"valueAddress12",
Address2:"valueAddress22",
},
{
Type:"Building",
Name:"valueName3",
BuildingID:"valueBuildingID3",
ZoneID:"valueZoneID3",
Address1:"valueAddress13",
Address2:"valueAddress23",
},
{
Type:"Building",
Name:"valueName4",
BuildingID:"valueBuildingID4",
ZoneID:"valueZoneID4",
Address1:"valueAddress14",
Address2:"valueAddress24",
}
]
}
}
}
}
showZone(item) {
return (item==="Zone");
}
showBuilding(item) {
return (item==="Building");
}
}
window.customElements.define(OsTestElement.is, OsTestElement);
</script>
You may use dom-if into dom-repeat as fallow.
<template is="dom-repeat" items="[[data]]">
<paper-card id="departmentspaperContainer2">
<div class="card-content">
<h3>Type: {{item.Type}}</h3>
<hr/>
<h4>Name: {{item.Name}}</h4>
<template is='dom-if' if='{{_show(item.Type)}}'>
<h4>ZoneID: {{item.ZoneID}}</h4>
</template>
<template is='dom-if' if='{{!_show(item.Type)}}'>
<h4>BuildingID: {{item.BuildingID}}</h4>
<h4>Address1: {{item.Address1}}</h4>
<h4>Address2: {{item.Address2}}</h4>
</template>
</div>
</paper-card>
</template>
....
_show(it){
return (it === "Zone");
}
With above logic should solve the problem.
Related
I'm building a small project where it has a component in it, I should render the data from the API.
Here is my code:
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li
v-for="(product, key) of product"
:key="product.id"
:img="product.img"
>
{{ product.description }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
async fetch() {
this.products = await this.$axios("https://dummyjson.com/products");
},
};
</script>
and here is the error code:
Property or method "product" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option or for class-based components, by initializing the property
This works
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li v-for="product in products" :key="product.id" :img="product.img">
{{ product.description }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
products: [],
};
},
async fetch() {
const response = await this.$axios.$get('https://dummyjson.com/products')
this.products = response.products
},
}
</script>
You need v-for="product in products" as explained here: https://vuejs.org/guide/essentials/list.html
Also, regarding the the network request
We can see that as usual, the actual data is inside data, hence you can use the $get shortcut: https://axios.nuxtjs.org/usage#-shortcuts
Then you need to access the products field to have the data to iterate on. Using the Vue devtools + network tab greatly helps debugging that one!
so the answer is i missed putting the data as #kissu has mentioned above
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.description }}
{{ product.images }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
products: [],
};
},
async fetch() {
const response = await this.$axios.$get("https://dummyjson.com/products");
this.products = response.products;
},
};
</script>
I have a web app which shows the blog posts in a grid. But the BlogCards component in the Home.vue just outputs nothing, whereas it should output the blogs in a grid format. All the datas are stored in firebase. If I go to /blogs, I can see the blogs in grid format, but it doesn't work on the Home.vue. It also spits out the Vue Warn: property or method "blogPostsCards" is not defined on the instance but referenced during render.
I took this code from this tutorial at 5:31:05 minute mark.
Any solution to this problem.
Home.vue
<template>
<div class="home">
<BlogPost :post="post" v-for="(post, index) in blogPostsFeed" :key="index" />
<div class="blog-card-wrap">
<div class="container">
<h3>View more recent blogs</h3>
<div class="blog-cards">
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BlogPost from '../components/BlogPost.vue'
import BlogCards from '../components/BlogCards.vue'
export default {
name: "Home",
components: {
BlogPost,
BlogCards,
Arrow
},
computed : {
blogPostsCards() {
return this.$store.getters.blogPostsCards;
},
blogPostsFeed() {
return this.$store.getters.blogPostsFeed;
},
}
};
</script>
BlogCards.vue
<template>
<div class="blog-card">
<img :src="post.blogCoverPhoto" alt="">
<div class="info">
<h4>{{ post.blogTitle }}</h4>
<h6>Posted on: {{ new Date(post.blogDate).toLocaleString('en-us', {dateStyle: "long"})}}</h6>
<router-link class="link" to="#" >
View Post <Arrow class="arrow" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "blogCard",
props: ["post"],
computed: {
editPost() {
return this.$store.state.editPost
},
}
}
</script>
And getter function in store/index.js
getters:{
blogPostsFeed(state){
return state.blogPosts.slice(0,2);
},
blogPostsCards(state) {
return state.blogPosts.slice(2,6);
},
},
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
In your Home.vue >> change blogPostsCard to blogPostsCards because you use blogPostsCards in your computed so it gives you that error.
I have two routes that render the same component but with different data coming from an API.
This component has a child component called <base-section> that has a v-if directive that checks if a specific slot has content or not (because if it has no content, I don't want the slot to show).
However, there might be more than one instance of this child component on the same parent component, and therefore, if one of the instances has content in the slot, but the other one doesn't, VUE will automatically assume that all slots have content.
Therefore, I would like to know if there is any way of checking the specific slot content of each instance and then compare it with the data coming from the API. Please find my code below:
Parent component (Content.vue)
<template>
<base-section
v-for="entry in this.entries"
:key="entry.title"
lang="lang-markup"
:title="entry.title"
>
<template v-slot:content>
<span v-html="entry.content"></span>
</template>
<template v-slot:examples>
<div v-html="entry.examples"></div>
</template>
<template v-slot:code>
{{ entry.code }}
</template>
</base-section>
</template>
Child component (BaseSection.vue)
<template>
<hr class="my-6" />
<h4 class="text-salmon">{{ title }}</h4>
<section>
<div class="section-sm txt-justify" v-if="this.$slots.content">
<slot name="content"></slot>
</div>
<span class="medal bg-light text-dark code-medal">Examples</span>
<div class="section-sm border-light-1 mb-3">
<slot name="examples"></slot>
</div>
<span class="medal text-dark code-medal">Code</span>
<pre :class="lang + ' border-light-1 bg-light'">
<code><slot name="code"></slot></code>
</pre>
</section>
</template>
The data coming from the API follows this structure:
getData() {
const url = this.apiUrl + this.$route.name + this.apiToken
fetch(url)
.then((collection) => collection.json())
.then((collection) => {
const entries = [];
this.entries = [];
for (const id in collection.entries) {
if (collection.entries[id].Version === this.byteVersion) {
entries.push({
title: collection.entries[id].Title,
content: collection.entries[id].Content,
examples: collection.entries[id].Examples,
code: collection.entries[id].Code,
});
}
}
this.entries = entries;
});
}
Thank you very much for your help!
Regards,
T.
Maybe you can pass the "entry.content" into your BaseSection component. and v-if the entryContent.
Parent component (Content.vue)
<template>
<base-section
v-for="entry in this.entries"
:key="entry.title"
lang="lang-markup"
:title="entry.title"
:entryContent="entry.content"
>
<template v-slot:content>
<span v-html="entry.content"></span>
</template>
<template v-slot:examples>
<div v-html="entry.examples"></div>
</template>
<template v-slot:code>
{{ entry.code }}
</template>
</base-section>
</template>
Child component (BaseSection.vue)
<div class="section-sm txt-justify" v-if="entryContent">
<slot name="content"></slot>
</div>
Or you can v-if your content template
<template v-slot:content v-if="entry.content">
<span v-html="entry.content"></span>
</template>
I have a .vue file, ServiceList, which imports the component Information.vue. I would like to run Information's code in a loop in ServiceList's template like so:
ServiceList.vue
<template>
<div>
<h1>Headline</h1>
<div v-for="service in services">
<h2>{{ service.name }}</h2>
<information v-bind:service="service"/>
</div>
</div>
</template>
<script>
import Information from './Information'
... (more components here)
export default {
components: {
Information,
... (more components here)
},
... (rest of export here)
}
</script>
This is what Information looks like:
Information.vue
<template>
<div>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
document.getElementById('info'+service.id).innerHTML = info;
}
}
}
</script>
I have tried to do stuff like
<template>
<div>
<p v-bind:id="'info'+service.id"/>
{{ getInfo(service) }}
</div>
</template>
but it never seems to work. Weird thing is that when I make it a button,
<template>
<div>
<button v-on:click="getInfo(service)">GET INFO</button>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
it works perfectly! But I don't want a button, I just want it to appear.
You don't need to manipulate DOM in Vue js for such trivial case, just add all you want to template and remove getInfo method, example:
Information.vue
<template>
<div>
<p>
<b>Servicename:</b> <br>
{{ service.name }}<br>
<b>Another Field:</b> <br>
{{ service.anotherField }}<br>
<b>Another Field 2 :</b> <br>
{{ service.anotherField2 }}<br>
</p>
</div>
</template>
OR if you really want to work with html do this:
Information.vue
<template>
<div>
<p v-html="getInfo(service)"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
if (!service) return '';
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
return info;
}
}
}
I'm a bit new to VueJS. I have this Blade template, which is passed the variable $plans which contains [{id:1,name:'1st Plan'},{id:2,name:'2nd Plan'}]:
#extends('layouts.backend')
#section('content')
#if(Auth::user()->can('edit-plans'))
<edit-plans
:workouts="{!! json_encode($plans) !!}"
></edit-plans>
#endif
#endsection
Within edit.vue:
<template>
<workout
v-repeat="workouts"
></workout>
</template>
<script>
export default {
props: [
'workouts'
]
}
</script>
Within workouts.vue:
<template>
<div>{{ id }} :: {{ name }}</div>
</template>
<script>
export default {
props: [
]
}
</script>
I have these templates registered globally:
Vue.component('edit-plans', require('./components/plans/edit.vue'));
Vue.component('workout', require('./components/plans/workout.vue'));
When I compile with yarn, I simply get one line with :: in it but noid or name values showing up on two lines as expected.
when I check the Chrome Vue JS developer extension, I see this in the EditPlans component...
...but nothing in the Workouts component.
In edit.vue declare the v-for (instead of v-repeat) and pass the props. In workout.vue, declare the props so they are available in the template.
edit.vue:
<template>
<workout
v-for="workout in workouts" :id="workout.id" :name="workout.name" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue:
<template>
<div>{{ id }} :: {{ name }}</div>
</template>
<script>
export default {
props: ['id', 'name']
}
</script>
Demo:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['id', 'name']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" :id="workout.id" :name="workout.name" :key="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ id }} :: {{ name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Update, per comment: What if I just want to pass each full workout into the child instead of each part of the object? In other words, I'd be able to do {{ workout.id }} :: {{ workout.name }}
You can use v-bind for that. Just change the edit.vue, keep workout.vue the same as above.
edit.vue:
<template>
<workout
v-for="workout in workouts" v-bind="workout" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue: same as above.
Demo for this:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['id', 'name']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" v-bind="workout" :id="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ id }} :: {{ name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Update, per comment: So no matter what, I have to declare every property within the workout object? I can't just do props: ['workout'] within the workout.vue so I can do {{ workout.id }} :: {{ workout.name }}?
You can declare the workout prop in the child and pass it in the parent like :workout="workout":
edit.vue:
<template>
<workout
v-for="workout in workouts" :workout="workout" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue:
<template>
<div>{{ workout.id }} :: {{ workout.name }}</div>
</template>
<script>
export default {
props: ['workout']
}
</script>
Demo for this:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['workout']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" :workout="workout" :key="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ workout.id }} :: {{ workout.name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Note: :key="workout.id" added to the loop: This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state. (thanks #channasmcs).