everyone I hope everyone is doing great. I'm new to vue.js and was learning prop's which is pretty intersting. The thing is I want to pass an array as props and want to display it in the child component. here is my code
parent component
<template>
<div id="app">
<showProduct :product="['products']"/>
</div>
</template>
<script>
import showProduct from './components/showProduct.vue'
export default {
name: 'App',
components: {
showProduct
},
data(){
return{
products:[
{productid:"01",productname:"t shirt",price:"50$",description:"a good description"},
{productid:"02",productname:"jeans pant",price:"150$",description:"a good description of jeans"},
{productid:"02",productname:"leather jacker",price:"250$",description:"a good description of jacket"},
],
}
}
}
</script>
where as the child component code is given below
<template>
<h1>{{product}}</h1>
</template>
<script>
export default {
name:'showProduct',
props:[
'product'
],
methods:{
}
}
</script>
now I want to do the following:
Pass just a single element for instance product id as a 'props'.
Pass the whole array of products and display it in child components.
Any hint and suggestion will be welcomed.
Like this you will receive in the child component array of names, and index of the current item, so you can get just the name of the item in the child component.
Also don't forget to add unique key where you use v-for directive.
Like this you will receive in the child component array of names, and index of the current item, so you can get just the name of the item in the child component.
Also don't forget to add unique key where you use v-for directive.
Parent.vue
<template>
<div>
<child
v-for="(skill, index) in skills.length"
:key="skill.name"
:index="index"
:names-array="skills.map(a => a.name)"
/>
</div>
</template>
<script>
import Child from './Child'
export default {
name: 'Parent',
components: {
Child
},
data () {
return {
skills: [
{
name: 'Frozen Yogurt',
required: 1,
vMode1: ''
},
{
name: 'Ice cream sandwich',
required: 3,
vMode1: ''
},
{
name: 'Eclair',
required: 1,
vMode1: ''
}
]
}
}
}
</script>
Child.vue
<template>
<div>
<div>Index: {{ index }}</div>
<div>Names Array: {{ namesArray }}</div>
<div>Name: {{ namesArray[index] }}</div>
</div>
</template>
<script>
export default {
name: "Child",
props: ["names-array", "index"]
};
</script>
Output:
Index: 0 Names Array: [ "Frozen Yogurt", "Ice cream sandwich",
"Eclair" ] Name: Frozen Yogurt
Index: 1 Names Array: [ "Frozen Yogurt", "Ice cream sandwich",
"Eclair" ] Name: Ice cream sandwich
Index: 2 Names Array: [ "Frozen Yogurt", "Ice cream sandwich",
"Eclair" ] Name: Eclair
Related
I can't seem to pass a JSON array in a angular component (custom element). I have the code in which I loop through the JSON array, and I have the array data set up in #Inject. Example:
import {
Input,
Component,
ViewEncapsulation,
EventEmitter,
Output
} from '#angular/core';
#Component({
selector: 'custom-wrapper',
template: `
<ul>
<li *ngFor="let item of items
{{ item.title }}
{{ item.age }}
</li>
</ul>
`,
encapsulation: ViewEncapsulation.Native
})
export class WrapperComponent {
#Input() items: any[];
}
Then, in my index.html file I call the custom element and I'm trying to pass a JSON array of objects into it, for example:
<custom-wrapper [items]="[{"title": "Mr", "age" : 23}, {"title": "Ms", "age" : "25"}]"></custom-wrapper>
All I get is an empty <custom-wrapper [items]="[{"title": "Mr", "age" : 23}, {"title": "Ms", "age" : "25"}]"></custom-wrapper> when I'm in inspect mode, and nothing is being listed.
Can someone help me locate the issue?
You should pass you're array using single quotes as you're using double quotes for attributes.
So this should be you're index.html:
<custom-wrapper [items]="[{title: 'Mr', age: 23}, {title: 'Ms', age: 25}]"></custom-wrapper>
Also in you're WrapperComponent, fix your li like this:
<li *ngFor="let item of items">
I have an array of object like that :
{ id: 33617,
datePublication: 1532266465,
dateUpdate: 1532266574,
headline: 'An headline title here',
images: [ [Object] ] },
{ id: 33614,
datePublication: 1532265771,
dateUpdate: 1532271769,
headline: 'another super headline article',
images: [ [Object], [Object], [Object], [Object], [Object] ] }
so i iterate that way on my vue js template :
<v-flex v-for="(ip, i) in ips" :key="i" xs12 sm6 >
<v-card>
<v-card-media
:src="ip.images[0].url"
height="200px"
/>
<v-card-title primary-title>
<div>
<h3 class="headline mb-0">{{ ip.headline }}</h3>
<div>Located two hours south of Sydney in the <br>Southern Highlands of New South Wales, ...</div>
</div>
</v-card-title>
<v-card-actions>
<v-btn flat color="orange">Share</v-btn>
<v-btn flat color="orange">Explore</v-btn>
</v-card-actions>
</v-card>
</v-flex>
And here is my associated script :
var api = "https://my api.org/news/fr-FR/2018-25-07";
export default {
data() {
return {};
},
layout: "no-live",
async asyncData({ app }) {
const ips = await app.$axios.$get(api);
console.log(ips);
return { ips };
}
};
Each object in the images array should return me an id and an url, so i want to take that url as a source in my image but i have this error : Cannot read property '0' of undefined
it seems i need to do another loop over the images array, is there a proper way to do that with vue JS ?
if you have any objects in ips that doesn't have images, you would get that error,
you can try to add a conditional to not have an error in rendering
<v-card-media
v-if="ip.images && ip.images[0]"
:src="ip.images[0].url"
height="200px"
/>
I usually add a <pre>{{ip}}</pre> in these cases to see what's going on.
Try put v-if="ip.images" on v-card-media component. You will assure the images are not empty and loaded.
I am building web app that will have a list of 3d models. I’m doing it for a practice purposes, in order to gain some initial knowledge of angular 2.
Each listed model, will have name, main picture, category, and a slider (array of images).
Model data array is:
export var MODELS: Model[] = [{
id: 1,
name: 'Model 1',
image: 'app/img/models/model-1.jpg',
category: 'Cat 1',
slides: [
{ alt: 'Model 1 front view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 1 rear view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 1 side view' , url: 'app/img/models/model-2.jpg' }
]
},
{
id: 2,
name: 'Model 2',
image: 'app/img/models/model-2.jpg',
category: 'Cat 2',
slides: [
{ alt: 'Model 2 front view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 2 rear view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 2 side view' , url: 'app/img/models/model-2.jpg' }
]
}
];
Service that is used for proper displaying of certain model is:
#Injectable()
export class ModelService {
getModels(): Promise<Model[]> {
return Promise.resolve(MODELS);
}
getModel(id: number): Promise<Model> {
return this.getModels()
.then(models => models.find(model => model.id === id));
}
}
Model detail’s page template is:
<div *ngIf="model">
<h2>{{model.name}} details!</h2>
<img [src]="model.image" alt="{{model.name}}"/>
<div>{{model.slides}}</div>
</div>
These are Mostly stuff that I used from: https://angular.io/docs/ts/latest/tutorial/
However, when I try to use simplest way to display slides array into model-detail page, it shows [object Object]. I read that this is might be a common issue in angular 2, that can be resolved by using custom pipes. Unfortunately, I have no clue how to write one for this certain case.
General question will be: How to properly display array of objects (slides) on html template, and wrap them separately into img tag.
Here is plunker example link: http://plnkr.co/edit/HVo3dtGprMHsPYeyRWBR?p=preview
Thanks in advance,
That's because your slides property is in fact an Object, it's an array so displaying the property alone will simply display 'object'. What you need to do is iterate over the slides property and separately create img divs:
<img *ngFor="let slide of model.slides" [alt]="slide.alt" [src]="slide.url" />
this is what your updated template would look like from your plunkr:
<ul class="models">
<li *ngFor="let model of models" (click)="gotoDetail(model)">
<img *ngFor="let slide of model.slides" [src]="slide.url"/>
{{model.name}},{{model.category}}
</li>
</ul>
You could use *ngFor in an img tag I have included some of my practice code to show you what I mean.
<div class="col-md-6" *ngFor="let picture of imageUrl; let i = index; trackBy: trackByFn">
<div class="portfolio-item"
style="padding-bottom: 10px;">
<a (click)="galleryOpen = false;componentIndex = i;" data-toggle="dropdown" href="#" name="gal2">
<img class="img-portfolio img-responsive" src={{picture}} height="350" width="455"
style="height: 350px; max-width: 455px;">
</a>
</div>
</div>
The *ngFor in this case could just a easily be placed in the img tag. I hope this helps you out.
I will try to be simple, i'm on the final stage of my app (a portfolio page), and i only need the interaction between pages, so:
short question:
how to select an specific item of some custom-element made with template is="dom-repeat" and publish like a property to bind later outside with another element attribute (selected="{{}}")
long question:
Form top to bottom. I've got a custom element with two elements inside of neon-animated-pages. The first element is a grid of cards made whit template is="dom-repeat", and the second is another neon animated pages, inside of this are all the works-pages (all pages make a change in a declarative way like this demo)
portfolio-page template:
<neon-animated-pages id="page" class="flex" selected="{{projectSelected}}">
<portfolio-grid id="grid" on-tap="_goToProject" card-selected="{{selected}}"> <-- card-selected not defined yet -->
</portfolio-grid>
<work-pages work-selected="{{selected}}" on-back-portfolio="_backPortfolio">
</work-pages>
</neon-animated-pages>
portfolio-page script:
Polymer({
is: "portfolio-page",
properties:{
projectSelected:{
type: Number,
value: 0
}
},
_goToProject: function(){
this.projectSelected = 1;
},
_backPortfolio: function(){
this.projectSelected = 0;
}
});
Inside of portfolio grid, is another custom element, a simple card with attributes like title and description, the img attribute here is the css id inside of the portfolio-card which sets the background image of my card:
<dom-module id="portfolio-grid">
<template>
<style>
SOME STYLE HERE
</style>
<div class="layout horizontal wrap">
<template is="dom-repeat" id="cardList" items="[[cards]]">
<portfolio-card class$="{{item.class}}" img="{{item.img}}"
title="{{item.title}}" description="{{item.description}}">
</portfolio-card>
</template>
</div>
</template>
<script>
Polymer({
is: "portfolio-grid",
behaviors: [
Polymer.NeonAnimatableBehavior
],
properties: {
cards: {
type: Array,
value: function(){
return [
{ img: 'monalisa', title: 'The Mona Lisa',
description: 'Painting', class: 'cardSmall'},
{ img: 'starrynight', title: 'Starry Night',
description: 'Painting', class: 'cardSmall'},
{ img: 'david', title: 'David',
description: 'Sculpture', class: 'cardMedium'},
{ img: 'memory', title: 'The Persistence Of Memory',
description: 'Painting', class: 'cardSmall'},
{ img: 'venus', title: 'Venus de milo',
description: 'Sculpture', class: 'cardSmall'},
{ img: 'birth', title: 'Birth of Venus',
description: 'Painting', class: 'cardMedium'},
{ img: 'guernica', title: 'The Guernica',
description: 'Painting', class: 'cardBig'},
{ img: 'nightwatch', title: 'The Night Watch',
description: 'Painting', class: 'cardSmall'},
{ img: 'kiss', title: 'The Kiss',
description: 'Painting', class: 'cardSmall'}
]
}
},
animationConfig: {
value: function() {
return {
'entry': {
name: 'fade-in-animation',
node: this,
timing: 2000
},
'exit': {
name: 'fade-out-animation',
node: this
}
}
}
}
}
});
</script>
</dom-module>
And finally my work-pages, here i've got all the works i want to show with fixed navigation controls, all working good (next, previous and back to portfolio grid):
<dom-module id="work-pages">
<template>
<style>
SOME STYLE HERE
</style>
<section id="container" class="layout vertical">
<neon-animated-pages id="works" class="flex" attr-for-selected="work" selected="{{workSelected}}">
<workpage-monalisa work="monalisa"></workpage-monalisa>
<workpage-starrynight work="starrynight"></workpage-starrynight>
<workpage-david work="david"></workpage-david>
<workpage-memory work="memory"></workpage-memory>
<workpage-venus work="venus"></workpage-venus>
<workpage-birth work="birth"></workpage-birth>
<workpage-guernica work="guernica"></workpage-guernica>
<workpage-nightwatch work=nightwatch""></workpage-nightwatch>
<workpage-kiss work="kiss"></workpage-kiss>
</neon-animated-pages>
</section>
<!-- Navigation Buttons -->
<div class="controls">
<!-- Back Work Button -->
<iron-icon id="menuIcon" icon="mrcauko-icons:menu"></iron-icon>
<!-- Back Work Button -->
<div id="back">
<iron-icon class="button" icon="mrcauko-icons:back" on-tap="_goPrev"></iron-icon>
</div>
<!-- Next Work Button -->
<div id="next">
<iron-icon class="button" icon="mrcauko-icons:next" on-tap="_goNext"></iron-icon>
</div>
<!-- Bottom Bar -->
<div class="bottomBar layout horizontal">
<div class="flex"></div>
<iron-icon id="backToPortfolio" icon="mrcauko-icons:view-portfolio" on-tap="_goBackPortfolio"></iron-icon>
<div class="flex"></div>
</div>
</div>
</template>
<script>
Polymer({
is: "work-pages",
behaviors: [
Polymer.NeonAnimatableBehavior
],
properties: {
workSelected: {
type: String,
notify: true
},
animationConfig: {
type: Object,
value: function() {
return {
'entry': {
name: 'fade-in-animation',
node: this
},
'exit': {
name: 'fade-out-animation',
node: this
}
}
}
}
},
listeners: {
'neon-animation-finish': '_onNeonAnimationFinish'
},
_onNeonAnimationFinish: function(){ //This isn't working
this.$.works.scroller.scrollTop = 0;
},
_goBackPortfolio: function(){
this.fire('back-portfolio');
},
_goPrev: function() {
this.$.works.selectPrevious();
},
_goNext: function() {
this.$.works.selectNext();
}
});
</script>
I've been thinking different aproaches but don't know how to implement them.
Because i've got the same name in item.img and attr-for-selected="work" in the work-pages, somehow say "hey, when i click this card, the item.work is the same as the work-selected"(see portfolio-page)
Don't know if i need to use the array selector
Wrap the portfolio-grid inside an iron-selector element, to establish attr-for-selected$="{{item.img}}"(not tried yet but i think this way is using too many elements, and im sure there is a better way)
I know there is demos with neon-animated-pages, but my project is more specific, besides the code of the demos is too complicated for my poor knowledge of js (that's why i love polymer, 'cause i can create great things, and i'm not an expertise in js). I'm not asking to do my job, just only some enlightening to continue. So I ask for help from the immortals like you guys.
If you only need your data to flow from <portfolio-grid> to <work-pages>, then I think the array selector is the way to go.
It should be fairly simple to implement, just like the example in the documentation (see the link your provided).
<ul ng-repeat="cate in restaurant.categories"><li>{{cate}}</li>
<li ng-repeat="menuItem in restaurant.menuItems" ng-show="menuItem.category == cate">{{menuItem.name}}</li></ul>
I want one ng-repeat loop inside another and to show the menu only if the menuItem is in the category. I only have items in the first category loop, and empty for all the other categories.
Categories and menuItem are 2 different arrays. If the menuItem's category is under the current category it should be added to the page.
menuItems = {{name: dish1, category:soup},
{name: dish2, category:beef}}
categories = {beef, soup}
#show-me-the-code : Bill Bi has two different array. So the best option to achieve this is by filter in inside loop as stated in my comment.
Here is the final code with filter for inside loop. I am including fiddler for quick reference.
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in categories">
<li>{{cate}}</li>
<li ng-repeat="menuItem in menuItems | filter:{category: cate}">{{menuItem.name}}</li>
</ul>
</div>
function testCtrl($scope) {
$scope.menuItems = [{name: 'dish1', category:'soup'},
{name: 'dish2', category:'beef'}];
$scope.categories = ['beef', 'soup']
}
Fiddle : JSFiddle
I would change my data representation to match what you are actually trying to display like so:
$scope.restaurant = {
categories: [{
name: "beef",
menuItems: [{
name: "dish1",
"price": "$10"
}, {
name: "dish2",
"price": "$15"
}]
}, {
name: "soup",
menuItems: [{
name: "dish1",
"price": "$20"
}, {
name: "dish2",
"price": "$25"
}]
}]
};
This way you could easily match your two nested loops like this:
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in restaurant.categories">
<li>{{cate.name}}</li>
<li ng-repeat="menuItem in cate.menuItems">{{menuItem.name}} - {{menuItem.price}}</li>
</ul>
</div>
Check out this fiddle if you would like to see it in action.
If you need to stick to your JSON data, you will have to do filtering to pull the contents you want to display.