How to properly bind iron-selector to iron-pages? - polymer

I am trying to do the same basic task as this other question, iron-selector selected={{}} binding with iron-pages selected={{}}, but I believe my case must be different, since I cannot replicate the solution.
I want to have a menu of buttons with iron-selector, and when clicked, the content with iron-pagesshould change.
So I have the actual page about.htm that has 1 webcomponent for the button-menu, called about-buttons.htm and then 1 webcomponent for each page, that should load according to which button is pushed by the user; about-who.htm, about-manifesto and about-team.
My question is:
How can I with this structure of my web components bind my buttons with my pages - and / or why is my current method wrong?
There's a lot of different ways to do a simple data binding like this. My method here is definitely not simple, and it does not work (by clicking buttons, the pages does not change).
So my about.htm looks like this (and this is the page people will visit):
<about-buttons selected="{{who}}">
</about-buttons>
<about-iron-pages attr-for-selected="name" selected="{{who}}" fallback-selection="who">
<about-us name="who">
</about-us>
<about-manifesto name="manifesto">
</about-manifesto>
<about-team name="team">
</about-team>
</about-iron-pages>
My about-buttons.htm looks like this:
<iron-selector
attr-for-selected="name"
selected="{{buttonSelected}}"
fallback-selection="who"
class="f-column f-column_3 f-column_mobile_2">
<button class="f-button-group__button" name="manifesto">Manifesto</button>
<button class="f-button-group__button" name="who">Who we are</button>
<button class="f-button-group__button" name="team">Team</button>
</iron-selector>
With this script:
<script>
Polymer({
is: 'about-buttons',
properties: {
buttonSelected: {
type: String,
notify: true,
value: 'who'
}
}
});
</script>
And here's my version of iron-pages:
<dom-module id="about-iron-pages">
<template>
<style>
:host {
display: block;
}
:host > ::content > :not(.iron-selected) {
display: none !important;
}
</style>
<content>
</content>
</template>
<script>
Polymer({
is: 'about-iron-pages',
behaviors: [
Polymer.IronResizableBehavior,
Polymer.IronSelectableBehavior
],
properties: {
activateEvent: {
type: String,
value: null,
}
},
observers: [
'_selectedPageChanged(selected)'
],
_selectedPageChanged: function(selected, old) {
this.async(this.notifyResize);
}
});
</script>
</dom-module>

As already pointed out, your attribute in <about-buttons selected="..."> does not match the actual property name. For <about-buttons>.buttonSelected, your attribute should be button-selected, and that's the only change to your code needed to get the selector working (plunker):
<about-buttons button-selected="{{who}}" ...>
Perhaps there's more context to your need for <about-buttons> and <about-iron-pages>, but otherwise, if you're only trying to implement a tabbed view, you could just use Polymer's components.
Specifically, <about-buttons> could be replaced by <paper-tabs> and <about-iron-pages> by <iron-pages> (plunker).

Related

Polymer app-route redirect via function issue

