Polymer: Updating Templatized template when parent changes - javascript

Following this question, I'm trying to get full two-way binding between parent and template when using Polymer.Templatizer. I'm not sure what the problem is _forwardParentProp and _forwardParentPath are never called when a property changes on parent. I think it's the reason why my templates don't update.
Here's my code (also available here). As you can see dom-if binds both ways and the two templates (one created dynamically other declared inside custom element) only update parent but aren't themselves updated.
Polymer({
is: "my-app",
behaviors: [Polymer.Templatizer],
properties: {
global: {
value: {
text: 'i'
},
notify: true
}
},
ready: function() {
this.count = 0;
// this ensures that global.text is updated when text changes
this._instanceProps = {
text: true
};
this.addHeadline(this.$.template);
this.addHeadline(this._dynamicTemplate());
},
addHeadline: function(template) {
this.templatize(template);
var clone = this.stamp({
text: this.global
});
// Append to my-app
Polymer.dom(this.root).appendChild(clone.root);
},
_dynamicTemplate: function() {
var template = document.createElement("template");
var templateRoot = document.createElement('div');
templateRoot.innerHTML = '<h1>{{text.text}}</h1><input type="text" value="{{text.text::input}}" />';
// you cannot just set innerHTML in <template>
template.content.appendChild(templateRoot);
return template;
},
_forwardInstanceProp: function(inst, p, val) {
console.log('_forwardInstanceProp');
},
_forwardInstancePath: function(inst, p, val) {
// notify parent's correct path when text.text changes
this.set(p.replace(/^text/, 'global'), val);
console.log('_forwardInstancePath');
},
_forwardParentProp: function(prop, value) {
console.log('_forwardParentProp');
},
_forwardParentPath: function(prop, value) {
console.log('_forwardParentPath');
}
});
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link rel="import" href="paper-input/paper-input.html"/>
</head>
<body>
<dom-module id="my-app">
<template>
<paper-input label="global.text" value="{{global.text}}"></paper-input>
<p>global.text: <{{global.text}}></p>
<template id="template">
<h1>{{text.text}}</h1><input type="text" value="{{text.text::input}}" />
</template>
<template if="true" is="dom-if">
<div>
dom-if:
<input type="text" value="{{global.text::input}}" />
</div>
</template>
</template>
</dom-module>
<my-app></my-app>
</body>
</html>

Related

parse json in a vue-treeselect component

In a https://vue-treeselect.js.org/ component, how can I load json? My code is not working
<html>
<head>
<!-- include Vue 2.x -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#^2"></script>
<!-- include vue-treeselect & its styles. you can change the version tag to better suit your needs. -->
<script src="https://cdn.jsdelivr.net/npm/#riophae/vue-treeselect#^0.4.0/dist/vue-treeselect.umd.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#riophae/vue-treeselect#^0.4.0/dist/vue-treeselect.min.css">
</head>
<body>
<div id="app">
<treeselect v-model="value" :multiple="true" :options="options" />
</div>
</body>
<script>
// register the component
Vue.component('treeselect', VueTreeselect.Treeselect)
var tree = new Vue({
el: '#app',
data: {
// define the default value
value: null,
// define options
options: function () {
return JSON.parse(this.json);
}
},
})
$.getJSON("my.json", function (json) {
tree.json = json;
});
</script>
</html>
Put the following code inside the mounted hook :
let vm = this;
$.getJSON("https://jsonplaceholder.typicode.com/users", function(json) {
vm.options = json;
});
you should update the options property directly in the callback of ajax request.
<html>
<head>
<!-- include Vue 2.x -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- include vue-treeselect & its styles. you can change the version tag to better suit your needs. -->
<script src="https://cdn.jsdelivr.net/npm/#riophae/vue-treeselect#^0.4.0/dist/vue-treeselect.umd.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#riophae/vue-treeselect#^0.4.0/dist/vue-treeselect.min.css">
</head>
<body>
<div id="app">
<treeselect v-model="value" :multiple="true" :options="options" />
</div>
</body>
<script>
// register the component
Vue.component('treeselect', VueTreeselect.Treeselect)
var tree = new Vue({
el: '#app',
data: {
// define the default value
value: null,
// define options
options: []
},
mounted() {
let vm = this;
$.getJSON("https://jsonplaceholder.typicode.com/users", function(json) {
vm.options = json;
});
}
})
</script>
</html>

Data binding between sibling custom elements

