I have a component <my-comp> which has a property with double binding which I pass to a different component <my-sec-comp show-drawer="{{showDrawer}}">.
When I change the property showDrawer in the parent element it is also noticed in the child element. But not the other way around. How can I achieve this?
declaration in child element is:
showDrawer : {
type: Boolean,
notify: true
}
declaration in parent element is:
showDrawer : {
type: Boolean,
notify: true,
observer: '_drawerChanged'
},
In Polymer before 2.0, you can't. There are no two way bindings between parent and child elements, only between properties and elements within the same element.
If you want to update variables from child to parent, use an event, like this.fire('drawerChanged', this.showDrawer) or try this.parentElement.set('showDrawer', this.showDrawer) inside your _drawerChanged method.
You can use redux for this it allows you to share information between any components. They donĀ“t even need to be siblings. Have a look at this
Related
live example
Basically:
I place the child component inside the parent's html.
The child component has a property title inside the static get properties() object.
In the parent component, i assign the child component's title property to a private variable that is not in the static get properties() of the parent component.
Later, in a setTimeout inside the parent component, i change the value of the private variable.
The child component doesn't re-render.
//inside parent component
render(){
return html`
<child-component title="${this._privateVariable}"></child-component>
`
}
constructor(){
super();
this._privateVariable = 'lore ipsum'
setTimeout(()=>{
this._privateVariable = '123455667'
}, 10000)
}
Now, i know that if i put the _privateVariable in the static get properties() of the parent component, the child re-renders, but that's because the whole parent is re-rendering.
Once attached to the DOM, the child component is watching his title property, so it should realize that the value assigned to it changed and re-render.
I don't understand why i have to re-render the whole parent to make the child re-render.
Using the developer console and accessing the component in the DOM:
If i change the child component's property with javascript, the child re-renders perfectly fine, without me having to re-render the whole parent element.
If i edit the child component's html and manually change the attribute, the child re-renders perfectly fine, without me having to re-render the whole parent element.
What am I missing here?
I answered on GitHub: https://github.com/Polymer/lit-element/issues/899#issuecomment-592295954
But here's a copy for other readers:
#dvolp the child component is observing its title property, but it's not observing the parent component's _privateVariable property. And without declaring _privateVariable as a LitElement property in the static properties object, the parent component isn't observing that property either.
You need to re-render the parent when you set _privateVariable because re-rendering is what sets the child's title property to the current value of _privateVariable.
The binding in here only runs on a render. If this isn't run, there's nothing else to set the title property:
//inside parent component
render(){
return html`
<child-component title="${this._privateVariable}"></child-component>
`
}
This is exactly why you must include _privateVariable in the static properties object of the parent, so that when you set it, the parent component is notified and re-renders, which in turn sets the title property on the child component.
In polymer, properties will be watched/observed for changes only if they are declared in static get properties() section. As you are not declaring '_privateVariable' in properties() section of parent, any changes made to it will not be injected back into the child component and hence the value won't be updated.
When you change the value of '_privateVariable' after declaring it in parent, complete parent won't be reloaded its only the child component which will reload.
However, if you still have any requirement to not declare the '_privateVaraible' in parent and still needs an update, you can update the variable value in child component and encapsulate the update in a function and call that function from your parent's setTimeout.
I have a parent component where the view model contains a property prop1.
In its view, a custom element has a view-model.ref="prop1".
export class Parent {
public prop1;
}
<template>
<custom-element view-model.ref="prop1"></custom-element>
</template>
This works like a charm and I get a reference to the view model of my custom element in my parent.
Now, I add a child router to parent with a child component. In child's view model, I add a property prop1. and in its view, a custom element has a view-model.ref="prop1". So exactly like I did in parent...
As soon as I navigate to parent/child, the parent's container prop1 stops referencing the custom element of parent and starts referencing the one from child.
If I name the properties differently, there is no problem.
Any idea why this happens? And how could I avoid this behavior without worrying about the naming of the properties?
EDIT
I chanced upon some more information! If the properties are initialized in the view model, I seem to be able to retain the references in the view models. Note that I'm using Typescript so I think the compiled code for an unassigned class property doesn't mention the property at all until it is assigned.
I still don't really understand where the problem comes from exactly...
And I remain with the same problem if I use a view-model.ref directly in the template without mapping it to an explicit property from the view model like this:
<template>
<custom-element view-model.ref="custom"></custom-element>
<custom-element2 opened.call="custom.opened()"></custom-element2>
</template>
when you create a property in a class and don't assign anything to it, babel/typescript will remove that property as if it was not even declared.. because it's really not doing anything.
typescript is interested in your definitions in compile time only.
now that your property in the child is emitted, you have a binding in the child to an undeclared property.
in that case, aurelia creates a property for you and bind to it..
but in your case, aurelia finds this property in the parent scope (just like regular JS scoping rules), and therefor do not create a "new property" in the child scope but rather binds to the parent property.
this is why when you initialize the property in the child (even with undefined) it "works". because you have 2 properties in 2 scopes.
and when you change the name, aurelia will create a new property for you..
and here come another rule - wen aurelia create a property for you - it creates it in the lowest scope available.. in your case - the child scope. and everything works again.
you can notice this behavior a lot with repeate.for, because the repeater creates an invisible scope for each repeate loop..
if you bind anything in the repeate to a property that dont exists, you will have that property in each child, and not once in the parent.
I have an issue in dealing with a hover event that is being propagated from a child of nested elements that are of the same directive. I am pretty new to Angular, so please be gentle and bear with me if I am missing something obvious.
What I am trying to accomplish:
I have a custom attribute directive that I am writing to create a pseudo-venn diagram ui module in which a circle will expand when hovered over. The idea is that I am trying to achieve this using a single custom directive, with behaviours changing depending on the location of the element within the dom, where if the parent of an element with the directive tag is not another element with the directive tag then it is viewed as a "root circle", otherwise if the element with the tag has a parent with the directive tag then it is viewed as a "child circle" as shown below.
<div vennCircle diameter="20vh" percentOverlap="35" parentAngle="300" id="smc">
Parent
<div vennCircle diameter="30vh" percentOverlap="35" parentAngle="0" id="smc1">
child 1
</div>
<div vennCircle diameter="30vh" percentOverlap="35" parentAngle="90" id="smc2">
child 2
</div>
</div>
I followed the Angular Docs for finding a parent component through injecting in order to create the directive as shown below and have been able to use this in order to successfully distinguish the parent from the child directive element.
export abstract class Parent { name: string; }
#Directive({
selector: '[vennCircle]',
providers: [{ provide: Parent, useExisting: forwardRef(() => VennDiagramDirective) }]
})
export class VennDiagramDirective implements AfterViewInit, Parent {
name: string
constructor(private el: ElementRef, #SkipSelf() #Optional() public parent: Parent) {
console.log(el);
console.log(parent);
}
#HostListener('mouseenter', ['$event']) onMouseEnter(event: any) {
this.el.nativeElement.style.backgroundColor = 'rgba(0,0,255,1)';
}
}
Following are the Angular docs for creating a custom attribute directive (I am limited in number of links that I can post) I am trying to capture the hover event to trigger other behavior in the code above, it is simply changing the background color of the element hovered over.
My problem:
The issue that I am having is that if I hover over just the parent circle, everything works as expected and the background color changes, however if I hover over either of the two child circles, they will also change the color, but so will the parent circle since the event propagates, the following images demonstrate what it looks like:
link to picture
Now I may be new to Angular, but this behaviour alone does not surprise me, it is my understanding that events propagate or bubble up and I was under the impression that this was supposed to occur first at the lowest level and moving upward in the hierarchy. If my understanding is correct, then it should follow that the hover event should be received by the child circle directive first when the child is hovered over and then should propagate upwards in the tree to the parent circle. If that follows, then I should be able to stop propagation of the event when it is first received, resulting in only the child circle changing colors, however this is not the case.
When I printed to the console with a timestamp, it shows that the parent circle ("#smc") is receiving the event prior to its child ("#smc1"), as shown below:
ElementRef {nativeElement: div#smc}
1504205896178
ElementRef {nativeElement: div#smc1}
1504205896206
My question(s)
How, if possible, can I prevent the parent element directive from receiving the hover event of its child?
If the above is not possible, how can I determine the origin of the hover event such that I can filter out events that originated from the child elements in the parent element directive?
Notes
I am currently dead set on achieving this with a single directive, even if it is tricky, the idea being that I want to be able to use this ui module elsewhere or share it with others, in a way that only requires a single declaration/import in the module.ts. I am already aware that I could create X different components/directives for each type of circle, however I do not want that, that makes using the module too complex in my opinion.
I do not want a dev using this to have to manually setup the events in the html, an attribute directive enables this so I want to be able to use it.
Thank you all in advance for your help, please comment if you need more info, or if I did something wrong, remember that I am new and learning!
I have a child custom element with data property.
My goal is to assign an event listener to this property (using javascript and not {{}} data-bind syntax) in the context of the parent element.
I search something like this:
ready(){
this.addEventListener(this.$.childElememt.data, _onChildDataChange);
},
_onChildDataChange() {
//called when data property of child elememt changes
}
In Polymer, properties that have notify: true set on them will cause the element to fire a non-bubbling <property>-changed event that their parent can listen to.
I suspect you could listen to this event manually (provided that the child property is set up to notify) by doing something like this:
ready() {
this.$.childElement.addEventListener('data-changed', this._onChildDataChange.bind(this));
},
_onChildDataChange() {
//called when data property of child element changes
}
However, this is effectively equivalent to just using the two-way data-binding syntax on the child with an observer on the parent property, so I would recommend that you do that instead if possible. The data-binding is basically already doing the exact same thing for you.
I have two polymer elements like
<moviegrep-element></moviegrep-element>
<custom-card-element></custom-card-element>
In moviegrep-element I got an array of objects called results. I want to use the results in my custom-card-element. How does it work?
Use Polymers data-binding:
<moviegrep-element results="{{sharedResults}}"></moviegrep-element>
<custom-card-element results="{{sharedResults}}"></custom-card-element>
This assumes that both of your elements publish the results property as an attribute. Changes to the results property in one element are then propagated to the results property in the other element.
This also assumes that your elements are itself inside a Polymer element. Otherwise you need an auto-binding template element