I make one collection Recipes and then define its schema and i have one array of objects ingredients in my schema and now i want to access those array of objects in my template but unable to show them can you please guide me how to do it?
collections.js
Recipes = new Mongo.Collection('recipes');
Recipes.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Recipe Name",
max: 100
},
ingredients: {
type: [Object],
minCount: 1
},
"ingredients.$.name": {
type: String
},
"ingredients.$.amount": {
type: String
},
description: {
type: String,
label: "How to prepare ",
},
time: {
type: Number,
label: "Time (Minutes)",
},
image: {
type: String,
autoform: {
afFieldInput: {
type: "cfs-file",
collection: 'recipesImages',
label: 'Recipe Picture'
}
}
}
}));
router.js
Router.route('/show_recipe/:_id', {
name: 'show_recipe',
template: 'show_recipe',
data: function() {
return Recipes.findOne(this.params._id);
}
});
show_recipe.html
<template name="show_recipe">
<div class="container">
<div class="row">
<div class="col-md-8">
{{#with FS.GetFile "recipesImages" image}}
<img class="img-responsive mt" src="{{url}}"/>
{{/with}}
</div>
<div class="col-md-4" >
<ul class="list-group">
<h3> Ingredients</h3>
<li class="list-group-item">{{ingredients.name}} - {{ingredients.amount}}</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-8">
<h4>{{name}}</h4>
<p> {{description}}</p>
</div>
</div>
</div>
</template>
You're only missing an {{#each}} to iterate over the ingredients array. Using it will set the data context to one element of the array and then you can just access the keys directly:
<h3> Ingredients</h3>
{{#each ingredients}}
<li class="list-group-item">{{name}} - {{amount}}</li>
{{/each}}
Related
I want to simply loop through several props object data in my component, so for example:
I have created a card component and passing as props objects:
<template>
<div class="container">
<div v-for="(title, index) in titles" :key="index" class="counter">
<div class="title ">
<span>{{ title }}</span>
</div>
<div class="content">
<span>{{ values.value1 }}</span>
</div>
<div>
<Button :prop1="moreActions.prop1" :prop2="moreActions.prop2"
:prop3="moreActions.prop3" />
</div>
</div>
<!-- <div class="counter">
<div class="title">
<span>{{ titles.usersA }}</span>
</div>
<div class="content">
<span>{{ values.values2 }}</span>
</div>
<div>
<Button :prop1="moreActions.prop1" :prop2="moreActions.prop2"
:prop3="moreActions.prop3" />
</div>
</div> -->
// more div here.....
</div>
</template>
<script>
import Button from "../test/testButton";
export Test {
name: "Card",
components: {
Button,
},
**//Comment: I want to loop through each objects ( titles, values, moreActions)**
props: {
titles: {
type: Object,
default: () => ({}),
},
values: {
type: Object,
default: () => ({}),
},
moreActions: {
type: Object,
default: () => ({}),
},
},
};
</script>
and in app.vue , i am passing the data something like this:
export const test = createTest ({
.........
props: // I can say 'data' too
{
titles: {
testA: "testA",
usersA: "TestB",
testc: "test3",
textd: "Test4",
},
values: {
value1: "9000",
value2: "600",
value3: "100",
value4: "2",
ofTotal: " of 9",
},
moreActions: {
prop1: "Test",
prop2: "testIcon",
prop3: "test",
},
}
});
in the second div I can loop through like this and so on:
<div v-for="(value, index) in values" :key="index" class="content">
<span>{{ value }}</span>
</div>
but then it doesnot look goods, I want to loop through every props objects in one v-for loop instead giving v-for loop to each div. how can I do that in efficient way.
Thanks in advance!
This question already has answers here:
A way to render multiple root elements on VueJS with v-for directive
(6 answers)
Closed 2 years ago.
hopefully someone here will be able to help me with this problem.
I have the following data:
[
{
title: 'Header',
children: [
{
title: 'Paragraph',
children: [],
},
],
},
{
title: 'Container',
children: [
{
title: 'Paragraph',
children: [],
},
],
},
]
I want to render this in a list of <div> like this:
<div class="sortable-item" data-depth="1" data-index="0">Header</div> <!-- Parent -->
<div class="sortable-item" data-depth="2" data-index="0">Paragraph</div> <!-- Child-->
<div class="sortable-item" data-depth="1" data-index="1">Container</div> <!-- Parent -->
<div class="sortable-item" data-depth="2" data-index="0">Paragraph</div> <!-- Child-->
I have built a component that would be recursive, this is what I have so far:
<template>
<template v-for="(item, index) in tree">
<div
class="sortable-item"
:data-depth="getDepth()"
:data-index="index"
:key="getKey(index)"
>
{{ item.title }}
</div>
<Multi-Level-Sortable
:tree="item.children"
:parent-depth="getDepth()"
:parent-index="index"
:key="getKey(index + 0.5)"
></Multi-Level-Sortable>
</template>
</template>
<script>
export default {
name: 'MultiLevelSortable',
props: {
tree: {
type: Array,
default() {
return [];
},
},
parentDepth: {
type: Number,
},
parentIndex: {
type: Number,
},
},
methods: {
getDepth() {
return typeof this.parentDepth !== 'undefined' ? this.parentDepth + 1 : 1;
},
getKey(index) {
return typeof this.parentIndex !== 'undefined' ? `${this.parentIndex}.${index}` : `${index}`;
},
},
};
</script>
As you can see not only I have a <template> as the root element I also have a v-for, two "no no" for Vue.js. How can I solve this to render the list of elements like I pointed out above?
Note: I have tried vue-fragment and I was able to achieve the structure I wanted, but then when I tried using Sortable.js it didn't work, as if it wouldn't recognise any of the .sortable-item elements.
Any help will be greatly appreciated! Thank you!
Thanks to #AlexMA I was able to solve my problem by using a functional component. Here is what it looks like:
import SortableItemContent from './SortableItemContent.vue';
export default {
functional: true,
props: {
tree: {
type: Array,
default() {
return [];
},
},
},
render(createElement, { props }) {
const flat = [];
function flatten(data, depth) {
const depthRef = typeof depth !== 'undefined' ? depth + 1 : 0;
data.forEach((item, index) => {
const itemCopy = item;
itemCopy.index = index;
itemCopy.depth = depthRef;
itemCopy.indentation = new Array(depthRef);
flat.push(itemCopy);
if (item.children.length) {
flatten(item.children, depthRef);
}
});
}
flatten(props.tree);
return flat.map((element) => createElement('div', {
attrs: {
'data-index': element.index,
'data-depth': element.depth,
class: 'sortable-item',
},
},
[
createElement(SortableItemContent, {
props: {
title: element.title,
indentation: element.indentation,
},
}),
]));
},
};
The SortableItemContent component looks like this:
<template>
<div class="item-content">
<div
v-for="(item, index) in indentation"
:key="index"
class="item-indentation"
></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">{{ title }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'SortableItemContent',
props: {
title: String,
indentation: Array,
},
};
</script>
Given the data I have posted on my question, it now renders the HTML elements like I wanted:
<div data-index="0" data-depth="0" class="sortable-item">
<div class="item-content">
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Header</div>
</div>
</div>
</div>
<div data-index="0" data-depth="1" class="sortable-item">
<div class="item-content">
<div class="item-indentation"></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Paragraph</div>
</div>
</div>
</div>
<div data-index="1" data-depth="0" class="sortable-item">
<div class="item-content">
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Container</div>
</div>
</div>
</div>
<div data-index="0" data-depth="1" class="sortable-item">
<div class="item-content">
<div class="item-indentation"></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Paragraph</div>
</div>
</div>
</div>
Thank you again #AlexMA for the tip on Functional Components.
I'm trying to fetch product data from a JSON file, but can't get it to work.
I've tried several things and searched the internet for a solution but none of the examples on the internet equals my situation.
I'm new to both vue and axios, so please excuse my ignorance.
This is what I have so far:
Vue.component('products',{
data: {
results: []
},
mounted() {
axios.get("js/prods.json")
.then(response => {this.results = response.data.results})
},
template:`
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<div class="">
<div class="mkcenter" style="position:relative">
<a class="item">
<img class="productImg" width="120px" height="120px" v-bind:src="'assets/products/' + product.image">
<div class="floating ui red label" v-if="product.new">NEW</div>
</a>
</div>
</div>
<div class="productItemName" >
<a>{{ product.name }}</a>
</div>
<div class="mkdivider mkcenter"></div>
<div class="productItemPrice" >
<a>€ {{ product.unit_price }}</a>
</div>
<div v-on:click="addToCart" class="mkcenter">
<div class="ui vertical animated basic button" tabindex="0">
<div class="hidden content">Koop</div>
<div class="visible content">
<i class="shop icon"></i>
</div>
</div>
</div>
</div>
</div>
</div>
`
})
new Vue({
el:"#app",
});
The json file is as follows
{
"products":[
{
"name": "Danser Skydancer",
"inventory": 5,
"unit_price": 45.99,
"image":"a.jpg",
"new":true
},
{
"name": "Avocado Zwem Ring",
"inventory": 10,
"unit_price": 123.75,
"image":"b.jpg",
"new":false
}
]
}
The problem is only with the fetching of the data from a JSON file, because the following worked:
Vue.component('products',{
data:function(){
return{
reactive:true,
products: [
{
name: "Danser Skydancer",
inventory: 5,
unit_price: 45.99,
image:"a.jpg",
new:true
},
{
name: "Avocado Zwem Ring",
inventory: 10,
unit_price: 123.75,
image:"b.jpg",
new:false
}
],
cart:0
}
},
template: etc.........
As the warnings suggest, please do the following:
Rename the data array from results to products since you are referencing it by the latter one as a name during render.
Make your data option a function returning an object since data option must be a function, so that each instance can maintain an independent copy of the returned data object. Have a look at the docs on this.
Vue.component('products', {
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
},
template: `
//...
`
}
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
...
Also, since you're not using CDN (I think), I would suggest making the template a component with a separate Vue file rather than doing it inside template literals, something like that:
Products.vue
<template>
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<!-- The rest of the elements -->
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Products',
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
}
}
</script>
And then in your main JS file or anywhere else requiring this component:
import Products from './components/Products.vue';
new Vue({
el: '#app',
data() {
return {
//...
}
},
components: {
Products
}
})
<div id="app">
<Products />
</div>
I've got a component that needs to render a list. However, every way I try it gives me some version of this error:
Property or method "messages" 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.
code:
Vue.component("vue-home-messages", {
props: ["user"],
methods: {
onClick: function(event, blah) {
console.log("CLICK: ", event);
console.log("Blah: ", blah);
}
},
data: function() {
return {
messages: [
{
id: 1,
sender: {
id: 234,
name: "Bob Barker"
},
subject: "Everything is AWESOME!",
body: "Etiam vel nisl ipsum."
},
{
id: 2,
sender: {
id: 234,
name: "Bob Barker"
},
subject: "Maybe Not",
body: "Etiam vel nisl ipsum."
}
]
}
},
template: `
<div class="col-lg-3">
<div class="card border-info mb-3" style="max-width: 20rem; margin: auto;">
<div class="card-header bg-primary text-white">New Messages</div>
<div class="card-body">
<div class="list-group">
<a v-for:"message in messages"
href="#"
class="list-group-item list-group-item-action"
#click="onClick">
{{ message.sender }} - {{ message.subject }}
</a>
</div>
</div>
</div>
</div>
`
});
change v-for:"message in messages" to v-for="message in messages"
I've created a simple app that should iist each item from a model in a list, created using a javascrit template.
Fiddle
Html:
<div id="tagsList" class="box">
<div class="box-head">
<h2 class="left">Tags</h2>
</div>
<div class="box-content">
<input type="text" placeholder="Add New Tag" />
<button>+ Add</button>
<div data-bind="template: 'tagsTempl'"></div>
</div>
</div>
<script id="tagsTempl" type="text/html">
<ul>
{{each tags}}
<li class="tagItem">
<span>${Name}</span>
<div>
Edit
Delete
</div>
</li>
{{/each}}
</ul>
</script>
Javascript:
$(function () {
//$("#tagDialog").hide();
var data = [
{ Id: 1, Name: "Ball Handling" },
{ Id: 2, Name: "Passing" },
{ Id: 3, Name: "Shooting" },
{ Id: 4, Name: "Rebounding" },
{ Id: 5, Name: "Transition" },
{ Id: 6, Name: "Defense" },
{ Id: 7, Name: "Team Offense" },
{ Id: 8, Name: "Team Defense" }
];
var viewModel = {
tags: ko.observableArray(data),
tagToAdd: ko.observable(""),
addTag: function() {
this.tags.push({ Name: this.tagToAdd() });
}
}
ko.applyBindings(viewModel)
});
Output of list:
{{each tags}}
${Name}
Edit Delete
{{/each}}
The scripts file is accessible through viewing source. I'm not sure where my error is. Any help?
I updated your fiddle. Now it is working like you want it to: The list of tags is being rendered using the knockout standard method as described in the docs.
HTML
<ul data-bind="template: {name: 'tagsTempl', foreach: tags}"></ul>
Template
<script id="tagsTempl" type="text/html">
<li class="tagItem">
<span data-bind="text: Name"></span>
<div>
Edit
Delete
</div>
</li>
</script>
Also I connected the viewmodel to the view.
For example:
<button data-bind="click: addTag">+ Add</button>
You simply forgot most of it. I suggest you follow the interactive tutorials on how to do this.