just when I thought I figured out how app-route is working, I run into an issue that let me doubt that my understanding of this element is correct.
From what I understood, it's the responsibility of app-location to keep the browser URL and the value of route in sync. If one of them changes app-location takes care that the other one is changing as well.
And because the route attribute of app-route, is in sync with it's data attribute, changes of the data attribute by the paper-tabs in the code below causes a change in the route attribute of app-route causes the app-location to update the browser URL.
However since I didn't use the fallback-selection attribute in paper-tabs the surfing to http://localhost will set the path to '/' and therefore not showing the home-page.
So I thought I could redirect the URL with the code in the ready function. But unfortunately the route.path indeed changes but the URL doesn't.
Why is that? What do I have to do to redirect the route manually via a function?
Or in other words: Why does a change of routeData.subpage via the paper-tabs element causes a redirect, and a change of routeData.subpage from the function not?
<dom-module id="polymer-app">
<template>
<style>
:host {
display: block;
}
</style>
<app-location route="{{route}}"></app-location>
<app-route id="ar"
route="{{route}}"
pattern="/:subpage"
data="{{routeData}}"
tail="{{routeTail}}"
active="{{routeActive}}">
</app-route>
<header>
<paper-tabs attr-for-selected="name" selected="{{routeData.subpage}}">
<paper-tab name="home">Home</paper-tab>
<paper-tab name="settings">Settings</paper-tab>
</paper-tabs>
</header>
<section id="main">
<iron-pages attr-for-selected="name" selected="[[routeData.subpage]]">
<home-page name="home"></home-page>
<settings-page name="settings"></settings-pagina>
</iron-pages>
</scection>
</template>
<script>
Polymer({
is: 'polymer-app',
properties: {
route: Object,
routeData: Object,
routeTail: Object,
routeActive: Boolean,
},
ready: function() {
if (this.route.path == "/") {
this.set('routeData.subpage', 'home');
console.log(this.routeData);
console.log(this.route);
}
},
});
</script>
</dom-module>
Add a page property with an observer that will either assign it the current value (based on the URL) or a default value if the URL path is empty
static get properties() {
return {
page: {
type: String,
reflectToAttribute: true,
observer: '_pageChanged',
},
}
static get observers() {
return [
'_routePageChanged(routeData.page)',
];
}
_routePageChanged(page) {
// If no page was found in the route data, page will be an empty string.
// Default to 'home' in that case.
this.routeData.subpage = page || 'home';
}

Complete example of Polymer Two Way Binding

The polymer documentation has the following two way binding example:
<script>
Polymer({
is: 'custom-element',
properties: {
someProp: {
type: String,
notify: true
}
}
});
</script>
...
<!-- changes to "value" propagate downward to "someProp" on child -->
<!-- changes to "someProp" propagate upward to "value" on host -->
<custom-element some-prop="{{value}}"></custom-element>
I'm looking for a complete example that includes the design of the child, programmatic and interactive events the can cause upward and downward propagation of the `{{value}} property, and a demo of of the complete setup / example.
Here are some examples on js fiddle that demonstrate different ways of binding:
Two-way binding:
https://jsfiddle.net/tej70osf/
One-way binding: notify is not set on value property of the child element:
https://jsfiddle.net/tej70osf/1/
One-way binding: notify is set to true true on value property of the child element however the value property is bound using square brackets [[value]] instead of {{value}}:
https://jsfiddle.net/tej70osf/2/
Hope that helps
<dom-module id="user-demo">
<template>
<paper-input label="FIRST NAME" value="{{firstName}}"></paper-input>
</template>
</dom-module>
<user-demo></user-demo>
In your javascript code:
Polymer({
is: 'user-demo',
properties: {
firstName: {
type: String,
value: 'John',
notify: true
}
}
});
Check out the following fiddle for the full example:
https://jsfiddle.net/meenakshi_dhanani/6ffwh0qv/
I tried to use more polymer elements and two way binding. Hope it helps

Polymer deactivating pages when their not in view

I'm using the polymer application drawer template from the polymer cli.
I'm having some trouble with:
When you load a new page, the html element is imported; then it's code executes
When I move to another page the code for the previous page is still running.
Is there a way to destroy and create the page/element or suspend and enable?
Whats the best practice for dealing with this problem?
Have the pages implement a create and destroy method and invoke it when changing page?
Ie
oldPageElement.destroy();
newPageElement.create();
Polymer({
is: 'my-random-page',
behaviors: [MyBehaviors.CommonPageBehavior],
/**
* #override
*/
create: function() {..}
/**
* #override
*/
destroy: function() {..}
})
You actually don't need to implement anything complicated, but just use a mere dom-if.
Working prototype: http://jsbin.com/gezihatera/edit?html,console,output
As you can see, the "View One" uses a custom page element, which is always restamped when re-selected. Other pages are ordinary div elements, since this is only a minimal prototype. But this also shows that you can selectively choose which pages get restamped and which do not (if you don't always need this).
The essence is the following: as per dom-if documentation, if you set the restamp attribute to true, then the dom-if will always create and destroy your pages upon selecting/deselecting them. You can see this in the console, where I print out sample-page ready on every ready element. I also create a helper function _equals to help with comparing whether the specified page is really selected.
To sum up, let me paste the code for the app:
<dom-module id="sample-app">
<template>
<style>
:host {
display: block;
}
</style>
<iron-selector selected="{{page}}" attr-for-selected="name">
<a name="view1" href="#">View One</a>
<a name="view2" href="#">View Two</a>
<a name="view3" href="#">View Three</a>
</iron-selector>
<iron-pages role="main" selected="[[page]]" attr-for-selected="name">
<template is="dom-if" if="[[_equals(page, 'view1')]]" restamp="true">
<sample-page name="view1">view1</sample-page>
</template>
<div name="view2">view2</div>
<div name="view3">view3</div>
</iron-pages>
</template>
<script>
Polymer({
is: 'sample-app',
_equals: function(a, b) {
return a == b;
},
});
</script>
</dom-module>
And the code for the sample page:
<dom-module id="sample-page">
<template>
<style>
:host {
display: block;
}
</style>
<content></content>
</template>
<script>
Polymer({
is: 'sample-page',
ready: function() {
console.log('sample-page ready');
},
});
</script>
</dom-module>
Hope this satisfies your question.
Note: you should not put the name attribute on the dom-if itself, but rather onto its content (the same way I did).
Thought I would post my solution after implementing #alesc's dom-if to get the element to be deactivated.
// after a successful importHref, _pageLoaded is called.
_pageLoaded: function(pageName) {
var name = 'my-' + pageName;
this.async(function() {
// async to wait for element restamping, if done
var pages = this.$.pages;
var page = pages.querySelector(name);
page.load()
.then(page.isAuthorized.bind(this))
.catch(this._catchPageIsAuthorizedError.bind(this))
.then(this._shouldSetPage.bind(this, pageName));
}.bind(this));
}

Polymer Iron list display is incorrect

<dom-module id="page-list">
<style include="shared-styles"></style>
<style>
iron-list {
height: 500px;
}
</style>
<template>
<neon-animated-pages id="pages" selected="0">
<neon-animatable><paper-button id="list-button" raised on-tap="_onOK" >List</paper-button></neon-animatable>
<neon-animatable>
<iron-list id="list" items="[[data]]" as="item">
<template>
<div>
Name: <span>[[item.name]]</span>
</div>
</template>
</iron-list>
</neon-animatable>
</neon-animated-pages>
</template>
<script>
Polymer({
is: "page-list",
attached: function () {
this.data=[
{
index: 0,
name: "Liz Grimes"
},
{
index: 1,
name: "Frazier Lara"
},
{
index: 2,
name: "Dora Griffith"
}];
this.$.list.fire('resize');
},
behaviors: [
Polymer.NeonAnimatableBehavior
],
properties: {
data: {
type: Array,
notify: true
}
},
_onOK: function (e) {
this.$.pages.selected="1";
}
});
</script>
After pushing the "List" button, the iron list is expected to be totatlly displayed. Unfortunately, the result is a display of one line with "Name:" and nothing else. If I force the selected page to "1", <neon-animated-pages id="pages" selected="1"> which means that on the first resfresh of the browser, the page with the iron list is displayed, the display is correct : three lines with the correct data.
The issue seems to be related to the fact that on startup, the page with the iron list is not yet displayed.
Anybody knows how to solve this ?
Seems to be a known issue https://github.com/PolymerElements/neon-animation/issues/115
Please check my suggested solution on github.
I think you need
this.set('data', [...]);
instead of
this.data=[...];
for Polymer to get notice of data change
The following solution is merely a hack, but if you listen to the iron-resize event on your neon-animatable and then call notifyResize() on any child iron-list elements you should be able to get around this issue until a more sustainable fix can be made to https://github.com/PolymerElements/neon-animation/issues/115

How can i bind a dynamic function within a polymer component?

As far as my Polymer knowledge goes I can
bind a function using the "on-*" syntax to a webcomponent method
bind a function available in the global window namespace using vanilla html js binding (using onClick="...")
But I want to bind a function (provided as property of datamodel objects) to the webcomponent template.
One sidenote : Moving the datamodel objects to the global javascript namespace (i.e. window.*) is not an option.
The example below does'nt work but reflects exactly my use case :
...
Polymer('x-foo', {
items : [
...,
{
label : "ExampleCommand",
action : function() {
// do something
}
}
...
]
})
...
<template>
<template repeat="{{item in items}}">
<paper-button onClick="{{item.action}}">
{{item.label}});
</paper-button>
</template>
</template>
...
one more question if someone has an idea how to solve the question above) : how can i provide additional arguments to function ?
Any help is appreciated :-)
I had to ask the team about this because it's kinda confusing. Declarative event "bindings" are not the same thing as a Polymer expression. Unfortunately, both event bindings and Polymer expressions use the {{ }} syntax, which implies they work the same. They don't. The scope of event bindings is the element itself, whereas as an expression is scoped to the model for the template instance.
In Polymer 0.8, I believe the syntax has changed, so event bindings no longer use {{ }}. Hopefully that will clear it up a bit.
To achieve the effect you want, you can define a method on the element, which looks at the event target, grabs its model, and calls the function you've defined.
<polymer-element name="x-foo">
<template>
<template repeat="{{items}}">
<button on-click="{{doAction}}">{{label}}</button>
</template>
</template>
<script>
Polymer({
items: [
{
label: 'ExampleCommand',
action: function() {
alert('hello world');
}
},
{
label: 'AnotherCommand',
action: function() {
alert('another command');
}
}
],
doAction: function(e) {
e.target.templateInstance.model.action();
}
});
</script>
</polymer-element>
Here's the example running on jsbin