2 Layouts in Polymer (ie: Main-App, Signup) - polymer

In the PSK, the index.html references a my-app.html template. In there I have my main layout with app-drawer, header ect..
How do I then have a page without that template which just has for example a centered card with login fields.
Do I somehow use my-app.html to just contain nested layouts for each?
index.html
-my-app.html
--main-shell.html (main content with iron-pages)
--blank.html (for signup/login)
Thanks.

I recommend to those that ask, that below the my-app element you create a my-session element which handles authentication and a my-pages element to manage the application itself. Within my-session I unhide a login element if that is needed (I allow users with an authenticated cookie to access the application without logging in).
My my-app's outline is like this
<app-header-layout>
<app-header
fixed
effects="waterfall">
<app-toolbar>
...
</app-toolbar>
</app-header>
<my-session id="session" user="{{user}}"></my-session>
<my-pages
id="pages"
user="[[user]]"
unresolved
hidden="[[!isLoggedOn]]" >APPLICATION LOADING</my-pages>
</app-header-layout>
my-session then has a my-login element which will contain the login form centered on a paper-card in the middle of the page
This is done with
<dom-module id="my-session">
<template>
<style>
[hidden] {
display: none!important;
}
</style>
<iron-ajax
id="validateuser"
url="/api/validate_user"
handle-as="json"
method="POST"
body="{}"
content-type="application/json"
on-response="_validated"></iron-ajax>
<iron-ajax
id="logoff"
url="/logoff"
body="{}"
handle-as="json"
method = "POST"
content-type="application/json"></iron-ajax>
<my-waiting waiting="[[waiting]]"></my-waiting>
<my-logon user="{{user}}" hidden="[[!needsLogon]]"></my-logon>
</template>
<script>...</script>
</dom-module>
The iron-ajax requests are to validate a users cookie (I still ask the server to validate it, since it contains an encrypted token) and to log of the user. This is all handled in javascript which I don't show (for simplicities sake).

Related

how to include google-map in polymer#^2.0 as it was working in polymer 1.7

