Dynamically change localization in Polymer app - polymer

I have a Polymer app with many templates, in which I would like to manage localization. I use app-localize-behavior for that. I found a nice way of managing localization from a single file. The solution proposed there uses behaviors, and requires little additions in each template, with a unique json data file.
My problem is that I would like to be able to change the language dynamically, and probably store it in a app-localstorage-document element, which I manage from another file. How can I set this language property from outside this file?
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/app-localize-behavior/app-localize-behavior.html">
<script>
MyLocalizeImpl = {
properties: {
language: {
value: 'fr'
}
},
attached: function() {
this.loadResources(this.resolveUrl('../locales.json'));
},
};
MyLocalize = [MyLocalizeImpl, Polymer.AppLocalizeBehavior];
</script>

OK I adapted the app-localize-behavior demo code. The template which changes the language has to override the language property, including “notify: true”:
properties: {
language: {
value: 'en',
type: String,
notify: true
},
},
Then I bound all other elements' language property with this:
<element language="{{language}}"></element>
Templates which use localize must of course link the localize script:
<link rel="import" href="localize.html">
… and add the behavior in the script part:
Polymer({
is: "my-page",
behaviors: [
MyLocalize
]
Bonus: how to use a different json file for every language: https://abendigo.github.io/2016/08/03/lazyloading_localization.html

Related

How to pass vuejs html template as slot and access from data or method

I have a component like this:
<my-dropzone value:"....">
<h1>upload</h1>
<p>your files</p>
</my-dropzone>
and my vuejs component is:
<template>
<div>
<div class="dropzone"></div>
<slot></slot>
</div>
</template>
<script>
....
export default {
props:{
....
},
components: {
vueDropzone: vue2Dropzone
},
data() {
return {
arrayFiles: [],
dropzoneOptions: {
....
dictDefaultMessage: // <------ I want to assign slot data here
,
....
},
mounted() {
.....
},
methods: {
.....
},
computed: {
.....
}
}
</script>
Now I want to get slot html data and assign to some local variable of component and also use in method. Is it possible? Is there any better solution to pass html to data variable
You can use Vue.js vm.$slots to access the slot children as HTML nodes. It is the only recommended way. Official Vue.js docs really explain it well.
You can use render() function for your components instead of template. Slots are designed to be used effectively with a render function. Without render function, using vm.$slots is not really that useful.
Long explanation:
Vue slots are not designed to work the way you need. Imagine slots as a rendering area. Also, a slot doesn't really contain HTML as a string. It is already passed from virtual-dom and rendered as HTMLElements on screen. By the time code is processed, html as a string has transformed into a render function.
Alternately, If you need to really access the underlying HTML nodes, then consider using plain DOM API like querySelector(), etc. As far as the $refs is concerned, Content within the slot is owned by the parent component and thus you cannot use it.

Polymer, binding to array items not working

In this example (Plunk) there is bind between property and array item.
firstName should change from 'John' to 'Test' on click, but it's not happening.
How to make the property to change on item update?
<script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
<!--
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.4/lib/paper-input/paper-input.html" />
-->
<dom-module id="first-el">
<template>
<br> firstName should change on click:<br><br>
firstName: <span>{{employees.employees.0.firstName}}</span>
<br>
<br>
<button on-tap="tap_change_firstName_1">change firstName to: Test</button>
</template>
<script>
(function() {
'use strict';
Polymer({
is: "first-el",
properties: {
employees: {
type: Object,
notify: true,
},
},
//domReady:
attached: function() {
this.async(function() {
console.log('first: domReady:');
this.employees = {
"employees": [{
"firstName": "John",
"lastName": "Doe"
}]
};
});
},
tap_change_firstName_1: function() {
console.log('First: tap_change_firstName_1');
//update array item property
this.set('employees.employees.0.firstName', 'Test');
console.log('New value:');
console.log(this.employees.employees[0].firstName);
//here the value is cnahged but that does not reflect in the DOM
},
});
})();
</script>
</dom-module>
<!-- use the element -->
<first-el></first-el>
Update:
array-selector (simple example) element can be used for this task too.
The set() convenience function just wraps the property setter and the notifyPath call in one. When your data is an array like that, I believe notifyPath is expecting the upper-level array itself and not just a single slice of it.
One way to resolve it (there are probably a few) would be to make that notifyPath call yourself after setting the property directly.
this.employees.employees[0].firstName = 'Test';
this.notifyPath('employees.employees', this.employees.employees);
See new Plunker.
Upper solution works only for one change, it does not work for multiple 'name' updates, example: Plunk
Correct solution, from the docs: in this Plunk
Explicit bindings to array items by index isn’t supported
<div>{{arrayItem(myArray.*, 0, 'name')}}</div>
...
// first argument is the change record for the array change,
// change.base is the array specified in the binding
arrayItem: function(change, index, path) {
// this.get(path, root) returns a value for a path
// relative to a root object.
return this.get(path, change.base[index]);
},
...
// change a subproperty
this.set('myArray.1.name', rnd_firstName);

Polymer 1.0 'array-style' path accessors, alternative to bracket notation in expressions

The Polymer 1.0 documentation states:
The path syntax doesn’t support array-style accessors (such as
users[0].name). However, you can include indexes directly in the path
(users.0.name).
How would one get around this in setting the path dynamically, and obtain the same behavior as the following example using Polymer 0.5? This is specifically in the context of generating forms for a model defined by an Object.
<template repeat="{{row in fieldset.rows}}">
<div layout horizontal flex>
<template repeat="{{field in row}}" flex>
<paper-field field="{{model.fields[field]}}" value="{{obj[field]}}">
</paper-field>
</template>
</div>
</template>
edit:
Per https://github.com/Polymer/polymer/issues/1504:
No near-term plans to support this. Polymer 0.5 had a complex expression parser used for bindings that we have eliminated for simplicity and performance. There are alternate patterns you can use today to achieve similar results that just require you to be more explicit.
What the alternate pattern would be to achieve two way data binding remains unclear.
Yes, it is true that Polymer 1.0 no longer supports myObject[key] in binding expressions. However, in your particular use-case, there are ways to sidestep this problem.
One-way data-binding
It is fairly simple to overcome this limitation when it comes to one-way data-binding. Simply use a computed property that accepts both the object and the key in question:
<my-element value="[[getValue(obj, key)]]"></my-element>
getValue: function(obj, key) {
return obj[key];
}
Two-way data-binding
In the case of two-way data-binding, it is still possible to create a functional alternative to the binding expression {{obj[key]}} in Polymer 1.0. However, it will require taking into consideration the particular use-case in which you are hoping to implement the binding.
Taking into account the example in your question, it seems that you are doing work with some sort of table or fieldset. For the purposes of the example here, I will use a slightly different, yet very similar structure.
Let's assume that we have a fieldset object, and that this object is structured as such:
{
"fields": [
{ "name": "Name", "prop": "name" },
{ "name": "E-mail", "prop": "email" },
{ "name": "Phone #", "prop": "phone" }
],
"rows": [
{
"name": "John Doe",
"email": "jdoe#example.com",
"phone": "(555) 555-1032"
},
{
"name": "Allison Dougherty",
"email": "polymer.rox.1337#example.com",
"phone": "(555) 555-2983"
},
{
"name": "Mike \"the\" Pike",
"email": "verypunny#example.com",
"phone": "(555) 555-7148"
}
]
}
If we want to create some sort of table-like output which represents this object, we can use two nested repeating templates: the first to iterate through the different rows, and the second to iterate through the different fields. This would be simple using the one-way data-binding alternative above.
However, achieving two-way data-binding is very different in this case. How is it do-able?
In order to understand how to come up with a solution, it is important to break down this structure in a way that will help us figure out what observation flow we should implement.
It becomes simple when you visualize the fieldset object like this:
fieldset
--> rows (0, 1, ...)
--> row
--> fields (name, email, phone)
--> value
And becomes even simpler when you factor in the workflow of your element/application.
In this case, we will be building a simple grid editor:
+---+------+--------+-------+
| | Name | E-mail | Phone |
+---+------+--------+-------+
| 0 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
| 1 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
| 2 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
There are two basic ways that data will flow in this app, triggered by different interactions.
Pre-populating the field value: This is easy; we can easily accomplish this with the nested templates mentioned earlier.
User changes a field's value: If we look at the application design, we can infer that, in order to handle this interaction, whatever element we use as an input control, it must have some sort of way of knowing:
The row it will affect
The field it represents
Therefore, first let's create a repeating template which will use a new custom element that we will call basic-field:
<div class="layout horizontal flex">
<div>-</div>
<template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
<div class="flex">[[item.name]]</div>
</template>
</div>
<template is="dom-repeat" items="{{fieldset.rows}}" as="row" index-as="rowIndex">
<div class="layout horizontal flex">
<div>[[rowIndex]]</div>
<template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
<basic-field class="flex" field="[[item]]" row="{{row}}"></basic-field>
</template>
</div>
</template>
(In the snippet above, you'll notice that I've also added a separate repeating template to generate the column headers, and added a column for the row index - this has no influence on our binding mechanism.)
Now that we've done this, let's create the basic-field element itself:
<dom-module>
<template>
<input value="{{value}}">
</template>
</dom-module>
<script>
Polymer({
is: 'basic-field',
properties: {
row: {
type: Object,
notify: true
},
field: {
type: Object
},
value: {
type: String
}
}
});
</script>
We have no need to modify the element's field from within the element itself, so the field property does not have notify: true. However, we will be modifying the contents of the row, so we do have notify: true on the row property.
However, this element is not yet complete: In its current state, it doesn't do any of the work of setting or getting its value. As discussed before, the value is dependent on the row and the field. Let's add a multi-property observer to the element's prototype that will watch for when both of these requirements have been met, and populate the value property from the dataset:
observers: [
'_dataChanged(row.*, field)'
],
_dataChanged: function(rowData, field) {
if (rowData && field) {
var value = rowData.base[field.prop];
if (this.value !== value) {
this.value = value;
}
}
}
There are several parts to this observer that make it work:
row.* - If we had just specified row, the observer would only be triggered if we set row to an entirely different row, thus updating the reference. row.* instead means that we are watching over the contents of row, as well as row itself.
rowData.base[field.prop] - When using obj.* syntax in an observer, this tells Polymer that we are using path observation. Thus, instead of returning just the object itself, this means that rowData will return an object with three properties:
path - The path to the change, in our case this could be row.name, row.phone, etc.
value - The value that was set at the given path
base - The base object, which in this case would be the row itself.
This code, however, only takes care of the first flow - populating the elements with data from the fieldset. To handle the other flow, user input, we need a way to catch when the user has inputted data, and to then update the data in the row.
First, let's modify the binding on the <input> element to {{value::input}}:
<input value="{{value::input}}">
Polymer does not fully automate binding to native elements as it doesn't add its own abstractions to them. With just {{value}}, Polymer doesn't know when to update the binding. {{value::input}} tells Polymer that it should update the binding on the native element's input event, which is fired whenever the value attribute of the <input> element is changed.
Now let's add an observer for the value property:
value: {
type: String,
observer: '_valueChanged'
}
...
_valueChanged: function(value) {
if (this.row && this.field && this.row[this.field] !== value) {
this.set('row.' + this.field.prop, value);
}
}
Notice that we aren't using this.row[this.field.prop] = value;. If we did, Polymer would not be aware of our change, as it does not do path observation automatically (for the reasons described earlier). The change would still be made, but any element outside of basic-field that may be observing the fieldset object would not be notified. Using this.set gives Polymer a tap on the shoulder that we are changing a property inside of row, which allows it to then trigger all the proper observers down the chain.
Altogether, the basic-field element should now look something like this (I've added some basic styling to fit the <input> element to the basic-field element):
<link rel="import" href="components/polymer/polymer.html">
<dom-module id="basic-field">
<style>
input {
width: 100%;
}
</style>
<template>
<input value="{{value::input}}">
</template>
</dom-module>
<script>
Polymer({
is: 'basic-field',
properties: {
row: {
type: Object,
notify: true
},
field: {
type: Object
},
value: {
type: String,
observer: '_valueChanged'
}
},
observers: [
'_dataChanged(row.*, field)'
],
_dataChanged: function(rowData, field) {
if (rowData && field) {
var value = rowData.base[field.prop];
if (this.value !== value) {
this.value = value;
}
}
},
_valueChanged: function(value) {
if (this.row && this.field && this.row[this.field] !== value) {
this.set('row.' + this.field.prop, value);
}
}
});
</script>
Let's also go ahead and take the templates we've made before and throw them into a custom element as well. We'll call it the fieldset-editor:
<link rel="import" href="components/polymer/polymer.html">
<link rel="import" href="components/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="basic-field.html">
<dom-module id="fieldset-editor">
<style>
div, basic-field {
padding: 4px;
}
</style>
<template>
<div class="layout horizontal flex">
<div>-</div>
<template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
<div class="flex">[[item.name]]</div>
</template>
</div>
<template is="dom-repeat" items="{{fieldset.rows}}" as="row" index-as="rowIndex">
<div class="layout horizontal flex">
<div>[[rowIndex]]</div>
<template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
<basic-field class="flex" field="[[item]]" row="{{row}}"></basic-field>
</template>
</div>
</template>
<pre>[[_previewFieldset(fieldset.*)]]</pre>
</template>
</dom-module>
<script>
Polymer({
is: 'fieldset-editor',
properties: {
fieldset: {
type: Object,
notify: true
}
},
_previewFieldset: function(fieldsetData) {
if (fieldsetData) {
return JSON.stringify(fieldsetData.base, null, 2);
}
return '';
}
});
</script>
You'll notice we're using the same path observation syntax as we did inside of basic-field to observe changes to fieldset. Using the _previewFieldset computed property, we will generate a JSON preview of the fieldset whenever any change is made to it, and display it below the data entry grid.
And, using the editor is now very simple - we can instantiate it just by using:
<fieldset-editor fieldset="{{fieldset}}"></fieldset-editor>
And there you have it! We have accomplished the equivalent of a two-way binding using bracket-notation accessors using Polymer 1.0.
If you'd like to play with this setup in real-time, I have it uploaded on Plunker.
You can make a computed binding. https://www.polymer-project.org/1.0/docs/migration.html#computed-bindings
<paper-field field="{{_computeArrayValue(model.fields, field)}}" value="{{_computeArrayValue(obj, field}}"></paper-field>
<script>
Polymer({
...
_computeArrayValue: function(array, index) {
return array[index];
},
...
});
</script>
As an aside, you also need to update your repeat to dom-repeat https://www.polymer-project.org/1.0/docs/devguide/templates.html#dom-repeat
Edit: Here is my ugly solution to the 2-way binding. The idea is that you have a calculated variable get the initial value and then update this variable upon updates with an observer.
<!-- Create a Polymer module that takes the index and wraps the paper field-->
<paper-field field="{{fieldArrayValue}}" value="{{objArrayValue}}"></paper-field>
<script>
Polymer({
...
properties: {
fields: { //model.fields
type: Array,
notify: true
},
obj: {
type: Array,
notify: true
},
arrayIndex: {
type: Number,
notify: true
},
fieldArrayValue: {
type: String,
computed: '_computeInitialValue(fields, number)'
},
objArrayValue: {
type: String,
computed: '_computeInitialValue(obj, number)'
}
},
_computeInitialValue: function(array, index) {
return array[index];
},
observers: [
'fieldsChanged(fields.*, arrayIndex)',
'objChanged(fields.*, arrayIndex)'
],
fieldsChanged: function (valueData, key) {
this.set('fieldArrayValue', this.fields[this.arrayIndex]);
},
objChanged: function (valueData, key) {
this.set('objArrayValue', this.obj[this.arrayIndex]);
},
...
});
</script>
Edit 2: Updated the code in Edit 1 to reflect the obeserver changes pointed out by Vartan Simonian

nodejs ractive and consolidate html comment issue

I am using consolidate.js and ractive templates, which by default strip out comments. I am trying to add some html logic in the page that does something like this:
<![if IE]>one thing<![endif]>
<![if !IE]>another thing<![endif]>
However ractive removes the comments. I know you can allow comments in ractive, by setting stripComments false, but I don't know how to do that through consolidate.js.
The second argument of consolidate.ractive() is passed directly to Ractive, so you can do this:
cons.ractive(path, { stripComments: false, user: 'Tobi' }, function () {});
In case you want to separate your data from Ractive config, you can do this instead:
cons.ractive(path, { stripComments: false, data: { user: 'Tobi' } }, function () {});
If the object doesn't have the data property, the whole object will be used as data as well.

dhtmlxtree XML refers to non existing parent

I am working with dhtmlxtree and are having issues getting it loading data from json fed to it by an external script. I am getting the error below. Any ideas? Thanks!
ErrorType: LoadXML
Error: Incorrect JSON
My HTML looks like this:
<body>
<div id="treeBox" style="width: 200px; height: 200px; overflow: auto;"></div>
<script>
jQuery( document ).ready(function() {
tree = new dhtmlXTreeObject("treeBox", "100%", "100%", 0);
tree.setSkin('dhx_skyblue');
tree.setImagePath("../sma-js/dhtmlxtree/img/csh_bluebooks/");
tree.setXMLAutoLoading("sma-php/loadcustomers.php");
tree.setDataMode("json");
//load first level of tree;
tree.loadJSON("sma-php/loadcustomers.php?id=PNR0000000001");
});
When I manually run the script, the JSON returned looks like this:
[{"phys_addr_state":"ACT","phys_addr_postcode":"2167","install_address":"2 Eade Street - Radiative, John"},{"phys_addr_state":"NSW","phys_addr_postcode":"2263","install_address":"69 The Corso - Flare, Steve"}, {"phys_addr_state":"NSW","phys_addr_postcode":"2112","install_address":"17 Price Street - Solar, Anita"},{"phys_addr_state":"QLD","phys_addr_postcode":"4001","install_address":"71 Eagle Street - Corona, Linda"},{"phys_addr_state":"VIC","phys_addr_postcode":"3053","install_address":"15 Lygon Street - Photon, Marco"}]
The issue I notice is the structure / hierarchy of my JSON. It contains an item per record, whereby other examples I see in dhtml site show a "hierarchical" JSON record. My encode on the server is done by:
echo json_encode($data);
The other dhtml example of JSON (for comparison) is:
{ id: 0, item: [{ id: 1, text: "1111" }, { id: 2, text: "222222", item: [{ id: "21", text: "child" }] }, { id: 3, text: "3333" }]}
Is there a different way to encode the sql records whereby all records of same state will be grouped together under the same entry per "id: 2 item above" per example above?
My header includes the following:
<script src="../sma-js/d3.v3/d3.v3.min.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="../sma-js/dhtmlxtree/css/dhtmlxtree.css">
<script src="../sma-js/dhtmlxtree/js/dhtmlxcommon.js"></script>
<script src="../sma-js/dhtmlxtree/js/dhtmlxtree.js"></script>
<script src="../sma-js/dhtmlxtree/js/dhtmlxtree_json.js"></script>
You should use specific JSON format, the same as in dhtmlx demos. However, it would be easier and faster to use dhtmlxConnector (free to use with dhtmlx components) instead of custom scripts. The Connector helps to generate XML output, and all you need is to set up table fields for tree XML.