Binding to viewmodel via a context? - polymer

I am looking at using Polymer for a new project I am working on. I'm lucky enough to be able to target the latest version of Chrome only.
One of the reasons I am looking at Polymer is that I understand that is uses the new Object.observe for data-binding. (I think I am right in saying that, please correct me if I am not!)
One thing I would like to achieve is to be able to run my tests against my view models rather than using a gui testing tool.
...but as far as I can see, Polymer is setup for the web components to actually be the view models. Am I right in saying that? Now I've not looked into Polymer testing, but I expect that would mean having to have a DOM to test...
Now, I can add a "context" attribute to every web component and bind the JS view model to that and have all the bindings as {{context.prop}}, {{context.prop1}}, etc...
Is this going against any polymer principle - have I got completely the wrong idea?
Example element:
<polymer-element name="my-element" attributes="context">
<template>
<textarea value="{{context.prop}}"></textarea>
</template>
<script>Polymer({});</script>
</polymer-element>
Element use:
<my-element id="ele"></my-element>
<script>
var model = { prop: 'initial value' }
// wait for DOM to load - should really use some sort of "ready" event here...
setTimeout(
function() {
var ele = document.querySelector('#ele');
ele.context = model;
}, 3000);
Object.observe(model, function() { console.log('changed'); });
</script>

Related

Why do I need addEventListener for my proto-element but not my dom-element in Polymer?

