How to structure a Polymer SPA? - polymer

I'm new to Polymer 1.0.
I get the whole "everything is an element" philosophy but, to what extent?
How to structure all theses elements together?
TL;DR: Is there an equivalent to the main() function with Polymer and WebComponents in general ? If so , where should it be and what should it do ?
I usually look at examples, demos and projects to see how it should work, but because Polymer is so new (especially v1.0), I have a hard time finding non-trivial examples.
Long version
I get how to use Polymer to create elements.
But I'm hitting a roadbloack when I want to interface them. What structure shoud I use to make components talk between them.
Coming from an Angular background I have a relatively precise view of what should go where.
For example: the data is contained within scopes which are accessible from controllers, directives and html elements and you can use services to pull data from different part of the app.
In Polymer I don't get where are the boundaries of the data.
Not in the component itself but outside of it, where it lives and if another component can access it.
A simple drawing could help me a lot. There is no such thing explained in the polymer docs, probably because it's broader than Polymer.
To give you insights on my problem this is what I came across:
I set up a SPA based on Polymer Starter Kit
I wanted to wire it to firebase with the firebase-element
I created my own element which itself use <firebase-auth>:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/firebase-element/firebase-auth.html">
<dom-module id="my-login">
<template>
<firebase-auth id="firebaseLogin"
user="{{user}}"
location="https://my-project.firebaseio.com"
provider="facebook"
on-error="_errorHandler"
on-login="_loginHandler"></firebase-auth>
<button on-tap="login">Login with facebook</button>
<button on-tap="logout">Logout</button>
</template>
</dom-module>
<script>
Polymer({
is: 'my-login',
properties: {
successRedirect: String
},
_errorHandler: function(e){
console.log(e.detail);
},
_loginHandler: function(e){
if(this.successRedirect){
// How can I tell pagejs to redirect me ?
app.route = this.successRedirect;
}
}
});
</script>
Basically How do I tell pagejs (my routing library) to redirect the app to a page after a successful login ? My pagejs config lives in it's own routing.html file and I don't understand how to piece together all of this.
I hope someone will be abe to understand this broad question and hopefully help me.
Thanks

Short answer: Event Listeners. Place an event listener on your router. Have the login handler fire the event on the router. The router will pick up that event and then redirect.
...
_loginHandler: function(e){
if(this.successRedirect){
// How can I tell pagejs to redirect me ?
var router = document.querySelector('router');
router.dispatchEvent(new Event('redirect', {redirectURL: this.successRedirect});
app.route = this.successRedirect;
}
}
...
Then in your router:
connectedCallback() {
this.addEventListener('redirect', function(event) {
var url = event.redirectURL;
// Redirect using the router's API.
});
}

Related

Sortable like vue.js component (without npm / webpack) or use jQuery plugin within vue area

I have a ASP.NET Core application which renders tables on the serverside, some are quite complex.
I used to use sorttable: Make all your tables sortable for make the tables sortable; now as I have included vue.js (2.0, without npm / webpack), the jquery plugin obviously does no longer work properly.
Now, before i transition fully over to 100% clientside table rendering - which I want to avoid for now, if its possible, cause its complex - is there something similar to add sorting to a rendered html with vue or is that concept that old and no longer viable in vue.js and other modern frameworks?
So, questions are:
How to make sorttable work in vue.js are (without npm / webpack)
Or how to add something like that to a already server rendered html with vue?
Looking forward and regards, Peter
Okay, got it. That was a journey :-)
Sorttable script: https://www.kryogenix.org/code/browser/sorttable/
The script:
Vue.component('date-table', {
template: '<div><slot></slot></div>',
props: ['innerHtml'],
mounted: function () {
var self = this;
sorttable.makeSortable(this.$el.firstChild);
},
beforeDestroy: function () {
}
});
The markup:
<date-table v-once>
HERE IS MY ORDINARY HTML WHICH SHOULD BE SORTED BY
"sortable.makeSortable(...."
</data-table>

Polymer Starter Kit adding function equivalent to ready for the main page