There are two child custom elements inside parent custom element. I don't know if it's even possible, but what I am trying to achieve is when "value" changes, and "prop" changes as a result of binding, I need "feat1" to change accordingly equating to the value of "prop".
parent-element:
<dom-module id="parent-element">
<template>
<first-child prop={{value}}></first-child>
<second-child feat1={{prop}}></second-child>
In parent-element
<h1>{{value}}</h1>
</template>
<script>
Polymer({
is: "parent-element",
properties: {
value: {
type: String
}
},
valueChanged:function(){
console.log("value changed");
}
});
</script>
</dom-module>
first-child:
<dom-module id="first-child">
<template>
<p>first element.</p>
<h2>{{prop}}</h2>
</template>
<script>
Polymer({
is: "first-child",
properties:{
prop:{
type:String,
notify:true
}
},
ready:function(){
this.prop = "property"; //this is just example, in reality it gets data from elsewhere
}
});
</script>
</dom-module>
second-child:
<dom-module id="second-child">
<template>
<p>Second element.</p>
<h2>{{feat1}}</h2>
</template>
<script>
Polymer({
is: "second-child",
properties:{
feat1:{
type:String,
notify:true,
value:"initial value"
}
},
ready:function(){
this.addEventListener("feat1-changed",this.myAct);
},
myAct:function(){
console.log("feat1-changed ",this.feat1);
}
});
</script>
</dom-module>
Here is the plunk
Sure you can. Some issues with your code:
From the parent element, bind prop in <first-child> and feat1 in <second-child> to the same value.
Use observer defined in the properties object to listen for changes.
Set up <first-child> to be a one-way bind from child to parent.
Set up <second-child> to be a one-way bind from parent to child.
Putting it all together:
<parent-element>:
<dom-module id="parent-element">
<template>
<first-child prop={{value}}></first-child>
<second-child feat1=[[value]]></second-child>
<div>parent-element: <b><span>[[value]]</span></b></div>
<div id="log"></div>
</template>
<script>
Polymer({
is: "parent-element",
properties: {
value: {
type: String,
observer: "valueChanged"
}
},
valueChanged: function (n, o) {
var el = document.createElement("div");
el.innerHTML = "value changed in parent from "+o+" to "+n;
Polymer.dom(this.$.log).appendChild(el);
}
});
</script>
</dom-module>
<first-child>:
<template>
<div>first-child: <b><span>{{prop}}</span></b>
<button on-tap="btnTapped">change 'prop' inside first-child</button>
</div>
</template>
<script>
Polymer({
is: "first-child",
properties:{
prop:{
type:String,
notify:true,
value: "first_child_default"
}
},
btnTapped: function () {
this.prop = Math.random().toString(36).substring(7);
}
});
</script>
</dom-module>
<second-child>:
<dom-module id="second-child">
<template>
<div>second-child: <b><span>{{feat1}}</span></b></div>
<div id="log"></div>
</template>
<script>
Polymer({
is: "second-child",
properties:{
feat1:{
type:String,
value:"second_child_default", // you should never see this since default value is passed in from parent
observer: "feat1Changed"
}
},
feat1Changed: function(n, o) {
var el = document.createElement("div");
el.innerHTML = "feat1 changed in second-child from "+o+" to "+n;
Polymer.dom(this.$.log).appendChild(el);
}
});
</script>
</dom-module>
Plunker: http://plnkr.co/edit/pONoTxdCDn6jj5axoap1?p=preview
Let me know if this is the correct behaviour you are trying to achieve.

Polymer data binding -

Can someone tell me where i'm going wrong? I have an element system-menu that queries for data using iron-ajax then i'm supposed to bind that data to my template.
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-menu-behavior/iron-menubar-behavior.html">
<dom-module id="system-menu">
<template>
<iron-ajax
auto
url="{{url}}"
params='{"ajax":"true"}'
handle-as="json"
on-response="dataLoaded"
debounce-duration="300">
</iron-ajax>
<div class="content"><content></content></div>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'system-menu',
behaviors: [
Polymer.IronMenubarBehavior
],
ready: function() {
},
properties: {
data: {
type: Object,
reflectToAttribute: true,
notify: true
}
},
attached: function () {
this.data = [];
},
dataLoaded: function (data) {
this.data = data.detail.response;
}
});
})();
</script>
`
This is how I use the element,
<link rel="import" href="/themes/_components/custom_components/system-menu/system-menu.html">
<system-menu class="list" url="www.some.site/widget/system-menu-widget/-menuRequest/">
<template is="dom-repeat" items='{{data}}'>
<li>{{item.label}}<li>
</template>
</system-menu>`
For some reason, the {{data}} isnt binding anything. It just shows {{data}} in the chrome inspect element console
To access the data loaded from the system-menu outside of the system-menu element, you need to add it as an attribute:
<system-menu class="list" url="www.some.site/widget/system-menu-widget/-menuRequest/" data="{{data}}">

Using polymer ajax response

