Polymer dom-repeat sub property changes child to host wiring - javascript

I have a host element binding array of object to child element which has a paper-input to edit its properties. I don't see the value change on input reflected in the host div element. Even though on debug I can see that the host object has the latest edited name. What should I do to get this automatically wired ?
<!-- Host element -->
<dom-module id="host-item">
<template>
<div>
<div>[[selectedEmployee.name]]</div>
<template is="dom-repeat" items="[[employees]]" as="employee">
<item-edit item="[[employee]]"></item-edit>
</template>
</div>
</template>
<script>
Polymer({
is: 'host-item',
properties: {
selectedEmployee: {
type: Object
},
employees: {
type: Array,
value = [ { name: 'Name 1'}, { name: 'Name 2'}, { name: 'Name 2'}]
}
},
ready: function() {
this.selectedEmployee = this.employees[0];
}
});
</script>
</dom-module>
<!-- Child element -->
<dom-module id="item-edit">
<template>
<paper-input id="input" value="{{item.name}}" error-message="Invalid name!"></paper-input>
</template>
<script>
Polymer({
is: 'item-edit',
properties: {
item: {
type: Object
}
}
});
</script>
</dom-module>

Use {{employee}} for 2 way binding. [[...]] is for one way only.
Use notify: true on property definition.
Child element should be defined before the parent.
Here is the working example Plunk, and similar Plunk
<item-edit item="{{employee}}"></item-edit>
...
employee: {
type: Object,
notify: true,
value: function () { return {name: 'Test' }; }
}
Update:
Now "employees" data is in form of an array of objects.
Check out this question for working with arrays:
Polymer, issue with binding array to paper slider value
Plunk
Docs: Binding to array items

Related

How to attach nuxeo-tree component to Polymer v1 app

