Routing to landing page - polymer

I am using Polymer and trying to get the app-router setup. I cannot get the app to direct the user to the landing page.
I have tested all of the other pieces individually, so I know that my pages will render.
<dom-module id="app-body">
<template>
<app-location route="{{route}}" use-hash-as-path></app-location>
<app-route route="{{route}}" pattern="/:page" data="{{data}}"></app-route>
<iron-pages id="content-pages" selected="{{data.page}}" attr-for-selected="name">
<landing-page name="landing"></landing-page>
<lobby-page name="lobby"></lobby-page>
</iron-pages>
</template>
<script>
window.addEventListener('WebComponentsReady', function() {
Polymer({
is: 'app-body',
properties: {
data: {
type: Object,
value: function() {
return {
page: '/landing'
};
notify: true
}
}
},
observers: [
'_onRoutePathChanged(route.path)'
],
_onRoutePathChanged: function(path) {
// If we do not have an initial URL, we redirect to /landing
if (!path) {
this.set('route.path', '/landing/');
}
}
});
});
</script>
</dom-module>

Firstly, you appear to have the same misconception I had, that to make properties transfer downwards you need notify true. That is not so, you only need notify true to export from your element to a parent element that uses it. In your case that is not needed.
What might be happening is that routeData gets set to {page: '/landing'} during your element initialization. At that point the route is not active so routeData is not mapped back to the route.path and fed back through app-location to the url. When it does eventually map back through, there is no change to routeData so the observer doesn't fire to tell you it has changed.

Related

Handling of loading indicator for multiple ajax calls in Polymer