I've got a newly created polymer starter kit, 1.3 to be exact.
I would like to created a method like the one below, on the index.html page
ready: function() {
firebase.auth().onAuthStateChanged(function(user) {
if(user) {
// console.log('user is logged in');
} else {
// console.log('user is not logged in');
}
});
}
On the main page, how can I do it? I would like to instantiate a Polymer element for the application template, but the document stays that should be avoided. If anyone could explain why that would be awesome.
Thanks in advance for any feedback!
On the main page, how can I do it?
In index.html, you could use a WebComponentsReady handler to perform any action that's dependent on all elements being registered:
window.addEventListener('WebComponentsReady', function(e) {
// imports are loaded and elements have been registered
...
});
I would like to instantiate a Polymer element for the application template, but the document stays that should be avoided.
Where did you see that? While version 1.3.0 of the Polymer Starter Kit uses an auto-binding template in index.html instead of an app element, I don't see why you'd try to avoid an app element. In fact, there's growing evidence that an app element is recommended:
Version 2 of Polymer Starter Kit replaces the auto-binding template with a custom app element
The dev guide describes the Basic Application Template (generated by polymer-cli) as a custom element:
The application template is the most basic starting point for any app built with Polymer. It starts with a single bare-bones custom element that can serve as the root of your application, from which you can build in any direction with maximum flexibility.

How to use x3dom with polymer?

I have a polymer site where I'd like to use the x3dom library to view an external x3d file, and simply be able to rotate the loaded 3D scene 360 degrees.
Has anyone successfully used x3dom with polymer? I don't see any reason why this wouldn't work. Please assume I have all the polymer stuff correct (which I have) and have loaded the prerequisite x3dom.js script and x3dom.css in the head of the page too. This is just a stripped down code snippet to show key bits:
<x3d width='350px' height='400px'>
<scene>
<inline nameSpaceName="myNS" mapDEFToID="true" url="{{_x3dPath}}"></inline>
</scene>
</x3d>
<script>
Polymer({
properties: {
_x3dPath: {type: String},
},
ready: function() {
this._x3dPath = "/someDynamicPath/threeDfile.x3d";
}
});
</script>
Any suggestions?
You need to create a custom element that serves as a wrapper for the 3rd party library. (e.g x3dom-wrapper.html ).
Inside that file, you must add the script reference to x3dom .js
<script type="text/javascript" src='../bower_components/x3dom/x3dom.js'></script>
Then, you need to import the wrapper custom element like any other polymer component. That way you ensure the x3dom dependency will be available when you need it.
Edit:
Wrapping the library is still a good practice but not the cause of your problem. I did a quick test and found that you must call x3dom.reload() on your "ready" polymer event handler. That way x3dom system reloads properly

Browser crashes on angularjs $http.get

