Event Handling between parent and child page in polymer 1.0 - javascript

I have a parent polymer element called parent-page and a child element called child-page.
parent-page calls child page and passes an array with it. for e.g, in parent page:
<child-page items={{itemsArray}}></child-page>
Now, on the basis of certain activity child page fires an event with a new array.
eg, in child page:
this.fire('eventPerformed', newArray);
This array is being listened by the parent page and received with expected values.
Now, I want to pass that new array to the child page such that the child-page is rendered according to the new array.
How to achieve it?
Edit: my child page looks like this.
<dom-module id="child-page">
<style>
</style>
<template>
<template is="dom-repeat" items="{{itemArray}}" as="fooditem">
<div class="horizontal layout">
<div>{{fooditem.quantity}}</div>
<div>{{fooditem.name}}</div>
</div>
</template>
<paper-button on-click"changeArray"> ChangeArray</paper-button>
</template>
<script type="text/javascript">
Polymer({
is:'child-page',
properties:{
itemArray:Array
},
changeArray:function(){
this.itemArray=<<Some new Array>>
this.fire('eventPerformed',newArray);
}
});
</script>
</dom-module>
Is there any way I can call the template repeat with the new array in the same child page? Or do I have to fire an event to the parent-page and call the child page again? How to achieve it either way?

The child-page re-renders its template is="dom-repeat" items="[[itemArray]]" automatically whenever its itemArray property was updated.
Just add notify: true to the itemArray property in child-page to enable two-way-binding with the parent-page element. Then the parent-page is also notified whenever the item-array of child-page has changed (see the Polymer documentation on this topic).
Here is a small complete example:
<dom-module id="child-page">
<template>
<template is="dom-repeat" items="[[itemArray]]">
<div>[[item]]</div>
</template>
<div on-click="_changeArray">Change</div>
</template>
<script>
Polymer({
is: 'child-page',
properties: {
itemArray: {type: Array, notify: true}
},
_changeArray: function() {
this.itemArray = [4,5,6,7,8,9];
}
})
</script>
</dom-module>
<dom-module id="parent-page">
<template>
<span>Item count: </span><span>[[itemArray.length]]</span>
<child-page item-array="{{itemArray}}"></child-page>
</template>
<script>
Polymer({
is: 'parent-page',
properties: {
itemArray: {type: Array, value: [1,2,3]}
}
})
</script>
</dom-module>
<parent-page></parent-page>
Btw. note my usage of {{...}} and [[...]]. Use curly braces for two-way bindings and square brackets for one-way bindings.

Related

Vue JS emit event to child component from parent component not triggering

I'm working in a Nuxt JS project. I have several components, and one of my components called ComplianceOptoutRawText includes a child component called ComplianceOptoutViaExternalService, and it's this child component where I'm using a v-on to listen for an $emit event from the ComplianceOptoutRawText so I can toggle a variable, but I'm not seeing it at all in my console logs and need to know what I'm doing wrong and need to change.
Here's my markup with everything that's appropriate:
ComplianceOptoutRawText
<template>
<div>
<div>
<div class="tw-hidden md:tw-block tw-font-bold tw-mb-4">
<ComplianceOptoutViaExternalService>
<template #trigger>
<button #click="$emit('shown-optout-intent', true)" type="button">here</button>
</template>
</ComplianceOptoutViaExternalService>
</div>
</div>
</div>
</template>
And then the child component itself:
ComplianceOptoutViaExternalService
<template>
<div>
<slot name="trigger"></slot>
<article v-show="wantsToOptOut" v-on:shown-optout-intent="hasShownOptoutIntent" class="tw-bg-white tw-p-4 md:tw-p-6 tw-rounded-xl tw-text-gray-800 tw-border tw-border-gray-300 tw-text-left tw-mt-4">
<validation-observer v-if="!didOptOut" ref="optoutServiceForm" key="optoutServiceForm" v-slot="{ handleSubmit }" tag="section">
<form>
...
</form>
</validation-observer>
</article>
</div>
</template>
<script>
export default {
data () {
return {
wantsToOptOut: false,
isOptingOut: false,
didOptOut: false
}
},
methods: {
/*
** User has shown opt out intent
*/
hasShownOptoutIntent (value) {
this.wantsToOptOut = !this.wantsToOptOut
}
}
}
</script>
Note that my child component uses a slot, this is so I can position everything as needed in the parent component, but at it's core, I have a parent component emitting a value and then listening for it via v-on:shown-optout-intent="hasShownOptoutIntent" which runs the hasShownOptoutIntent method.
But I never see anything from the button, even if I console.log this. What am I missing?
I'm doing something similar with an embedded component, so it's perhaps worth trying:
<button #click="$emit('update:shown-optout-intent', true)" type="button">here</button>
... and then:
v-on:shown-optout-intent.sync="hasShownOptoutIntent"
As an aside, it's safe to remove v-on and use: :shown-optout-intent.

Using datalist from local DOM in polymer