I have included api, before everything were working perfect in polymer 1.7, after upgraded to polymer ^2.0 google map does not renders.
here is my code in main app page written polymer class base:
<iron-pages role="main" selected="[[page]]" attr-for-selected="name" selected-attribute="visible" fallback-selection="404">
<jj-maps name="maps" user="{{user}}" sprof="{{sprof}}"></jj-maps>
<jj-list name="list" > Jobs </jj-list>
<jj-infos name="infos"> infos </jj-infos>
<jj-contacts name="contacts" > Contacts </jj-contacts>
<jj-messages name="messages"> Messages </jj-messages>
<jj-404 name="404" > 404 </jj-404>
</iron-pages>
at jj-maps.html code sample is :
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/google-map/google-map.html">
<link rel="import" href="../bower_components/google-map/google-map-marker.html">
style codes in template tag:
<dom-module id="jj-maps">
<template>
<style include="iron-flex iron-flex-alignment">
:host {
.....
google-map, #mapResults {
margin-top: 10px;
position: relative;
height: 100vh;
width: 100vh%;
z-index: 1;
}
....
<div id="mapResults">
<google-map
id="map"
map="{{map}}"
latitude="[[latitude]]"
longitude="[[longitude]]"
zoom="10"
api-key="[[myApiKey]]"
on-google-map-ready= '_mapLoaded'
additional-map-options='{"gestureHandling" : "greedy"}'
>
<google-map-marker map="{{map}}" slot="marker" latitude="{{latitude}}" longitude="{{longitude}}"
title="You are here !" icon="./src/image/gpslocc.png" draggable="true">
</google-map-marker>
<template is="dom-repeat" items="{{sprof}}" as="item">
<google-map-marker map="[[map]]" slot="marker" latitude="[[item.myLat]]" longitude="[[item.myLng]]" animation="DROP" click-events title="{{item.prof}}" icon="{{calculateIconType(item.isFree)}}" on-google-map-marker-click='showUserDetail' userid="[[item.uid]]" isFree="{{item.isFree}}" >
</google-map-marker>
</template>
<paper-fab icon="maps:my-location" on-tap="updateCurrentPosition"></paper-fab>
</google-map>
</div>
</template>
this code works perfect in previous polymer. And Another point when I bower install the dependencies as bower install --save GoogleWebComponents/google-map
bower asks me for two as :
- Unable to find a suitable version for polymer, please choose one by typing one of the numbers below:
I chose : 7) polymer#^2.0.0 which resolved to 2.0.1 and is required by myApp
- Unable to find a suitable version for webcomponentsjs, please choose one by typing one of the numbers below:
I chose : 2) webcomponentsjs#^1.0.0 which resolved to 1.0.1 and is required by myApp
Sorry in advance that I ve written detailed codes. Meanwhile I have tried many options but could not able to render map in my jj-maps elements (class base template, even I have tried legacy template (as same as polymer 1.7 ver)
Here is is console warning :
dom-module.html:24 dom-module google-map has style outside template
dom-module google-map-marker has style outside template
Maps are not rendering...
So, how to solve ? Thanks in advance. (previous polymer ver. working at jobijoy.com
I'm having exactly same issue. Tried to add slot="marker" to google-map-marker as someone suggested Polymer 2.0 issue in github but doesn't work for me. It looks like the google-map component's size is set to 0 by 0 thus does not display at all. Since the google components are not updated to be compatible with Polymer 2.0. I've manually updated my local google-map.html and google-map-marker.html files under bower_component folder and moved the style tags into the template tags. The warnings are gone and the map displays now. I guess we have to wait for the update of the google components for things to work properly. I hope this helps.
slot="marker"
should solve the issue.
Unfortunately the example in the code does not insert this important line.
Here is the without slot="markers" current code example:
<google-map latitude="37.77493" longitude="-122.41942" fit-to-markers>
<google-map-marker latitude="37.779" longitude="-122.3892" draggable="true" title="Go Giants!">
</google-map-marker>
<google-map-marker latitude="37.777" longitude="-122.38911">
</google-map-marker>
</google-map>
And below the suggested corrected example that should be inside the code:
<google-map latitude="37.77493" longitude="-122.41942" fit-to-markers>
<google-map-marker slot="markers" latitude="37.779" longitude="-122.3892" draggable="true" title="Go Giants!">
</google-map-marker>
<google-map-marker slot="markers" latitude="37.777" longitude="-122.38911">
</google-map-marker>
</google-map>
What is happening?
google-map does not know when markers are inserted or updated because without the attribute slot="markers" they are inserted outside the slot where they should be inserted in.
Several consequences can happen from this issue: the map can be viewed, but not respond to important events, like fit-to-markers.
In these turbulent moves from original without-slots version to the current slotted version (and soon moving to polymer 3.0 version) of the excellent google-map webcomponent, there is a high chance that the busy developers simply forgot to add this important slot="markers" attribute in the example that shows how to add each new google-map-marker webcomponent inside google-map.
This only complements the already correct above answer. If anyone needed more explanation, I offer these, If I am correct, of course (please correct me if I made wrong conclusions).
You can detect this flaw inspecting your google-map element. Without the slot="markers" correction, open google-map; inside it, open iron-selector; and finally inside it open the element <slot id="markers" name="markers"><slot>. If you do not use the suggested correction, this slot will be empty. If you use this suggested correction, this slot will contain references to each google-map-marker. And so the google-map element will be able to handle several events that rely on detecting when each marker is inserted or updated.
Sorry for the long answer.

How to use app-route in polymer 2 to have deep paths?

This is part of polymer application starter kit code. I just added a my-news-list.html to elements in src:
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"></app-route>
<app-drawer-layout fullbleed>
<!-- Drawer content -->
<app-drawer id="drawer" slot="drawer">
<app-toolbar>Menu</app-toolbar>
<iron-selector selected="[[page]]" attr-for-selected="name" class="drawer-list" role="navigation">
<a name="news" href="/news">News</a>
<a name="view1" href="/view1">View One</a>
</iron-selector>
</app-drawer>
<!-- Main content -->
<app-header-layout has-scrolling-region>
<app-header slot="header" condenses reveals effects="waterfall">
<app-toolbar>
<paper-icon-button icon="my-icons:menu" drawer-toggle></paper-icon-button>
<div main-title>My App</div>
</app-toolbar>
</app-header>
<iron-pages
selected="[[page]]"
attr-for-selected="name"
fallback-selection="view404"
role="main">
<my-news-list name="news" route="{{subroute}}"></my-news-list>
<my-view1 name="view1"></my-view1>
<my-view404 name="view404"></my-view404>
</iron-pages>
</app-header-layout>
</app-drawer-layout>
Everything is OK, <my-news-list> and <my-view1> loads correctly when click on theme in <iron-selector>. in <my-news-list> element I got list of all news with <iron-ajax> and it works fine:
<iron-ajax auto url="localhost/api/news/news" handle-as="json" last-response="{{newsList}}"></iron-ajax>
<template is="dom-repeat" items="{{newsList}}">
[[item.title]]
</template>
I have a other element for viewing single news content, named <my-news-view> that I want to load it when click on title of each news in <my-news-list>. On click path changes to: localhost:8081:/news/2 correctly but <my-news-list> element loads again Instead of <my-news-view>.
I don't paste other elements codes(bindings, ...).
Now I want to know how config <app-route> and use subroute to have load elements in this paths:
// For News
/news //all news list. loads my-news-list element
/news/12 //news[12] view. loads my-news-view elemnt
/news/categories //all categories list view. loads my-news-categories element
/news/categories/3 //category[3] news list view. loads my-news-category element
// and jobs similar to module
/Jobs
/jobs/country/cityName
/jobs/country/cityName/featured
Thanks for your help.
You should declare you "my-news-view" component in "iron-pages" just like you do:
<iron-pages selected="[[page]]" attr-for-selected="name"
fallback-selection="view404" role="main">
<my-news-view name="news-view" route="{{subroute}}" />
</iron-pages>
So iron-pages would know what component to show when user navigate to /news-view/12 based on name attribute not component name itself.
BUT. There are some tricky parts on your solution:
Note how you pass subroute to child components to decouple it internal
routing mechanism from actual url path where it resides.
Starter kit uses lazy-loading to import components and inside that function it adds prefix 'my-' to actual route 'news-view'. So that is how result component name to load become 'my-news-view'. That may confuse.
You could read more about this polymer shop case study.
PS: personally I think that the way the polymers sample app routing works confused a lot because it looks like web-server static name resolution (based on component name, and not components attribute 'name') but it don't.
You should add the component inside the component and then use the sub route to know the id for
Try dna-router. It is now compatible with Polymer 2.0 project.
Please follow this answer - https://stackoverflow.com/a/31236817/5069120
Or visit https://github.com/Saquib764/dna-router

How do I change the route from inside a view in Polymer?

In a simple routing configuration in Polymer, like that obtained from the starter-kit:
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"></app-route>
<app-drawer-layout fullbleed>
<!-- Drawer content -->
<app-drawer>
<app-toolbar>Menu</app-toolbar>
<iron-selector selected="[[page]]" attr-for-selected="name" class="drawer-list" role="navigation">
<a name="view1" href="/view1">View One</a>
<a name="view2" href="/view2">View Two</a>
<a name="view3" href="/view3">View Three</a>
</iron-selector>
</app-drawer>
<!-- Main content -->
<app-header-layout has-scrolling-region>
<app-header condenses reveals effects="waterfall">
<app-toolbar>
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
<div main-title>My App</div>
</app-toolbar>
</app-header>
<iron-pages
selected="[[page]]"
attr-for-selected="name"
fallback-selection="view404"
role="main">
<my-view1 name="view1"></my-view1>
<my-view2 name="view2"></my-view2>
<my-view3 name="view3"></my-view3>
<my-view404 name="view404"></my-view404>
</iron-pages>
</app-header-layout>
</app-drawer-layout>
and a view with a button which if pressed should change the route, like this:
<dom-module id="my-view1">
<template>
<paper-button raised on-tap="changeRoute">Change route</paper-button>
</template>
<script>
Polymer({
is: 'my-view1',
changeRoute() {
// How to change route here?
}
});
</script>
</dom-module>
how can I change the route programmatically, in the event handler above? I tried changing window.location without success (I'm inside Electron, not sure if it's part of the problem).
From the Polymer docs, about changing routes:
Updating the route. The route object is read-write, so you can use
two-way data binding or this.set to update the route. Both the route
and routeData objects can be manipulated this way. For example:
this.set('route.path', '/search/');
Or:
this.set('routeData.user', 'mary');
but this is correct (and works) if done inside the main component (the one that defines the routes), while it doesn't work if called inside a view. Another way to put this question maybe is: how can I access the main router object from an inner component?
There's a few ways to do this, I may have gone overboard but I started to get into it so here goes...
Based on the answer you gave it looks like you just want to "go back". If that's the case you could just use
history.back();
Or if you want to move to a new path then a more robust, including more SEO friendly approach (I believe) is to use something like:
<a href="/newpath" tabindex="-1">
<paper-button>Change Route</paper-button>
</a>
Or the answer by #Carlos works too, but the app-route "philosophy" seems to be more about allowing a decentralized routing approach & so passing an event up to let another component handle the route feels kind of like it's going against the grain. Although this is not necessarily the case since a component can be quite simple, maybe just a button, icon, etc, so you would probably want routing handled by a parent element. In this case though it looks like your component is a full view so I'd lean more towards it being a case where it might handle the routing itself.
I'd also say something similar to the above for #tony19's answer as well.
I think the biggest drawback of the approach in your own answer is that you're digging in the guts of another element so you've tightly coupled to it. That one is definitely not recommended.
Oh & one more way to change the route:
window.history.pushState({}, null, '/new_path');
window.dispatchEvent(new CustomEvent('location-changed'));
This is given here - https://github.com/PolymerElements/app-route#integrating-with-other-routing-code But this is really more for working with other routing code or special situations & would be pretty hacky in your case.
Uhm. Turned out it was not so difficult:
document.querySelector("main-window").set('route.path', "/");
where main-window is the container component that defines the routes (first source file).
Does anyone have a better/more general approach to this problem?
I had the same issue and solved it like by adding the routing-elements to the views as well:
<app-location route="{{route}}"></app-location>
<app-route route="{{route}}" pattern="/app/:view" data="{{routeData}}" tail="{{subRoute}}"></app-route>
<app-route route="{{subRoute}}" pattern="/:id" data="{{idData}}" active="{{onDetailPage}}"></app-route>
then you can change the route from within the element like this:
this.set("route.path", "/app/foo");
Your 'view1' could fire a custom event that would trigger a route change in your app.
This would follow the observer pattern recommended in: Thinking in Polymer (The Polymer Summit 2015)
Note: the answer to this question may also help you.
You can easily do so with the path property of the app-location's element:
changeRoute() {
var $router = this.shadowRoot.querySelector("app-location");
$router.path = "/view1";
}
Here the element's documentation app-location:
Setting the path property will fire up the path-changed event... This will begin the events' cascade to the iron-pages element and switch the view.

How to loop through nested Firebase keys in Polymer

So, I have a Polymer project that is saving to Firebase. My data looks like this:
What I'm trying to do is loop through the teams property in the data. Considering Polymer only loops through Arrays right now, this is proving to be pretty challenging (since Firebase preferably returns data as objects). So far, I can loop through the keys and get the teams, but can't get into the teams to loop through it. Here is my code:
<template repeat="{{key in keys}}">
<div class="tile">
<paper-shadow z="2" class="card" animated>
<div id="header" class="header"></div>
<div class="content">
<p>{{data[key]}}</p>
<span>{{team.club}}</span>
</div>
<footer horizontal layout>
<paper-button id="teamview" on-tap="{{viewTeam}}" flex>VIEW</paper-button>
<span flex></span>
<paper-button id="teamDelete" on-tap="{{deleteTeam}}">DELETE</paper-button>
<paper-button id="teamEdit" on-tap="{{editTeam}}">EDIT</paper-button>
</footer>
</paper-shadow>
</div>
</template>
I feel like I've tried almost every scenerio. Every time I try and loop one more level with repeat="{{team in key}}" it breaks. Seeing if maybe some one else has a better perspective on this? Thanks in advance!
You need to bind the data, id and keys like this I think.
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../firebase-element/firebase-element.html">
<!--
Element providing solution to displaying value from firebase.
##### Example
<firebase-repeat-element></firebase-repeat-element>
#element fb-repeat-element
#blurb Element providing solution to displaying value from firebase.
#status alpha
#homepage http://basicelements.github.io/firebase-repeat-element
-->
<polymer-element name="firebase-repeat-element" attributes="firebaseName">
<template>
<firebase-element location="https://{{firebaseName}}.firebaseio.com/members/{{id}}" data="{{data}}" keys="{{keys}}"></firebase-element>
<template repeat="{{id in keys}}">
<h2>{{data[id]['name']}}</h2>
<img src="{{data[id]['image']}}">
</template>
</template>
<script>
Polymer({
});
</script>
</polymer-element>
Be free to fork it #https://gist.github.com/ed2ba06d5a16d11381d9.git
So, the way I found out needs more in-depth explaining regarding Polymer and Firebase. First off, when you enter your location attribute in the firebase-element, whenever you are sourcing your data and keys, it is specifically with that location - not your data as a whole.
Therefore, the problem I was having was rooted in my location attribute. Once I changed my location to specify the exact nested level I wanted to push and source data from - all was well. The end code looked like this:
<firebase-element id="fbase"
location="https://volleyball-app.firebaseio.com/{{user.uid}}/userTeams"
data="{{userTeams}}" keys="{{keys}}" dataReady="{{userReady}}">
</firebase-element>
<template repeat="{{key in keys}}">
<div class="content">
<p>{{userTeams[key]['team']}}</p>
<span>{{userTeams[key]['club']}}</span>
</div>
</template>
What this is doing is repeating through all the keys in the <firebase-element id="fbase"
location="https://volleyball-app.firebaseio.com/{{user.uid}}/userTeams location (nothing more). From there I'm grabbing the team property in each key.

Polymer : How to procedurally add nodes to a polymer-element's Light Dom, from inside the element?

I am trying to define a custom polymer-element (groups-manager), which takes a JSON configuration, and produces its contents (a set of custom polymer-elements called generic-group):
<polymer-element name="groups-manager" attributes="display type config">
<template>
<content id="content" select="generic-group">
<template repeat="{{c in config}}">
<generic-group id="{{c.groupName}}" config="{{c}}" type="{{type}}">
</generic-group>
</template>
</content>
</template>
<script>
Polymer('groups-manager',{})
</script>
</polymer-element>
Although contained in a <content> node, my <generic-group> nodes become parts of the Shadow DOM.
Is there a way to have the <generic-group> nodes that are produced by the template, exposed in the light DOM?
You can add them with Polymer.dom(this).appendChild().
The nodes you add will be put in the light dom, and the <content> tag will display them into the element.