I'm new to Polymer. I start writing a simple webapp in which
1. User first land in a login page
2. If user passes login, then direct user to content page
I write an element "login"
<dom-module id="login">
<template>
<!-- local DOM for your element -->
<p>Username</p>
<input class="paper-font-body2" value="{{email::change}}" type="email">
<br/>
<p>Password</p>
<input class="paper-font-body2" value="{{password::change}}" type="password">
<p>{{errorMessage}}</p>
<iron-ajax
id="ajax"
url=""
method="POST"
on-response="signInResponse"
debounce-duration="300">
</iron-ajax>
<button on-click="signIn">Signin</button>
</template>
</dom-module>
<script>
// element registration
Polymer({
is: "login",
// add properties and methods on the element's prototype
properties: {
// declare properties for the element's public API
email: {
type: String,
value: "username"
},
password: {
type: String,
value: "password"
},
errorMessage: {
type: String,
value: ""
}
},
signIn: function() {
this.$.ajax.url = "http://myserver/login/email";
this.$.ajax.params = {"email":this.email, "password": this.password};
this.$.ajax.generateRequest();
},
signInResponse: function(request) {
response = request.detail.response;
console.log(response);
if (response.code == '0') {
this.fire("signin-success", response);
} else {
this.fire("signin-fail", response);
}
}
});
</script>
On index.html (main page), I use
<self-login
sign-in-success="onSignedIn"
></self-login>
Question: in the onSignedIn() callback, I would route my page to /content. How can I do?
EDIT 1: As #zacharytamas suggest, I try to use app-router as following
index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>app-router</title>
<script src="../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="stylesheet" href="styles/main.css" shim-shadowdom>
<link rel="import" href="../bower_components/app-router/app-router.html">
</head>
<body unresolved>
<app-router>
<app-route path="/" import="/elements/home-page.html"></app-route>
<app-route path="/test" import="/elements/home-page.html"></app-route>
<app-route path="*" import="/elements/not-found-page.html"></app-route>
</app-router>
<script src="scripts/app.js"></script>
</body>
</html>
home-page.html
<dom-module id="home-page" noscript>
<template>
<h2>Hello</h2>
</template>
</dom-module>
It shows a blank page when I browse to both http://localhost:3000/ and http://localhost:3000/test on Chrome. Any idea?
Polymer does not have routing built into it by default. There are several front-end routing frameworks you can use with Polymer, however.
A very commonly used approach is a custom element called app-router, which lets you define routes declaratively with just HTML alone. I've had some success with it before. Check out the websitefor information on setting it up.
Another HTML-based way of doing it is a custom element made by a member of the Polymer team called more-routing. Google has a video series about Polymer called Polycasts which made a video explaining the more-routing approach. You should check that video out for info about getting started.
Another option is to do it with JavaScript using the page.js framework. This is the method used the Polymer Starter Kit. Here's another Polycast to get you started on that way.
Welcome to the Polymer world!
Good news! The Polymer team has released an official router. An excellent introduction is available on their blog:
https://www.polymer-project.org/1.0/blog/routing
And the Readme.md on the Github repo is quite instructive:
https://github.com/PolymerElements/app-route
There are two elements necessary for basic functionality, <app-location> and <app-route>
The following is a simple example:
<app-location route="{{route}}"></app-location>
<app-route
route="[[route]]"
pattern="/users"
active="{{usersRouteIsActive}}">
</app-route>
In the above example usersRouteIsActive will receive a boolean value, true if the route matches /users, and false if it does not. Simple, right? After this, as your application's routes get more complicated the app-route element has more features that will support those needs.
Try using 'dna-router'. Its relatively new and supports Polymer-1.0.
You can create routing purely in html.
Dna-router also supports user-authentication. You can pass login status and loggedin data in its 'dna-config' element. Router will show page based on login status.
You can declare which states need user authentication.
Its still under development and have some glitches in it. But worth giving a try.
Github: https://github.com/Saquib764/dna-router/
The answer depends on the kind of router you use. I was unhappy with the state of Polymer routers, so I wrote excess-router. With this router, you would:
define your routes, and make /content is a default route
configure router to be started manually
start the router manually on signin
<excess-router-config manual-start></excess-router-config>
<excess-route route="/content"></excess-route>
<excess-route route="/(.*)" redirect-to="/content" activation-modifiers="x"></excess-route>
<script>
function onSignedIn() {
Excess.RouteManager.start();
}
</script>
I solving this problem just adding in page template this code:
<script>
Polymer({
is: 'home-page'
});
</script>
Full page code:
<link href="../bower_components/polymer/polymer.html" rel="import">
<dom-module id="home-page">
<template>
<div>Home page</div>
</template>
<script>
Polymer({
is: 'home-page'
});
</script>
</dom-module>
As of Polymer 1.4, carbon-route (later renamed app-route) can be used:
https://github.com/polymerelements/carbon-route
https://blog.polymer-project.org/announcements/2016/03/28/carbon-route-released/
https://www.polymer-project.org/1.0/articles/routing.html
Here's an example taken from the polymer blog:
<carbon-location route="{{route}}">
</carbon-location>
<carbon-route route="{{route}}" pattern="/tabs/:tabName" data="{{data}}">
</carbon-route>
<paper-tabs selected="{{data.tabName}}" attr-for-selected="key">
<paper-tab key="foo">Foo</paper-tab>
<paper-tab key="bar">Bar</paper-tab>
<paper-tab key="baz">Baz!</paper-tab>
</paper-tabs>
<neon-animated-pages selected="{{data.tabName}}"
attr-for-selected="key"
entry-animation="slide-from-left-animation"
exit-animation="slide-right-animation">
<neon-animatable key="foo">Foo Page Here</neon-animatable>
<neon-animatable key="bar">Bar Page Goes Here</neon-animatable>
<neon-animatable key="baz">Baz Page, the Best One of the Three</neon-animatable>
</neon-animated-pages>
See also similar question: Routing in polymer 1.0
Related
I want to insert HTML which I write in the custom element into the custom element. For example:
<dom-module id="site">
<template>
<!-- other HTML -->
<!-- insert the HTML -->
<!-- other HTML -->
</template>
</dom-module>
<site>
<!-- HTML to insert -->
</site>
Check out Polymers Templatizer behavior.
https://www.polymer-project.org/1.0/docs/api/Polymer.Templatizer
You can use a template and stamp out the instances that you require in your element.
Start by including the Templatizer behavior.
Then put your desired HTML into a tag (within your element).
In your attached method you can load that template into the Templatizer with a call to templatize() that is mixed in from the behavior.
Then when you are ready to use that content you 'stamp' an instance of it. When stamping the instance you can pass in your own model object to represent the data that can be referenced within the scope of the instance.
<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../paper-button/paper-button.html">
<dom-module id="my-el">
<template>
<template id="stampTemplate">
<p>[[message]]</p>
</template>
<paper-button raised on-click="doStamp">Stamp</paper-button>
<div id="myStamps"></div>
</template>
<script>
Polymer({
is: 'my-el',
behaviors: [Polymer.Templatizer],
attached: function() {
// load/templatize the template
var template = this.$.stampTemplate;
this.templatize(template);
},
doStamp: function() {
var instance = this.stamp({message: "Hello World"});
this.$.myStamps.appendChild(instance.root);
}
});
</script>
</dom-module>
Lastly, you can append that instance to your page/element as you see fit.
In the example above, I have a template that is a simple paragraph containing a message. When the paper button is clicked I stamp a new instance of the template with the message "Hello World". The instance of this template is then appended to the 'myStamps' div.
First off, I apologize for the title-gore. I struggled to summarize this issue in a single sentence.
I have a Polymer element. In my element, I wish to display (or simply log to console) the innerHTML of a template element that is a child of (contents of) the element instance.
(Please note that the intent here is to create an element similar to the demo-snippet of iron-demo-helpers.)
Consider the following Polymer element:
<link rel="import" href="../../polymer/polymer.html">
<dom-module id="print-contents-html">
<script>
Polymer({
is: 'print-contents-html',
attached: function() {
var template = Polymer.dom(this).queryDistributedElements('template')[0];
console.log(template.innerHTML);
}
});
</script>
</dom-module>
Very simply, I query for the 'template' element and log its inner HTML.
Now consider the following usage:
<!doctype html>
<html>
<head>
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="./print-contents-html.html">
</head>
<body>
<print-contents-html>
<template>
Hello World
</template>
</print-contents-html>
</body>
</html>
As expected, this results in "Hello World" being logged to the console.
However, I am trying to make use of the 'print-contents-html' element within another Polymer element:
<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="./print-contents-html.html">
<dom-module id="simple-demo">
<template>
<print-contents-html>
<template>
Hello World from simple demo
</template>
</print-contents-html>
</template>
<script>
Polymer({
is: 'simple-demo',
});
</script>
</dom-module>
Now if I update the usage to also include an instance of the simple-demo element:
<!doctype html>
<html>
<head>
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="./print-contents-html.html">
<link rel="import" href="./simple-demo.html">
</head>
<body>
<print-contents-html>
<template>
Hello World
</template>
</print-contents-html>
<simple-demo></simple-demo>
</body>
</html>
I would expect to see both "Hello World" and "Hello World from simple demo" logged to the console. However the simple demo message is not logged.
It appears that while the queryDistributedElements() does return the instance of the template element, the innerHTML field is an empty string.
Does anyone know why this is the case? That accessing the content/child template element does not have its innerHTML set?
Secondly, does anyone know of an alternative method through which I can access the innerHTML of the template element that is a content/child of a polymer element?
Kind Regards,
Andrew Butler
This issue is discussed in Polymer's Github repository.
The solution is to add a preserve-content attribute it the inner <template> tag:
<dom-module id="simple-demo">
<template>
<print-contents-html>
<template preserve-content>
Hello World from simple demo
</template>
</print-contents-html>
</template>
...
Then it works!
I am am trying to create a custom element that plays a youtube video in paper-dialog. So videoPlayer = Polymer.dom(this.root).querySelector('video-player'); inherits/has access to that paper-dialogs open method, I am trying to extend my custom element. It isn't working, but hopefully I am on the right track and someone can show me correctly.
I am using Polymer 1.0, but I only have https://www.polymer-project.org/0.5/docs/polymer/polymer.html#extending-other-elements to go by for extending elements.
<link rel="import" href="../bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../bower_components/google-youtube/google-youtube.html">
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="video-player">
<template>
<div class="layout horizontal">
<paper-button dialog-dismiss>
<paper-icon-button icon="arrow-back"></paper-icon-button>
</paper-button>
</div>
<div style="height: 100%; width: 100%">
<google-youtube style="height: 100%;"
video-id="YMWd7QnXY8E"
rel="1"
start="5"
playsinline="0"
controls="2"
showinfo="0"
width="100%"
height="100%"
autoplay="1">
</google-youtube>
</div>
</template>
<script>
Polymer({
is: "video-player"
});
</script>
<paper-dialog name="video-player" extends="video-player">
<template>
<shadow></shadow>
</template>
<script>
Polymer();
</script>
</paper-dialog>
<video-player></video-player>
As was mentioned in the comments, you can't yet extend custom elements, so the existing pattern (or at least the one I use) is to make use of behaviors wherever possible and wrappers wherever not.
e.g.
<dom-module id="popup-video-player">
<template>
<video-player></video-player>
</template>
<script>
Polymer({
is: 'popup-video-player',
behaviors: [Polymer.PaperDialogBehavior],
...
});
</script>
</dom-module>
Now you can use <popup-video-player> just like a paper-dialog.
I know it stinks because if video-player has a bunch of properties that you want access to, you have to copy them in the popup-video-player element's API, which is not exactly DRY.
If you look at the paper-input source, you'll see them doing the same thing. It's obvious that they want to extend iron-input, but they can't so you get things like this:
<input is="iron-input" id="input"
aria-labelledby$="[[_ariaLabelledBy]]"
aria-describedby$="[[_ariaDescribedBy]]"
disabled$="[[disabled]]"
title$="[[title]]"
... >
As a side note, you could always hook into the <video-player>s "properties" property and make the API additions programatically.
maybe something like this would work: (untested!)
Polymer({
...
properties: (function () {
var prop = {
//special properties specific to the pop up version of video-player
//..obviously be careful to avoid name space conflicts.
};
var video_player = document.createElement('video-player');
video_player.properties.keys().forEach( function(key) {
props[key] = video_player[key];
});
return props;
}()),
});
I've created a test polymer element where in I was figuring out how to use use arrays in templates. My code does not work and the documentation for 1.0 doesn't really talk much about how to use repeat in template tags.
my element:
<!-- Imports polymer -->
<link rel="import" href="polymer/polymer.html">
<!-- Defines element markup -->
<dom-module id="my-element" >
<template>
<style>
my-element
</style>
<h2>{{data}}</h2>
<ul>
<template repeat={{column in columns}} bind>
<li>{{column}}</li>
</template>
</ul>
</template>
</dom-module>
<!-- Registers custom element -->
<script>
Polymer({
is: 'my-element',
// Fires when an instance of the element is created
created: function() {
},
// Fires when the local DOM has been fully prepared
ready: function() {},
// Fires when the element was inserted into the document
attached: function() {},
// Fires when the element was removed from the document
detached: function() {},
// Fires when an attribute was added, removed, or updated
attributeChanged: function(name, type) {
alert("changed");
},
properties:{
data :String,
columns:Array
}
});
</script>
and the index.html page where I'm using the element:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><my-repo></title>
<!-- Imports polyfill -->
<script src="webcomponents-lite.min.js"></script>
<!-- Imports
custom element -->
<link rel="import" href="my-element.html">
<!-- Runs custom element -->
<my-element users = '{{[1,2,3,4]}}' data="This is a polymer table"></my-element>
Please let me know what's wrong with my code!!
You have to use
<template is="dom-repeat" items="{{users}}">
<li>{{item}}</li>
</template>
And in main file:
<my-element users="[1,2,3,4]" data="This is a polymer table"></my-element>
You can search Youtube for Polycast, a series by Google Developers where they're talking about Polymer for beginners and showing cool tricks.
Polymer 1.0 does not allow expressions in data binding. The problem is in:
<my-element users = '{{[1,2,3,4]}}' ...>
You need to replace {{[1,2,3,4]}} with a property. Something like this:
<template is="dom-bind">
<my-element users = '{{myarray}}' data="This is a polymer table"></my-element>
</template>
<script>
(function() {
var template = document.querySelector('template[is="dom-bind"]');
template.myarray = [1,2,3,4];
})();
</script>
Sometimes it takes a while for polymer to load, and when using <body unresolved>, the page stays blank until everything is ready. Is there a way to display something between the time that the page is served and the time that polymer is done doing its magic?
The documentation that describes the unresolved attribute clears some of this up.
While it's common to apply unresolved to the <body> element, causing the entirety of your page's content to be hidden until Polymer is ready, it can be applied to any element(s). You can, for instance, use <div unresolved> as a wrapper around the portion of your page that relies on Polymer, and create a loading message that's outside that wrapper which will be visible immediately. (You'd then want to listen to the polymer-ready event and hide your loading message when that's fired.)
Here's an example using a very contrived way of slowing down the time it takes for the Polymer element to complete one of its lifecycle methods (live demo):
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Polymer Demo</title>
<style>
.hidden {
display: none;
}
</style>
</head>
<body>
<p id="spinner">Loading...</p>
<script src="http://www.polymer-project.org/platform.js"></script>
<link rel="import" href="http://www.polymer-project.org/components/polymer/polymer.html">
<polymer-element name="slow-poke">
<template>
<h1><content></content></h1>
</template>
<script>
Polymer({
// Used to introduce a delay in initializing the Polymer element.
// Don't try this at home!
created: function() {
var start = Date.now();
while (true) {
if (Date.now() - start > 1000) {
break;
}
}
}
});
</script>
</polymer-element>
<div unresolved>
<slow-poke>Here I am... finally!</slow-poke>
<slow-poke>Me too!</slow-poke>
</div>
<script>
window.addEventListener('polymer-ready', function() {
document.querySelector('#spinner').classList.add('hidden');
});
</script>
</body>
</html>
(By the way, what are you finding to be slow-loading? If it's a standard/core element, it might be worth filing a bug against the corresponding project on GitHub.)