My custom component 'autocomplete-input' should provide an autocomplete functionality. I am using the 'list' attribute of 'paper-input'. The value of the 'list' attribute has to be the 'id' of a 'datalist'.
This works fine, as long as I instantiate only one 'autocomplete-input' element. If there are multiple instances of the 'autocomplete-input' element, they all show the same autocomplete proposals. I guess this is because the 'list' attribute does not use the local DOM and therefore uses the first 'datalist' with that particular 'id'. How can I avoid this?
The 'autocomplete-input' element:
<dom-module id="autocomplete-input">
<template>
<datalist id="autocomplete">
<template is="dom-repeat" items="{{proposals}}">
<option data-value$="{{item.value}}">{{item.content}}</option>
</template>
</datalist>
<paper-input id="input" list="autocomplete" label="{{header}}" value="{{content::input}}"></paper-input>
</template>
<script>
Polymer({
is: 'autocomplete-input',
properties: {
header: String,
content: String,
proposals: Array
}
});
</script>
</dom-module>
Use a global counter and add it to your id
<dom-module id="autocomplete-input">
<template>
<datalist id$="[[autocompleteId]]">
<template is="dom-repeat" items="{{proposals}}">
<option data-value$="{{item.value}}">{{item.content}}</option>
</template>
</datalist>
<paper-input id="input" list$="[[autocompleteId]]" label="{{header}}" value="{{content::input}}"></paper-input>
</template>
<script>
Polymer({
is: 'autocomplete-input',
properties: {
header: String,
content: String,
proposals: Array,
}
}),
autoCompleteId: function() {
return this.autocompleteId = this.autocompleteId || ++globalId;
// see linke below to figure out how to use globals
}
</script>
</dom-module>
See Polymer 1.0 Global Variables for more details about globals in Polymer (My JS isn't good enough because I use Polymer only from Dart)

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>

How to get composed DOM of Polymer Web Component?

I'm using Polymer 0.8-preview.
For example, I have the following component:
<dom-module name="greeting-tag">
<template>
<ul>
<template repeat="{{s in salutations}}">
<li>{{s.what}}: <input type="text" value="{{s.who}}"></li>
</template>
</ul>
</template>
</dom-module>
<script>
Polymer({
is: 'greeting-tag',
ready: function () {
this.salutations = [
{what: 'Hello', who: 'World'},
{what: 'GoodBye', who: 'DOM APIs'}
];
}
});
</script>
Is it possible to get the composed DOM (as JS object or text) at some point of the component life cycle?
For the example above I would like to get
<ul>
<li>Hello: <input type="text" value="World"></li>
<li>GoodBye: <input type="text" value="DOM APIs"></li>
</ul>
In other words, I need the result of processed template.
Polymer 0.8 is not yet released. Furthermore, there is clearly stated in the documentation draft, that array notification is in high flux.
The code you provided has a bundle of glitches:
dom-module should be identified by id, not by name.
template repeat is gone. One should use <template is='x-repeat'
items="{{salutations}}"> instead. Since array reflection is not yet released/frozen, I eliminated it from the example code below.
Putting everything together:
<dom-module id="greeting-tag">
<template>
<p>{{caption}}</p>
</template>
</dom-module>
<script>
Polymer({
is: 'greeting-tag',
properties: {
caption: {
type: String,
reflect: true
}
},
ready: function () {
this.caption = 'Welcome here',
console.log(this.innerHTML);
}
});
</script>
The latter will print:
//⇒ <p style-scope="greeting-tag">Welcome here</p>
AFAIK, there is no ready-to-use method to do the same with arrays yet.

Does polymer core-ajax reuse bindings?

I created two polymer elements: http://jsbin.com/vusere/1
sparkline-chart
node-list
My goal is to render a list of sparkline charts backed up by data out of a REST API.
Now I've got the problem that on each iteration over nodes all sparkline-chart values are set to those of the actual iteration. But this only occures for the history binding and not the node.id binding.
Do I miss a point here?
node-list
<polymer-element name="node-list">
<template>
<core-ajax
auto
url="http://demonstrator.herokuapp.com/nodes"
handleAs="json"
response="{{nodes}}"></core-ajax>
<template repeat="{{node in nodes}}">
<core-ajax
auto
url="http://demonstrator.herokuapp.com/node/{{node.id}}/hist_bandwidth"
handleAs="json"
response="{{history}}"></core-ajax>
<sparkline-chart values="{{history | filterHistory}}"></sparkline-chart>
<h4>{{history | filterHistory}}</h4>
<h4>{{node.id}}</h4>
</template>
</template>
<script>
Polymer('node-list', {
filterHistory: function (data) {
if (data) {
return _(data.histBandwidth.data).pluck('bandwidth').last(20).valueOf();
}
}
});
</script>
</polymer-element>
sparkline-chart
<polymer-element name="sparkline-chart" attributes="values width">
<template>
<span id="values">{{values}}</span>
<h4>{{values}}</h4>
</template>
<script>
Polymer('sparkline-chart', {
width: 100,
created: function () {
this.values = [0];
},
domReady: function () {
$(this.$.values).peity('line', { width: this.width, fill: 'none' });
},
valuesChanged: function () {
$(this.$.values).change();
}
});
</script>
</polymer-element>
Mustache bindings always refer to a property on a model object (the default model object in a polymer element is the element itself). So, in this case history refers to this.history, which is a single property, and will be constantly overwritten by the various ajax calls.
One way to fix this is by using a history per node like so:
<core-ajax
auto
url="http://demonstrator.herokuapp.com/node/{{node.id}}/hist_bandwidth"
handleAs="json"
response="{{node.history}}"></core-ajax>
<sparkline-chart values="{{node.history | filterHistory}}"></sparkline-chart>

Categories

Resources