I have the following polymer element which I have created:
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" on-response="handleResponse"></iron-ajax>
<template is="dom-repeater" items="{{todos}}">
<span>hello</span>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app",
created: function () {
this.todos = [];
},
handleResponse: function (data) {
this.todos = data.detail.response;
}
});
</script>
I am calling this inside my index.html by doing:
<task-list-app></task-list-app>
I am expecting that for every object returned in the todo array, a <span> will be printed. However, when I run the app, I get the following output in the console:
Uncaught TypeError: Cannot read property 'todos' of undefined
in polymer.html line 1001
I am not sure what is happening here and how to reference the data received back from the ajax response.
First of all second template that you are using to loop through your data should be a "dom-repeat" and not a "dom-repeater". Secondly you can directly bind the response of iron-ajax to your looping template. Like this,
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" last-response="{{ajaxResponse}}"></iron-ajax>
<template is="dom-repeat" items="[[ajaxResponse.todos]]">
<span>{{item.todoItem}}</span>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app"
});
</script>
So you are basically binding value of last-response property to your looping template directly.
After banging my head on the wall for a few hours I have managed to solve this. I have created my own element called ajax-service that has a public property called todos which is an Array. In this element, I use the iron-ajax element to do the ajax call.
When the ajax is complete, a function is called and the response is set on the todos property. I have also set the keys reflectToAttribute and notify to true. This means the todos property's value is reflected back to the attribute on the host node and that it is available for two-way binding (see here for more information).
My task-list-app element is as follows:
<link rel="import" href="ajax-service.html">
<link rel="import" href="task-item.html">
<link rel="import" href="tiny-badge.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<ajax-service todos="{{todos}}"></ajax-service>
<template is="dom-repeat" items="{{todos}}">
<task-item task="{{item}}"></task-item>
</template>
<div>
<tiny-badge>[[todos.length]]</tiny-badge> total tasks
</div>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app"
});
</script>
and my ajax-service element:
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="ajax-service">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" on-response="tasksLoaded"></iron-ajax>
</template>
</dom-module>
<script>
Polymer({
is: "ajax-service",
properties: {
todos: {
type: Array,
reflectToAttribute: true,
notify: true
}
},
attached: function () {
this.todos = [];
},
tasksLoaded: function (data) {
this.todos = data.detail.response;
}
});
</script>
Doing it this way means I am able to edit the data in the on-response function before setting it on the element.
You need to define property before using it in scripts:
<script>
Polymer({
is: "task-list-app",
properties: {
todos: {
type: Array,
notify: true
}
},
handleResponse: function (data) {
this.todos = data.detail.response;
}
});
</script>

Bind a value between a parent and a child element where child element is created using javascript

Using Polymer, does anyone know how to bind a value between a parent and a child element?
Below is my attempt however it doesn't work.
Note: child-component needs to be created using JavaScript.
<dom-module id="host-component">
<template>
<div>{{sharedValue}}</div>
<div id="childComponentContainer">
<!-- CHILD-COMPONENT GETS CREATED HERE -->
</div>
</template>
<script>
Polymer({is:'host-component',
properties:{
sharedValue:{
type: String,
notify:true,
observer: 'sharedValueChanged'
}
},
attached: function(){
var newElement = document.createElement('child-component');
Polymer.dom(this.$.childComponentContainer).appendChild(newElement);
},
sharedValueChanged:function(d){
console.log(d, ", said the child");
}
});
</script>
</dom-module>
<dom-module id="child-component">
<template>
<input is="iron-input" bind-value="{{sharedValue}}" />
</template>
<script>
Polymer({is:'child-component',
properties:{
sharedValue:{
type: String,
notify:true,
reflectToAttribute:true,
}
},
});
</script>
</dom-module>
Thanks :)
After re-reading Polymer's documentation (many times) I found a section about how Two-way data-binding events work where by on each change Polymer fires a DOM event. From this I came up with the work around below.
You'll notice this version also has two way binding so the host can change the child's value and the child can change the host's value!
<dom-module id="host-component">
<template>
<div>Host: <span>{{sharedValue}}</span></div>
<div>Host: <span><input is="iron-input" bind-value="{{sharedValue}}" /></span></div>
<div id="childComponentContainer">
<!-- CHILD-COMPONENT GETS CREATED HERE -->
</div>
</template>
<script>
Polymer({is:'host-component',
properties:{
sharedValue:{
type: String,
notify:true,
value: "Unchanged",
observer: 'sharedValueChanged'
}
},
attached: function(){
this.$.childComponent = document.createElement('child-component');
var host = this;
//Listens to the child's sharedValue. When changed it will update host's sharedValue.
this.$.childComponent.addEventListener("shared-value-changed", function(e){
host.sharedValue = this.sharedValue;
});
Polymer.dom(this.$.childComponentContainer).appendChild(this.$.childComponent);
},
//Observes the hosts's sharedValue. When changed it will update child's sharedValue.
sharedValueChanged: function(value){
if (this.$.childComponent) {
this.$.childComponent.sharedValue = value;
}
}
});
</script>
</dom-module>
<dom-module id="child-component">
<template>
<div>Child: <span>{{sharedValue}}</span></div>
<div>Child: <span><input is="iron-input" bind-value="{{sharedValue}}" /></span></div>
</template>
<script>
Polymer({is:'child-component',
properties:{
sharedValue:{
type: String,
notify:true,
value:"Unchanged",
reflectToAttribute:true,
}
}
});
</script>
</dom-module>

Categories

Resources