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).
Related
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
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.
So, I'm assigned to work with vue at work, but VUE and I aren't friends yet. Currently I'm facing an issue that I don't know how to resolve - I'll explain it the best I can with the limited VUE knowledge I possess.
Simplistically I have a vue component, which looks like this:
Vue.component('input-checkboxes', {
template: '#input_checkboxes',
props: ['id', 'label', 'list', 'required', 'readonly']
});
Then I have a template that looks like this:
<template id="input_checkboxes">
<div>
<div>{{ label }}</div>
<div>
<label v-for="list_item in list">
<input type="checkbox" v-model="value" :name="id" :required="required" :readonly="readonly" value="{{ list_item.name }}"> {{ list_item.name }}
</label>
</div>
</div>
</template>
Then I have a rather large vue instance that I'll paste the relevant parts of here.
This variable is being created:
var some_form = {
form : {
Endless: '',
Amounts: '',
Of: '',
Names: '',
In: '',
The: '',
Form: '',
THIS-ONE: ''
}
};
var vm = new Vue({
el: '#form_product',
data: $.extend({
someStuff : 'some values',
someLists : {}
}, some_form),
ready: function() {
this.getLists(); // Fetches alot of lists
},
methods: {
this.$http.get(
framework.url('api','getLookupLists')
).then(function (response) {
this.lists = response.body;
this.pageLoading = false;
}.bind(this));
}
In the end I have my html page that amongst loads of other fields, that works very well, has this:
<input-checkboxes
id="THIS-ONE"
label="A Fitting Label"
:value.sync="form.SomeID"
:list="lists.AnAppropriateList">
</input-checkboxes>
So, that's the gist of the setup. I have numerous other components like input-text, that works just fine (someone else made it before I did), I even created other components by copying his way, and just changing some elements.
I cannot get checkboxes to work, I think my problem is that there are numerous inputs, and that I don't know how to bind the results of those inputs to my VUE instance.
I really hope this makes sense, because I would really like some pointers on how to get on... Maybe if someone duplicated this setup really simplistic and showed how the array of values from the checkboxes could be bound to the vue instance?
There are a couple of mistakes you are (or might be) making.
First of all, the value prop you pass down has to be an array (seems
like it's a string from your example)
value is not correctly set, you need to set it by doing :value="someValue"; you can't have curlies in an attribute.
Lastly, value should probably be the id of the item and not the name. You have a chance of a collision if you use the name.
Bonus: you don't need to use :name at all (unless you are submitting the form server side...? But I can't see why you would do that.)
Here's a simple working example to sum this up:
HTML
<label v-for="list_item in list">
<input type="checkbox" v-model="value" :required="required" :readonly="readonly" :value="list_item.id"> {{ list_item.name }}
</label>
JS
var app = new Vue({
el: 'main',
data: function () {
return {
value: [],
label: 'Label name',
readonly: false,
required: true,
list: [
{
name: 'Item 1',
id: 'item1'
},
{
name: 'Item 2',
id: 'item2'
}
]
}
}
})
I've also made a bin for you to try it out.
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.
Let's say I have a 4 div tags in my view:
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
And let's say that when the user selects clicks on one of them I want the rest to, say, turn red. Normally in a dirty jQuery style I'd use something like:
var tiles = $('.tile');
tiles.click(function()
{
tiles.css('background', 'red');
});
However, how would I do this in the world of AngularJS? Would I stick this code in the controller and have it relative to the $scope? Or would I create a directive and bind that to each tile element?
Assuming that you wouldn't just have 4 random tiles in your interface not bound to some kind of model, you could do something like this:
http://jsfiddle.net/V4YC9/1/
HTML
<div ng-app ng-controller="x">
<div ng-repeat="tile in tiles" ng-click="selectTile(tile)" ng-class="tile.class">{{tile.name}}</div>
</div>
JavaScript
function x($scope) {
$scope.selectedTileIndex = null;
$scope.tiles = [
{id: 1, name: 'tile 1'},
{id: 2, name: 'tile 2'},
{id: 3, name: 'tile 3'},
{id: 4, name: 'tile 4'}
];
// provide default class to all tiles
angular.forEach($scope.tiles, function (tile) {
tile.class = 'tile';
});
$scope.selectTile = function (clickedTile) {
angular.forEach($scope.tiles, function (tile) {
tile.class = 'tileNotSelected';
});
clickedTile.class = 'tileSelected';
}
}
Edit: There are probably 10 different ways to do it. If you don't want to muddy up your model, you could store a separate array in $scope and calculate the class real time by saying ng-class="calculateTileClass(tile)", similar to what I did in my initial response: http://jsfiddle.net/V4YC9/1/