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.
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 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!
Can parent-child directive communication using 'require' be used if there are multiple instances of both parent and child directives? (i.e. both directives are inside ng-repeat blocks)
I have a parent child directive relationship, but the parent directive is included in an ng-repeat block. When I use 'require' on the child directive it always binds to the first instance of the parent directive and not the first one above the child directive in the DOM.
Yes it is possible; as nice visual example (I'm a visual person) angular-gridster has parent and children directives. If we just throw them in a ng-repeat everything works. This is because ng-repeat has its own scope in which it creates new child scopes for each item.
See this plunker
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
I inherited a custom component from TextField. The component needs to know when any of its styles got changed at runtime via setStyle. How would I do that? It's probably obvious but I couldn't find an event or appropriate method to override.
If you want the text field to play nicely with containers and other components in Flex you may want to wrap it in a UIComponent, or have the subclass implement the IUIComponent and IStyleClient or ISimpleStyleClient interfaces (which UIComponent implements). If you do the component will work with Flex' style system and every time a style changes a method calledstyleChanged` will be called:
public function styleChanged(styleProp:String):void
See http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#styleChanged()
styleChanged () method
public function styleChanged(styleProp:String):void
Detects changes to style properties. When any style property is set, Flex calls the styleChanged() method, passing to it the name of the style being set.
This is an advanced method that you might override when creating a subclass of UIComponent. When you create a custom component, you can override the styleChanged() method to check the style name passed to it, and handle the change accordingly. This lets you override the default behavior of an existing style, or add your own custom style properties.
If you handle the style property, your override of the styleChanged() method should call the invalidateDisplayList() method to cause Flex to execute the component's updateDisplayList() method at the next screen update.
Parameters styleProp:String — The name of the style property, or null if all styles for this component have changed.