So I'm learning polymer through the Firefox browser and I was running into the Polymer not defined error. When researching SO I saw I have to wrap my script in the addEventListener function.
<link rel="import" href="https://polygit2.appspot.com/components/polymer/polymer.html">
<script>
// register a new element called proto-element
//This example needs this eventlistener
addEventListener('WebComponentsReady', function() {
Polymer({
is: "proto-element",
// add a callback to the element's prototype
ready: function() {
this.textContent = "I'm a proto-element. Check out my prototype!"
}
});
})
</script>
Now when I run it in the browser it works fine. When moving on to the DOM element, it seems my script no longer needs to be wrapped in addEventListener().
This works:
<dom-module id="dom-element">
<template>
<p>I'm a DOM element. This is my local DOM!</p>
</template>
<script>
//for some reason this example does not
//addEventListener('WebComponentsReady', function() {
Polymer({
is: "dom-element"
});
//})
</script>
</dom-module>
So my question, why does my proto-element need the addEventListener function and my dom-module does not?
This behavior can be explained with polyfills for web components, which are (at this point) still necessary for Firefox. In particular, HTML imports need to be polyfilled, which is accomplished by the webcomponents-lite.js script (missing in your examples).
In your first example, you don't actually have to wait for the WebComponentsReady event to make a call to the Polymer() function, but for the HTMLImportsLoaded event. Because, it's at that point that the browser knows what to do with the link tag:
<link rel="import" href="https://polygit2.appspot.com/components/polymer/polymer.html">. It then imports the Polymer library and you can make the call to Polymer(). Check out this JSBin showing how this works.
In your second example, the browser at first doesn't know what to do with the <dom-module>, because this is an element from the Polymer library and not part of the HTML specification. So, <dom-module> is only available after the HTMLImports event has fired. Once the polyfills kick in, the <dom-module> is upgraded, the template is rendered and the script inside it is executed. In other words there's no need to wait for this event explicitly, because the upgrade can only happen after the event has fired (or if it's supported natively, as in Chrome).
This is my interpretation, but maybe the Polymer team can clarify it further...

Passing Data Through Pages with Vue

I receive data in a function and this function needs to load a new page. I was doing this with Jquery using the method .load(), But my vue method gets an error saying that can not find the element in the other page.I believe that could be a better approach, but I do not know how I can pass data through a vue component
static foo(a,b){
$('#maindiv').load("/newPage");
new Vue({
el: '#newPageElem',
data: {
vueVar1:a
vueVar2:b
},
});
}
How can I do that? I believe that using Jquery is not the best way to achieve this, but I dont know how to change routes with vue and passing parameter between them.
Thanks.
You can take a look at vue-router here: http://router.vuejs.org/en/essentials/getting-started.html - it allows you to handle all the client-side routes.
For making http calls to receive data from server, you can use vue-resource (https://github.com/vuejs/vue-resource)
It is better to avoid mixing jQuery with Vue, as Vue is responsible for rendering the DOM. If you make any DOM changes with jQuery, it will get over-written by Vue in the next DOM update. So you will end up spending a lot of time in debugging.
Also, you cannot instantiate a Vue app inside a function, as seen in your code example, unless you call the function on page load. Ideally, the first few lines of script should start the Vue app.

Custom HTML Dialog in Electron

How (or is it even possible) to use custom HTML dialogs in Electron? I know that Electron provides certain dialogs (showMessageDialog, showErrorDialog) but these do not seem to allow custom HTML.
I do not wish to use native HTML dialogs (dialog) tag as it does not 'blend in' with the user interface.
Any help would be much appreciated. Thanks!
You can create a BrowserWindow that's modal and, if you like, frameless. See http://electron.atom.io/docs/api/browser-window/.
Yes.
On your parent you should have:
const { remote } = require('electron');
const { BrowserWindow } = require('electron').remote;
and then:
let child = new BrowserWindow({
parent: remote.getCurrentWindow(),
modal: true,
width:300, height:300,
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true
}
});
child.loadFile('myCustomModal.html');
On myCustomModal.html remeber to include a way to close the modal!
like:
<button id="cancel-btn">Cancel</button>
<script>
const remote = require('electron').remote;
document.getElementById("cancel-btn").addEventListener("click", function (e) {
var window = remote.getCurrentWindow();
window.close();
});
</script>
As Marc Rochkind said in a previous answer, you can use modal windows in Electron.
However, I have found a small bug with modal windows which causes the parent window to flicker for a very short duration when its .show() function is called. After quite some time on Google, I found an open issue on GitHub about the same problem. After reading the comment section in the issue, and stumbling across some code snippets, I shared a hacky solution in the issue's comment section.
It does take some work to set up, but once it's done, it's really easy to port to other child windows.

Message between multi-level elements (Polymer)

What's the best way to change data between different levels (in DOM) elements?
For example, I would like to save an REST service base URL in a variable on index.html (<template is="auto-binding"), so I could access this value from anywhere in the project, regardless of the hierarchy.
I guess <core-storage> would be an option, is it? What are the other options?
Thanks
Edit:
Just found my answer here: https://stackoverflow.com/a/24876397/2750721
As Trevor Dixon explained in an earlier answer you can use globals. Divshot does this for a lot of their apps (even before it was officially in the docs) but they use their own custom element for globals.
So far their is no silver bullet answer though. My experience so far is it depends on the situation as far as what is the best practice. Here are some generalizations I usually think about...
Global browser storage (<core-storage>, <local-forage>, pouchDB, IndexedDB, cookies, etc.)
Usage: this is great if you have data that you need to share with multiple elements, managing offline data, and for maintaining state/data across apps that utilize traditional browser page navigation
Considerations: can have limited space on client (localStorage has 5mb base as an example), usually synchronous API's (not for all though like Mozilla's localForage), limited to a single browser, can get complicated fast in maintaining state
Custom Events
Usage: great for to sharing small amounts of data and signaling across the application. Events are clean & bubble up throughout the DOM, ignoring shadow boundaries
Considerations: not good for large amounts of data, not optimal for Virtual DOM approaches, reliable & easy to use, limited to window & document lifecycle (refresh will not maintain data state)
Globals
Usage: Great for sharing small & large amounts of data across the app in JavaScript objects
Considerations: Maintenance & performance could issues if not tended to early on, Polymer has a built in mechanism for globals so it is easy to use, bound to the document & window lifecycle
Generally, no you'll use a combination of these strategies with a restful API that data is ultimately persisted to and synchronized with. this is not always the case but it is typical in modern JavaScript applications on the client.
I'd do something like what is suggested in the Polymer docs under "Supporting global variables". https://www.polymer-project.org/0.5/docs/polymer/polymer.html#global. Make an element that uses a function closure to share state between all instances of the element, then create an instance of that element any time you need access to the shared data.
Copied straight from the docs:
<polymer-element name="app-globals">
<script>
(function() {
// these variables are shared by all instances of app-globals
var firstName = 'John';
var lastName = 'Smith';
Polymer({
ready: function() {
// copy global values into instance properties
this.firstName = firstName;
this.lastName = lastName;
}
});
})();
</script>
</polymer-element>
Then use the element as you would any other. You can
access its properties using Polymer data binding or plain JavaScript:
<polymer-element name="my-component">
<template>
<app-globals id="globals"></app-globals>
<div id="firstname">{{$.globals.firstName}}</div>
<div id="lastname">{{$.globals.lastName}}</div>
</template>
<script>
Polymer({
ready: function() {
console.log('Last name: ' + this.$.globals.lastName);
}
});
</script>
</polymer-element>
You can use custom events to alert changes between multi-level element, e.g.
<component-1>
Polymer({
ready: function() {
this.addEventListener("data-change", function(e) {
this.data = e.detail.data;
});
},
</component-1>
<component-deep>
Polymer({
dataChanged: function() {
this.fire("data-change", {data: this.data});
}
});
</component-deep>

Polymer: How many observers is my element maintaining?

Given an element instance, how do I see how many observers it's maintaining?
I'm trying to figure if either of these implementations is more expensive.
Polymer({
fooChanged: function() {
this.bar = foo.baz;
}
}
Polymer({
computed: {
'bar': 'foo.baz'
}
}
I suspect they're equivalent (except that one is watching foo, the other is watching the path) but I want to be sure.
Internally, Polymer uses uses Node.bind() to bind the property changes.
It will use PathObserver to watch 'foo.baz' and of course, it's slower to watch a computed object like that versus a single attribute.
https://www.polymer-project.org/docs/polymer/node_bind.html
You can check all the event listeners in the chrome dev tools. Select the ID in the console and on the right you have the "event listeners" tab.
See:
Using Chrome, how to find who's binded to an event?
But I doubt this will show you anything performance wise. I think it is better to use the CPU profile in the profiles tab in the chrome dev tools.