I want to achieve communication between child parent with Polymer element.
Here my index.html
<proto-receiver data="message">
<proto-element data="message"></proto-element>
</proto-receiver>
Both element have their respective "data" property
properties: {
data: {
value: 'my-data',
notify: true,
}
},
In proto-receiver, which is the parent I update "data" by handling simple click
<template>
<span on-tap="onClick">proto receiver: {{data}}</span>
<content></content>
</template>
onClick: function () {
this.data = 'new-message';
},
I want the change to be propagate to the child element as well, as it mentioned here.
I achieve this by passing a setter in my child element and called it like this. Which is, I guess, not the way it should be done.
Polymer.Base.$$('body').querySelector('proto-element').setData(this.data);
What I'm doing wrong
Thanks
UPDATE:
For those coming here. The proper way of doing this is by using Events.
Polymer 1.x
this.fire('kick', {kicked: true});
Polymer 2.x (simple javascript)
this.dispatchEvent(new CustomEvent('kick', {detail: {kicked: true}}));
In both case the receiver should implement the regular addEventListener
document.querySelector('x-custom').addEventListener('kick', function (e) {
console.log(e.detail.kicked); // true
})
To provide a concrete example to Scott Miles' comments, if you can wrap your parent and child elements in a Polymer template (such as dom-bind or as children to yet another Polymer element), then you can handle this declaratively. Check out the mediator pattern.
parent element:
<dom-module id="parent-el">
<template>
<button on-tap="onTap">set message from parent-el</button>
<p>parent-el.message: {{message}}</p>
<content></content>
</template>
<script>
Polymer({
is: 'parent-el',
properties: {
message: {
type: String,
notify: true
}
},
onTap: function() {
this.message = 'this was set from parent-el';
}
});
</script>
</dom-module>
child element:
<dom-module id="child-el">
<template>
<p>child-el.message: {{message}}</p>
</template>
<script>
Polymer({
is: 'child-el',
properties: {
message: {
type: String,
notify: true
}
}
});
</script>
</dom-module>
index.html:
<template is="dom-bind" id="app">
<parent-el message="{{message}}">
<child-el message="{{message}}"></child-el>
</parent-el>
</template>
<script>
(function(document) {
var app = document.querySelector('#app');
app.message = 'this was set from index.html script';
}) (document);
</script>
JS Bin
I was facing same issue and got solution for it and fixed it as below
this.fire('iron-signal', {name: 'hello', data: null});
You can refer this iron-signals you will get the solution which you are looking for its basically event fire from any element to another
Hope this will help you
Polymer iron signals
Related
I have a vue3 app, and one of the child component uses vue-draggable.
In the parent component I have an object (let's call it myJson) which propagates to child component with props.
So far it works as expected.
However, when adding 'KeepAlive' to the parent component, every time I drag the items, myJson is set to the drag event instead of the origin data it had.
It still occures even if I pass to the child component a copy of myJson (with JSON parse-JSON stringify). See details below
parent component:
<template>
<KeepAlive>
<component :is="activeComponent" :my-json="myJson" />
</KeepAlive >
</template>
data: () => ({
myJson: { ...someData }
})
mid component:
<template>
<list-items :items="items" />
</template>
<script>
export default {
components: { ListItems },
computed: {
items() {
return JSON.parse(JSON.stringify(this.myJson.value.items))
}
},
}
</script>
child component (ListItems):
<template>
<draggable
v-model="items"
animation="100"
handle=".dnd-handle"
item-key="product"
class="items-list"
#start="drag=true"
#end="drag=false"
>
<template #item="{ element, index }">
{{element}}
</template>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: { draggable },
props: ['items'],
}
</script>
The items are displayed correctly in the component.
Before dragging, myJson is an object with my data.
After dragging myJson is an event.
Any idea?
vuedraggable version is 4.1.0
--UPDATE--
In parent component there is a function "update", which gets value and updates myJson.
methods: {
update (value) {
myJson = value
}
}
I found out that every time I drag, there is a call to this function with the dragging event as value, even when I try to catch the draggable events. Thats why myJson gets wrong value.
My problem was solved when I changed the function's name. But anyone knows why this happens?
I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.
I have a component that loads an object from Firebase using firebase-document. Then that object is passed to a child component. When I change the object in the child component, the change is not detected in the parent, so the object is not updated in Firebase.
Here is the main component:
<dom-module id="some-component">
<template>
<firebase-document path="/projects/[[project_id]]" data="{{project}}"></firebase-document>
<some-child project="{{project}}"></some-child>
</template>
<script>
Polymer({
is: 'some-component',
properties: {
project: {type: Object, notify: true, observer: "projectChanged"}
},
projectChanged: function() {
console.log("we've detected some changes!");
}
});
</script>
</dom-module>
And here is the child component:
<dom-module id="some-child">
<template>
<a on-tap="changeProject">Let's change some property on our project!</a>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
project: {type: Object, notify: true}
},
changeProject: function() {
this.project.name = "A new name"; // this never propagates back to the parent component
}
});
</script>
</dom-module>
The expected behavior is that when I click on the link, the object's property would change, it would be detected by the parent, and there would be a console.log. However it doesn't seem to happen.
Update with the solution
Using this.set() in the child component does the trick:
this.set("project.name", "A new name")
I think your issue here, is from the observer.
A simple observer like this only watch the reference of the object itself.
You can use a deep observer instead like this
properties: {
project: {type: Object, notify: true}
},
observers:[
'projectChanged(project.name)'
],
or for a more properties generic version
properties: {
project: {type: Object, notify: true}
},
observers:[
'projectChanged(project.*)'
],
and it should work like this.
Here is the link to the full documentation on the subject.
https://www.polymer-project.org/1.0/docs/devguide/observers#observing-path-changes
To be perfect you can also change the classic way of setting the value by the polymer way, it ensure better detection by the framework, remplacing
this.project.name = "A new name";
by
this.set("project.name", "A new name")
It will help on complexe binding case, or heavy objects it seems.
How is this for a solution for syning the data from a paper input to a firebase database.
properties: {
teamid: {
type: String,
value: null
},
formid: {
type: String,
value: null
},
metaName: {
type: String,
value: null,
observer: '_updateMetaName'
}
},
_updateMetaName: function(metaName) {
var path = 'formModel/' + this.teamid + '/' + this.formid + '/meta/name';
firebase.database().ref(path).set(metaName);
},
The data metaName comes from a a paper-input element
<paper-input value="{{metaName}}"></paper-input>
I'm using an observer over the on-change attribute because I hate the idea that a user must move out of an input for it to persist.
I've also chosen not to use PolymerFire because i dosen't have some features I need and its not production ready.
I also don't like the idea that the observer runs multiple times before any data has been changed. And that should, i thought, break it but its working to my surprise.
What other options do I have?
Are their any disadvantages to my current solution?
One disadvantage is that every keystroke fires off a request to Firebase, which could be inefficient (a waste of CPU and bandwidth).
To address this, you could debounce the callback with this.debounce(jobName, callback, wait), as shown in the following demo.
HTMLImports.whenReady(_ => {
"use strict";
Polymer({
is: 'x-foo',
properties : {
metaName: {
type: String,
value: 'Hello world!',
observer: '_metaNameChanged'
}
},
_setFirebaseMetaName: function(metaName) {
var path = 'formModel/' + this.teamid + '/' + this.formid + '/meta/name';
//firebase.database().ref(path).set(metaName);
console.log('metaName', metaName);
},
_metaNameChanged: function(metaName) {
this.debounce('keyDebouncer',
_ => this._setFirebaseMetaName(metaName),
500);
}
});
});
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="paper-input/paper-input.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<paper-input label="Meta Name" value="{{metaName}}"></paper-input>
</template>
</dom-module>
</body>
codepen
I've decided to go with on-keyup="_updateViewDesc" to stop a error occurring when multiple clients have the same page open. Using observers, when some data updates, it triggers the observer on all the connected clients. Causing characters to go missing.
When trying to define an element with conditional attribute
<polymer-element name="hover-button" extends="paper-button" hover?="{{hover}}" attributes="hover">
I'm running into the following error
Uncaught InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'hover?' is not a valid attribute name.
Still at Polymer 0.5.
Here is the full code:
<polymer-element name="hover-button" extends="paper-button" hover?="{{hover}}">
<template>
<shadow></shadow>
</template>
<script>
(function(){
Polymer('hover-button', {
ready: function(){
this.addEventListener('mouseover', function(){ this.hover = true; }.bind(this));
this.addEventListener('mouseout', function(){ this.hover = false; }.bind(this));
},
activeChanged: function(){ /* foo */ },
hoverChanged: function(){ /* bar */ }
});
})();
</script>
</polymer-element>
You can't use data-binding features on <polymer-element> itself. Furthermore, any attribute that is declared on <polymer-element> (which is not part of the attributes attribute is added to instances of the element by default. I suspect that's where the error is coming from.