I'm working on a template system with angularJs, and I am including template files to my view with ng-include from an external js-file.
When I run my index.html the browser, it crashes after a long while. It seems as if the page continues to reload over and over, and my CPU monitor goes through the roof.
What on Earth can be wrong? I am fairly new at angularjs, so if anyone could point me in the right direction, I'd be very thankful.
index.html
<!DOCTYPE html>
<html lang="en" data-ng-app="angularTemplate" data-ng-controller="templateCtrl">
<head>
<meta charset="UTF-8">
<title>{{ pageName + " - " + siteName }}</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
</head>
<body>
<header data-ng-include="includeHeader()"></header>
<script type="text/javascript" src="./assets/js/templateCtrl.js"></script>
</body>
</html>
JS
var app = angular.module('angularTemplate', []);
app.controller('templateCtrl', function($scope, $http) {
$scope.includeHeader = function(){
$http.get("./assets/php/mysql.php?action=getSiteSetup")
.success(function(response){
$scope.siteName = response.siteName;
$scope.siteTagline = response.siteTagline;
});
return "./assets/templates/header.html";
}
});
the mysql.php?action=getSiteSetup is a simple sql call which returns
{"siteName":"AngularJS Template","siteTagline":"Just another template...","siteBaseURL":"http:\/\/localhost\/angularjs_template\/"}
This issue exists because of the following two implementations:
<header data-ng-include="includeHeader()">
and
$scope.includeHeader = function(){
$http.get("./assets/php/mysql.php?action=getSiteSetup")
...
}
You need to understand how AngularJS's scope works to detect change in scope. See here
Basically AngularJS will repeatedly rerun the expression given and compare it to its previous value to determine if its value has changed, if it did, then it will do whatever it needs upon scope variable change. Therefore includeHeader() here is rerun repeatedly every time AngularJS wants to check if things have changed. However inside your includeHeader, it triggers a network call every time. To make things into an infinite loop, one of the triggers for AngularJS to decide if it needs to check for scope change is upon the completion of $http. There's your infinite loop.
In short, do not use any expression for scope value check if it's process intensive and vice versa. includeHeader is NOT where you should put initialization code. Simply trigger it in controller.
For routing between pages you should use Angular-Route or ui.router.
It is described here with examples: https://docs.angularjs.org/tutorial/step_07
This uses ng-view directive witch allows you to inject content based on called url from your web-app.
Here you have ng-view docs: https://docs.angularjs.org/api/ngRoute/directive/ngView
I really recomend you to read it and solve it this way. This completly remodel your work but will let you work easier and faster in future. And also it will make much easier making a tests to it.
Also please past your header.html code for improving this answer.
Remove the Ajax request from inside the function that data-ng-include directive binds to in the controller so that the code looks like the following
var app = angular.module('angularTemplate', []);
app.controller('templateCtrl', function($scope, $http) {
$http.get("./assets/php/mysql.php?action=getSiteSetup")
.success(function(response){
$scope.siteName = response.siteName;
$scope.siteTagline = response.siteTagline;
});
$scope.includeHeader = function(){
return "./assets/templates/header.html";
}
});
Note:
You don't necessary need to bind the ng-include directive to a
function on the scope when the template is not loaded dynamically
based on user interactions

Working with Polymer and requirejs

In an attempt to create polymer elements that use requirejs modules I ran into a blocking issue. I understand that polymer is not designed to work with requirejs, but for the time being It is my only option.
Searching for answers I found two solutions:
Don't use requirejs and make your modules compatible with HTML imports.
Put Polymer() call inside the requirejs callback as described here
Since I have to use require, at least for the time being, I went with the solution no.2. However, it turns out the solution causes asynchronous delays of element registration and incorrect data binding prior to Polymer upgrading the element.
Digging deeper into this issue, I started hacking undocumented Polymer internals with an intention to stop Polymer entirely until requirejs does its thing. Here is what I came up with:
Polymer.require = function(tag, deps, func) {
var stopper = {}
Polymer.queue.wait(stopper);
require(deps, function() {
delete stopper.__queue;
Polymer.queue.check();
Polymer(tag, func.apply(this, arguments));
});
};
I know this is terribly wrong. Is there a better solution?
I found that if I embed the call to require within the Polymer script I avoid this issue.
<link rel="import" href="../polymer/polymer.html"/>
<script src="../requirejs/require.js"></script>
<script src="../something/something.js"></script>
<polymer-element name="some-component">
<template>...</template>
<script>
(function() {
Polymer('some-component', {
someMethod: function () {
require(['something'], function (Something) {
var something = new Something();
...
}
}
)();
</script>
</polymer-element>
So there's this solution from Scott Miles but I find it a bit simplistic and inflexible as it relies on:
<script> tags to be executed in order, therefore ruling out:
async script tags
xhr based script loading
polymer getting loaded from a <script> tag, therefore:
layout.html and associated css won't be loaded
any future call to polymer.html won't be deduped
If you want more control over your bootstrapping logic you will need to enforce some amount of synchronisation between your components (which is what both requirejs and polymer are competing to do) before those are fully loaded.
The previous example is a more declarative (read polymer) way of doing things but falls short of fine grained tuning. I've started working on a repository to show how you can fully customise your load ordering, by using a more imperative approach where requirejs is given priority to orchestrate the rest of the bootstrapping.
At the time of writing, this extra control comes at the price of worse performance as the various scripts can't be loaded in parallel but I'm still working on optimising this.