I want to add the <nuxeo-tree> component to my Polymer v1 app, but I'm seeing an error in the console. This is the code I've tried:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/nuxeo-ui-elements/nuxeo-tree/nuxeo-tree.html">
<link rel="import" href="./myVerySpecialLib-import.html">
<dom-module id="my-app">
<template>
tree:<br/>
<nuxeo-tree data="[ title: 'root', children: [ { title: 'a', children: [] }, { title: 'b', children: [ {title: 'x'}, {title: 'y'} ] } ]]]" controller="[[controller]">
<template>
<template is="dom-if" if="[[!opened]]">
<iron-icon icon="hardware:keyboard-arrow-right" toggle></iron-icon>
</template>
<template is="dom-if" if="[[opened]]">
<iron-icon icon="hardware:keyboard-arrow-down" toggle></iron-icon>
</template>
<span select>My title is: [[item.title]]</span>
<span>Am I a leaf? [[isLeaf]]</span>
</template>
</nuxeo-tree>
</template>
<script>
Polymer({
is: 'my-app',
properties: {
data: {
type: String,
value: "[ title: 'root', children: [{ title: 'a',children: []},{title: 'b',children: [{title: 'x'},{title: 'y'}]}]]",
},
opened: {
type: Boolean,
value: true,
},
},
controller: {
// How to get children of a node. Returns a promise.
getChildren: function(node) {
return Promise.resolve(node.children);
},
// Logics you may want to have to control if a node is a leaf.
isLeaf: function(node) {
return node.children.length === 0;
}
},
});
</script>
</dom-module>
And the myVerySpecialLib-import.html file:
controller = {
// How to get children of a node. Returns a promise.
getChildren: function(node) {
return Promise.resolve(node.children);
},
// Logics you may want to have to control if a node is a leaf.
isLeaf: function(node) {
return node.children.length === 0;
}
};
This is the console error:
TypeError: this.controller.isLeaf is not a function
I tried to add the JSON data as a property and also directly into the data field, but neither had a positive effect. How do I fix this?
The myVerySpecialLib-import.html seems to contain a global variable declaration, but that doesn't really help you because <nuxeo-tree> expects controller on the container element (not in a global variable).
Also, your data binding for <nuxeo-tree>.controller is malformed (it's missing a ] at the end):
<nuxeo-tree controller="[[controller]">
And controller probably should be declared as a property if you're binding it. It's currently declared outside the properties object.
// DON'T DO THIS
/*
properties: {...},
controller: {...}
*/
// DO THIS
properties: {
controller: {...}
}
I recommend setting this.controller in the ready() callback of the parent element of <nuxeo-tree> (where this is the container). You could also set <nuxeo-tree>.data via a binding to simplify your HTML template, and that property could be initialized in ready() as well.
ready: function() {
this.data = /* insert data object here */;
this.controller = /* insert controller object here */;
}
demo

Access element's property from dom-repeat

I'm using a dom-repeat template in my element, and I want to use the property typeElement of that element (<custom-element>) in a new element (<media-element>) from within dom-repeat:
<dom-module id="custom-element">
<template>
<template is="dom-repeat" items="{{array}}" as="file">
<media-element some-prop="{{typeElement}}" file="{{file}}"></media-element>
</template>
</template>
<script>
Polymer({
is: 'custom-element',
properties: {
typeElement: Number,
array: {
type: Array,
value: function() { return[]; }
}
}
});
</script>
</dom-module>
How can I do that?
I'm not sure what you mean by "use the property", so I'm making assumptions below.
Assuming you want to set <media-element>.someProp to the value of <custom-element>.typeElement, then your data binding is correct. Whenever the value of typeElement changes, someProp will be set to the same value. You could access the value in a method of <media-element> with this.someProp. Example:
<dom-module id="media-element">
<template>...</template>
<script>
Polymer({
is: 'media-element',
properties: {
someProp: Number
},
foo: function() {
console.log(this.someProp);
}
});
</script>
</dom-module>
Assuming you also want the changes to <media-element>.someProp to update <custom-element>.typeElement, then you'd need to set notify: true on the property declaration of someProp:
// media-element
properties: {
someProp: {
type: Number,
notify: true // <-- only needed for upward notifications (two-way data binding and observers)
}
}
HTMLImports.whenReady(() => {
Polymer({
is: 'custom-element',
properties: {
typeElement: {
type: Number,
value: 100,
observer: '_typeElementChanged'
}
},
_typeElementChanged: function(typeElement) {
console.log('new typeElement', typeElement);
}
});
Polymer({
is: 'media-element',
properties: {
someProp: {
type: Number,
notify: true
}
},
_logSomeProp: function() {
console.log('someProp', this.someProp);
},
_incrementSomeProp: function() {
this.someProp++;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.8.1/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<custom-element></custom-element>
<dom-module id="custom-element">
<template>
<media-element some-prop="{{typeElement}}"></media-element>
</template>
</dom-module>
<dom-module id="media-element">
<template>
<button on-tap="_logSomeProp">Log someProp</button>
<button on-tap="_incrementSomeProp">Incremeent someProp</button>
</template>
</dom-module>
</body>
codepen
I recommend reading Polymer Data Binding.
because settings property like:
typeElement: Number won't work. You have to define it like you defined array property. so:
typeElement: {
type: Number,
value: Number,
}
i don't know what value you want to have. After this it should work
Ok i figured myself, the problem isn't here. The problem was on the child element (<media-element>). So i'll post a new question te resolve the problem:
how do the properties of an element been initialized before the element creation? Or, how to trigger the dom-if recalling my condition function after propertie change/init?
My code:
<dom-module id="media-element">
<template>
<template is="dom-if" if="[[isType(0)]]">
....
</template>
</template>
<script>
Polymer({
is: 'media-element',
properties: {
myType: {
type: Number,
value: 0
}
},
isType: function(t){return (this.myType===t);}
});
</script>
</dom-module>
UPDATE: I sent my question and receive a goode answer: here

How to Troubleshooting Binding

I found a cool project (RoboJS), and I forked it: Forked Repo. My plan was to try to add a nice front end with Polymer 1.0 and learn a little in the process.
What I am having trouble with is getting the binding to show in my component. I've built a really simple "robot" component to show the status of the robot during the game.
To start, all I want to do is to show the name in the title, but it comes out blank. Here's the component:
<dom-module id="robojs-robot-status">
<template>
<div>Robot Name <span>[[robot]]</span><span>{{test}}</span></div>
</template>
</dom-module>
<script>
Polymer({
is: "robojs-robot-status",
properties: {
robot: {
type: String,
value: "testing"
},
test: {
type: String,
value: "testing2"
}
},
ready: function() {
},
init: function() {
console.log(this.robot);
console.log(this.test);
}
});
</script>
On the parent component, I set the robot attribute:
Here's the attribute:
<link rel="import" href="robojs-robot-status.html">
<robojs-robot-status robot="{{robot}}"></robojs-robot-status>
And, I have a script that, for now, sets the value on the ready event:
Polymer({
is: "robojs-arena",
properties: {
robot: {
type: String,
value: "hello"
}
},
ready: function() {
this.games = window.roboJS.games;
console.log(this.games);
//this.robot = {name: "hello"};
this.robot = "hello";
},
init: function() {
console.log("******* init *******");
console.log(this.robot);
document.querySelector("robojs-robot-status").init();
},
pause: function() {
window.roboJS.pause();
},
start: function() {
console.log(window.roboJS);
window.roboJS.resume();
}
});
[[robot]] is blank. {{test}} binds to "testing2".
Using {{robot}} or [[robot]] doesn't make a difference. So, that doesn't have an impact.
If I remove, the "robot" attribute in the parent component, the value works. It shows "testing". So, it is binding, but not with the actual value.
Beyond figuring out what I am doing wrong in this instance, is there a good way to troubleshoot? I am having similar issues in other places in the app.
If this were Angular + jQuery, I would do something like this:
$('robotjs-robot-status').scope().$eval("robot")
I could type that into the developer console in Chrome and see what it said and troubleshoot. I could also use the Batarang extension in Chrome.
With Polymer, I am not sure where to start. Any help/ideas?
If the parent snippet is posted here exactly as it appears in the code, then it's probably to blame. The
<link rel="import" href="robojs-robot-status.html">
should be outside , like
<dom-module id="robojs-robot">
<link rel="import" href="robojs-robot-status.html">
<template>
<robojs-robot-status robotname="{{robotname}}"></robojs-robot-status>
</template>
<script>
Polymer({
is: "robojs-robot",
ready: function() {
console.log('setting to Dilly');
this.robotname = "Dilly";
},
properties: {
robotname: {
type: String,
value: "hello"
}
},
});
</script>
</dom-module>
and then if status is
<dom-module id="robojs-robot-status">
<template>
<div>Robot Name <span>[[robotname]]</span></div>
</template>
<script>
Polymer({
is: "robojs-robot-status",
properties: {
robotname: {
type: String,
value: "testing",
observer: '_robotnameChanged'
}
},
_robotnameChanged: function(newValue, oldValue) {
console.log('_robotnameChanged: newValue='+newValue+' oldValue='+oldValue)
}
});
</script>
</dom-module>
everything works for me.
PS: properties seem to be not really needed here as binding is unidirectional.

Change in data bound property is not propagating upwards

I have nested custom elements which interact with each other.
<dom-module id="dom-element">
<template>
<custom-element bindingTest="{{found}}"></custom-element>
</template>
<script>
Polymer({
is: "dom-element",
properties: {
found: {
type:String,
value:"4"
}
},
ready: function() {
this.addEventListener("found-changed", this.foundChanged);
},
foundChanged:function(e){
console.log("found changed");
}
});
</script>
</dom-module>
Child custom element:
<dom-module id="custom-element">
<template>
<button on-click="toParent">Send to parent</button>
</template>
<script>
Polymer({
is: "custom-element",
properties:{
bindingTest: {
type: String,
notify: true
}
},
toParent: function () {
this.bindingTest = "change of binding test"
}
});
</script>
</dom-module>
If I understood correctly, this.bindingTest = "change of binding test" should notify parent custom element and "found" parameter should become equal to "change of binding test" String, that is, toParent function has to be called, but it isn't being called for some reason. How do I notify parent when bindingTest changes?
EDIT:
The problem here is that you are accessing the bindingTest property of the child element incorrectly. From the documentation
In order to configure camel-case properties of elements using attributes, dash- case should be used in the attribute name.
So you will need to change your custom-element tag to be:
<custom-element binding-test="{{found}}"></custom-element>
I've set up a plunker here for you to look at.

Polymer change notification and two-way binding syntax

I'm trying to figure out how to use the 2-way notifications in polymer described here
But while this explains that there is some mechanism for notifying if an object on a child element has changed:
When a sub-property of a property configured with type: Object
changes, an element fires a non-bubbling -changed DOM event
with a detail.path value indicating the path on the object that
changed.
But it doesn't give any clue (that I can sort out) how the syntax would look to script some behaviour based on this change.
For example if I have a parent:
<dom-module id="parent-element">
<template>
<child-element obj="{{myObj}}"></child-element>
</template>
<script>
Polymer({
is: "parent-element",
myObj: {
type: Object
},
parentClick: function(){
this.myName = "Parent";
},
myObjChanged: function(){ //How to handle this event?
console.log("I have no idea what I'm doing")
}
});
</script>
and I have a child element:
<dom-module id="child-element">
<template>
<span on-click="childClick">Click me</span>
</template>
<script>
Polymer({
is: 'child-element',
properties: {
obj: {
type: Object,
notify: true
}
},
ready: function(){
this.obj = {foo: "bar"}
},
childClick: function(){
this.obj.foo = "baz"
}
});
</script>
When the child is clicked, I expect some event to fire and get picked up by the parent, but I have no clue how to script for that event at the parent. What am I missing?
I recently had this problem and did this by firing my own event. You can see the documentation for that here (please note that this documentation is for 0.5 but I have done this with version 1.0). In your child element you could change your childClick function to fire the custom event as follows:
childClick: function(){
this.obj.foo = "baz"
this.fire("child-element-click"); // this can be anything you want...
}
and then in your parent element you want to add a listener to the child-element:
<dom-module id="parent-element">
<template>
<child-element obj="{{myObj}}" on-child-element-click="myObjChanged"></child-element>
</template>
<script>
Polymer({
is: "parent-element",
myObj: {
type: Object
},
parentClick: function () {
this.myName = "Parent";
},
myObjChanged: function () {
console.log("I have no idea what I'm doing")
}
});
</script>

Categories