I'm using Polymer 1.0 to create a web application.
I have various elements doing one or more iron-ajax calls and I have another element for showing the loading-overlay. But in my current sollution I have added the loading-overlay, with its logic to show or not, to every element doing ajax calls.
<dom-module id="backend-call-application">
<template>
<iron-ajax id='loadA' loading="{{_loadingA}}" ...></iron-ajax>
<iron-ajax id='loadB' loading="{{_loadingB}}" ...></iron-ajax>
<loading-overlay id="loadingOverlay" with-backdrop></loading-overlay>
</template>
<script>
Polymer({
is: 'backend-call-application',
observers:[
"_isXhrLoading(_loadingA,_loadingB,....)"
],
_isXhrLoading: function() {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i]) {
this.$.loadingOverlay.open()
return;
}
}
this.$.loadingOverlay.close()
}
});
</script>
</dom-module>
Now my question is, what is the best way to show such a loading-overlay?
One idea of mine would be, to have something like an observer in the loading overlay. So every element doing requests will bind its properties to the observer. These properties could be stored in an array and everytime on change, the loading-overlay checks if at least one have loading properties set to true. When one or more properties are true the loading-overlay will be opened and when all requests finished loading it will be closed.
Another idea was to use events to tell the loading-overlay when a element starts/stops loading. But here will be the problem, that I have more than one request at the same time (The first request closes the overlay, but the page hasn't finished loading).
Edit:
The loading-overlay is an element containing the IronOverlayBehavior.
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-overlay-behavior/iron-overlay-behavior.html">
<link rel="import" href="../../../bower_components/paper-spinner/paper-spinner.html">
<dom-module id="loading-overlay">
<template>
<paper-spinner active="true"></paper-spinner>
</template>
<script>
Polymer({
is: 'loading-overlay',
behaviors: [
Polymer.IronOverlayBehavior
]
});
</script>
</dom-module>
use one property for loading instead of 2!!
<iron-ajax id='loadA' loading="{{_loading}}" ...></iron-ajax>
<iron-ajax id='loadB' loading="{{_loading}}" ...></iron-ajax>
bind loading value to loading-overlay with some attribute and when loading is true display loader
<loading-overlay id="loadingOverlay" is-loading="[[_loading]]" with-backdrop></loading-overlay>
so each time any ajax is made then _loading will become true so displays loader
I think the only thing you could shorten is the _isXhrLoading observer.
_isXhrLoading: function() {
if(Array.from(arguments).indexOf(true) >= 0) {
this.$.loadingOverlay.open()
} else {
this.$.loadingOverlay.close()
}
}

Polymer 1.6 - App shows blank page after refresh. I am using <app-route> router

I am new to Polymer and am using Polymer version 1.6.0.
Use Case : User should see login screen when app is initiated. After successful login, he should be directed to a content page.
**This is working on my localhost (127.0.0.1/index.html), but when I try to run it on external server with path "myapplication.io/myapp/admin/index.html" it is showing blank screen.
This is the code I am using:
index.html:
<body>
<myadmin-app></myadmin-app>
</body>
myadmin-app.html:
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"
active="{{active}}"></app-route>
<app-header condenses reveals effects="waterfall">
<app-toolbar>
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
<div main-title style="text-align: center;">MY ADMIN</div>
</app-toolbar>
</app-header>
<iron-pages
selected="[[page]]"
attr-for-selected="name"
selected-attribute="active"
fallback-selection="not-found"
role="main">
<config-option name="config-option"></config-option>
<register-login name="register-login"></register-login>
<other-option name="other-option"></other-option>
<not-found name="not-found"></not-found>
</iron-pages>
<script>
(function() {
Polymer({
is: 'myadmin-app',
properties: {
page: {
type: String,
reflectToAttribute: true,
observer: '_pageChanged'
},
storedUser: Object
},
ready: function(){
},
observers: [
'_routePageChanged(routeData.page)'
],
_routePageChanged: function(page) {
console.log("routechanged page = " + page);
this.page = page || 'register-login';
},
_pageChanged: function(page) {
// Load page import on demand. Show 404 page if fails
var resolvedPageUrl = this.resolveUrl(page + '.html');
this.importHref(resolvedPageUrl, null, this._showPage404, true);
},
_showPage404: function() {
this.page = 'not-found';
}
});
}());
</script>
The resolvedPageURL in the above code is coming as "myapp" instead of "/" when I run it on the server. Thus the app is trying to find "myapp.html" page which is non existing. It should be getting "/" so that it will be redirected to "register-login.html" page where login code is present.
Please let me know where I am going wrong
I solved the issue by using hash URL as shown:
<app-location route="{{route}}" use-hash-as-path></app-location>
I used it in all the files where is used.

On attached, `this.$` an empty object

I'm trying to observe added and removed dom nodes to my custom Polymer element without success. I've noticed that the this.$ is an empty object.
Polymer({
is: 'dramatic-view',
attached: function () {
this._observer = Polymer.dom(this.$.contentNode).observeNodes(function (info) {
console.log('kromid info', info);
});
}
});
The callback is being called only once (even tho I change content afterwards) with the following strange parameters:
I was following the docs here.
this.$ is a hash of elements in your template that have an id attribute (see Automatic node finding).
So, for this.$.contentNode to exist, you need an element with id="contentNode" in your <dom-module>'s <template>, and it must not be inside a dom-if or dom-repeat template:
<dom-module id="x-foo">
<template>
<content id="contentNode"></content>
</template>
</dom-module>
Nodes created dynamically (i.e., those inside dom-if or dom-repeat) must be queried with this.$$() (e.g., this.$$('#contentNode')). If you're trying to set them up in the attached callback, you'll need to wait until after the nodes are stamped, which could be observed with Polymer.RenderStatus.afterNextRender().
<dom-module id="x-foo">
<template>
<template is="dom-if" if="[[useDynamicContent]]">
<content id="contentNode"></content> <!-- unavailable to this.$ -->
</template>
</template>
<script>
Polymer({
is: 'x-foo',
attached: function() {
Polymer.RenderStatus.afterNextRender(this, function() {
var contentNode = this.$$('#contentNode');
if (contentNode) {
contentNode.observeNodes(...);
}
});
}
});
</script>
</dom-module>
To trigger the node observer, use Polymer's DOM API like this:
Polymer.dom(this).appendChild(node);
Polymer.dom(this).removeChild(node);
codepen
UPDATE It seems you're integrating Polymer with Elm, and expecting to see the observer callback for node-changes made my Elm. You'll need to hook Elm's calls to appendChild/removeChild somehow so that it uses Polymer's DOM API.

Object.observe() useable with Polymer?

In my ideal world, I'd love for Polymer components to be able to use Object.observe() transparently to listen for changes to the properties of models, without the model objects having to themselves be custom elements.
In the example below, I have an element that has a property model of type Object. The HTML template uses {{model.name}} and I would like the element to update automatically whenever that property changes.
In my demo/example, the hacky way I accomplish this is by using Object.observe() within custom code, and when any change happens, I set model to undefined and back again. This "refreshes" the UI and picks up the changes. Of course, in any nontrivial UI, this would be very janky, so this is a bit of a "hope is not a strategy" moment....
Is there any design pattern for tersely doing this sort of thing right now, and/or a roadmap for doing such things in the future?
This is my custom element:
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="test-view">
<template>
<p>Hello, I am called <span>{{model.name}}</span>, how are you?</p>
</template>
</dom-module>
<script>
Polymer({
is: 'test-view',
properties: {
model: Object,
},
observers: [
'_modelChanged(model)'
],
ready: function() { },
attached: function() { },
detached: function() { },
_modelChanged: function(model) {
if (model) { Object.observe(model, this._observer.bind(this)); }
},
_observer: function() {
const oldModel = this.model;
this.model = undefined;
this.model = oldModel;
}
});
</script>
and this is an HTML page that drives it through its state transitions:
<html>
<head>
<script src="../bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="../test-view/test-view.html">
</head>
<body>
<test-view id="first" model='{ "name": "Wilma" }'></test-view>
<script>
const newModel = { name: 'Fred' };
const testView = document.querySelector('#first');
window.setTimeout(function() { testView.model = newModel; }, 2000);
window.setTimeout(function() { newModel.name = 'Barney'; }, 4000);
</script>
</body>
</html>
This is probably not a complete answer to your question, more of comment, but since code does not look well in comments I'm still posting it as an answer. I think that kind of thing may even have worked in 0.5 without the additional observer. However, to improve performance this kind of behaviour was removed in 1.0. I also hope we get it back sometime.
To avoid setting the model to undefined and back to trigger a refresh, you could also just notify Polymer of the changes. I assume that this would be more efficient, especially when you have complex objects and lots of data-binding.
_observer: function(changes) {
changes.forEach(function(change){
this.notifyPath("model." +change.name, change.object[change.name]);
}.bind(this));
}

Polymer 1.0 services issue

I'm working on a reddit client using polymer to check out web compoments technologies. I started with the 0.5 version and got back on this project recently. That when I found out that polymer had the 1.0 released so I started over (as it wasn't that advanced anyway).
I have a service that use the iron-ajax to request reddit api and look for the posts. Here is the code :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax
url='https://www.reddit.com/new.json'
handle-as='json'
debounce-duration="300"
on-response='handleResponse'
debounce-duration="300"
auto>
</iron-ajax>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-list-service',
properties: {
modhash: {
type: String,
value: function() {
return '';
}
},
posts: {
type: Array,
value: function () {
return [];
}
},
after: {
type: String,
value: function () {
return '';
}
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.properties.modash = resp.detail.response.data.modhash;
this.properties.posts = resp.detail.response.data.children;
this.properties.after = resp.detail.response.data.after;
this.post = this.properties.posts; // just to try
console.log(this.properties.posts);
}
});
})();
</script>
My log shows me that I get posts from the API and that's great!
Here's the issue when I want to use this service to make a list out of the posts array I can't figure out how to get them into my list compoment which is below :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../reddit-list-service/reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}">
</reddit-list-service>
<template is="dom-repeat" id="post-list" posts="{{posts}}">
<p>{{post.author}}</p>
<template>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-post-list',
properties: {
},
});
})();
</script>
I've tried several think I saw in the documentation but I can't figure out what's wrong the author property doesn't show up.
Any clue?
You have a few things that aren't quite right here. In reddit-post-list you are not using the dom-repeat template correctly. See below:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}"></reddit-list-service>
<template is="dom-repeat" items="[[posts]]">
<p>{{item.data.author}}</p>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-post-list"
});
</script>
You need an attribute called items which is the array the dom-repeat will iterate over. Inside the iteration you need to refer to item as this is the array item for the current iteration. Here are the docs.
For you reddit-list-service, you need to set the the reflectToAttribute and notify attributes to true on your posts property. This means that any changes to this property are reflected back on the posts attribute on the reddit-list-service element. See here for more information.
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax auto url="https://www.reddit.com/new.json" handle-as="json" on-response="handleResponse"></iron-ajax>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-list-service",
properties: {
modhash: {
type: String,
value: ""
},
posts: {
type: Array,
value: function () {
return [];
},
reflectToAttribute: true, // note these two new attributes
notify: true
},
after: {
type: String,
value: ""
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.modash = resp.detail.response.data.modhash;
this.posts = resp.detail.response.data.children;
this.after = resp.detail.response.data.after;
}
});
</script>
I have also tidied up the following things:
Removed the immediately called function wrapper from the <script> tags as these are not needed.
When referring to properties in your element you only need to use this.PROPERTYNAME rather than this.properties.PROPERTYNAME.
Having looked at the JSON returned, it appears that the author property is on the another property called data.
When you declare a value for a property in your element, you only need to have a function that returns a value if the property type is an Object or Array.