I seem to have an odd error, and I am not sure if it is my assumption which is incorrect here or not, but there is no docs on the subject.
So when you make custom elements/attributes and you set members on that class, they are per use, so every usage of these custom attr/elements would have their own instance of the class to go with it.
However it currently seems that when using binding behaviours the class is shared between all uses of that binding behaviour, with the bind method being the point of isolation.
I say this because I was assuming they were all setup to be an instance per use and in this case some async logic is getting a value from the last used binding behaviour on the class i.e this.someClassStateVar.
So can anyone confirm if Aurelia uses binding behaviours in a singleton scope?
=== Edit ===
Based upon first answer I wanted to confirm scenario:
<section with-binding.bind="something">
<input value.bind="somethingElse & someBindingBehaviour">
<input value.bind="somethingElse2 & someBindingBehaviour">
</section>
So I was expecting that there would be 2 instances of someBindingBehaviour class and each would be applicable to the relevant input, but the behaviour I am seeing is that they both use the same instance of the someBindingBehaviour class.
In Aurelia, value converters and binding behaviors are singletons. They should be stateless. Some types of binding behaviors may need to keep track of some state, the best place to store that state is on the binding instance (passed to the binding behavior's bind and unbind methods).
Related
I’m trying to fire a callback passed down from a parent component. Our old pattern for handling this was to call the function in didInsertElement. In Octane, I see that we can use the did-insert modifier but that seems weird for this use case since we’re not updating the DOM element that we’d use to call did-insert. I've also seen onRender being used in a few cases but I don’t see documentation on that and it’s not firing for me. Any suggestions?
For this specific use case, we have a parent component that can have one of many child components. And for each child component we have specific text that gets displayed in the parent component and we want the child component to be the owner of that text.
Your instinct that did-insert isn't the right solution here is, I think, correct. In general, modifiers should only be used when the element they're going to be attached to is used in some way—that is, for managing interactions with the DOM. In general, we prefer one-way data flow otherwise. However, the scenario you've outlined looks similar to a "registration" pattern, where when a child is instantiated, it tells its parent "Hey, I'm here, here's the info you need about me."
There are a couple reasonable options in this case:
Rethink whether the child component should in fact own that data. I don't doubt you have a good reason for the child component owning the data, but the fact that the parent is already responsible for deciding which child to render may suggest that a mapping which includes both the component to render and the associated text could be a good solution in this space. That would cleanly solve this issue.
Given that per your description you currently do want to avoid having the parent own that data, you could also consider having the child yield the data. However, this usually only works if the DOM relationship for it makes sense. If it does, you could choose to do something like this:
{{yield (hash block='data' text=this.theText)}}
<div class='the-component-body'>
{{yield}}
</div>
<ChildComponent as |child|>
{{#if (eq child.block 'data'}}
<h2>{{child.text}}</h2>
{{/if}}
{{child}}
</Child>
(You can see this strategy working here—in particular, see the resulting DOM!)
While that's very powerful, again, it only works if your DOM layout supports it.
Finally, and perhaps most simply for the use case you have, you can (though not necessarily best, as I think the other options above are usually better when available), you can 'register' the value for the component by calling an action passed into your component during the constructor. Now, using a component's constructor this way for component behavior can be a problem in that it only runs once, when the component is first instantiated, and Glimmer and Ember keep the component instance stable and just change the values passed to them over time as much as possible, so if the value you're passing back up depends on the argument you pass to it, the constructor won't work. If it's always stable and does not depend on the arguments to a component, this does work, and it's often appropriate for a 'registration' pattern, where the child component simply needs to pass a single piece of data to the parent when instantiated.
I wrote this demo, and I want to make sure I understand the dynamics correctly.
In it whenever one of the checkboxes is selected it causes the corresponding merge of the observable checkbox events to trigger.
The trigger causes the redefinition of an array of instances that say whether a material table columns should be hidden or not.
The component also has this method that defines the material table columns:
getDisplayedColumns():string[] {
return this.columnDefinitions.filter(cd=>!cd.hide).map(cd=>cd.def);
}
IIUC the reason that method gets called is that it gets called any time the UI is updated. In other words Angular Change detection triggers it?
Your method is called from the template for every change detection cycle. The change detection mechanism tries to find any changes in the model of the component and then re-render changed parts.
But you can manage how the change detection mechanism works with your component by setting the change detection strategy.
Change detection strategies
By default change detection is launched for many reasons - clicks, scrolling, or other async events. Hence the method can be invoked for hundreds/thousands of times.
But you can use changeDetection: ChangeDetectionStrategy.OnPush in your component. The change detection mechanism checks changes only if #Input() properties have changed.
But it is only half of the solution...
Avoid using methods in the template
Another half - is not to use a method in the template at all and use class property there. Btw follow the same rule for getters as well
<mat-row *matRowDef="let row; columns: columns"></mat-row>
Everything you need is to set this.columns in your class when it should be really changed.
I hope it helps.
Yes. Every click or another event in the UI tries to call getDisplayedColumns once and again because it is a method in the html. You have to avoid that, setting a class property with this columns when it has loaded.
Look my corrections:
https://stackblitz.com/edit/angular-material-table-hide-columns-owzmtl?file=src/app/app.component.ts
I'm new to web components, and I've noticed some examples set the dom in the custom-element's constructor, while others do it in the connectedCallback.
As both seem to work fine(although I tried only Chrome), I assume the main difference is the case in which a user creates the element in js and not attaching it to the page?
I guess the main question here is whether I'm missing some other reason to prefer one method over the other.
Thanks.
Best practices and rules for custom element constructors
What's safe to do in the constructor
In the constructor, it's safe to
create the shadow root;
create and append elements *;
attach event listeners to those elements (scoped to your own ShadowDOM);
create attributes * (which might still be a bad idea because in the dynamic creation case this might come unexpected).
What you cannot do in the constructor
In the constructor, you are not allowed (amongst other things)
to read any attributes you haven't created beforehand...
to access child elements...
...because those might not be present in the non-upgrade case, and definitely won't be present when you dynamically create your custom element using either document.createElement('my-custom-element') or new MyCustomElement.
What's unwise to do in the constructor
In the constructor, you probably don't want to
attach event listeners to elements outside of the component's shadow DOM (like e.g. document, window), because these are the kind of listeners you should clean up in your component's disconnectedCallback (which will be called when e.g. your component is moved in the DOM).
Attaching these listeners in the constructor and properly cleaning them up in the disconnectedCallback results in missing listeners once your component gets removed from (and later re-added) or moved in the DOM.
*Pitfalls and things to be aware of
You need to be aware of the custom element lifecycle to not fall into otherwise obvious pitfalls, which include:
If you add attributes in the constructor and have included those in your component's observedAttributes, remember this will immediately trigger the attributeChangedCallback for those attributes, even if you element is not yet connected (a.k.a. in the DOM).
If you create and append other custom elements into your component's shadow DOM, remember this will trigger those components' connectedCallback.
In part, these best practices and rules follow https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance, in other parts they deviate from recommendations done there in the spec.
Specifically I disagree on the following (given the scope for the listeners is outside the component), for the reasons I gave above.
In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.
I have a drop down in Angular that is a 2 way bound FORM control. When the form loads all I want to do is set a default value on the bound value so it displays that value in the drop down. When I change the value in the drop down and print to console I see the binding is correct. However the first time it loads the default value is not displayed in the drop down (not preselected) . In this sample when the form loads "cow" should be the default selected item but it is not working on page load. Please advise what is wrong in the code.
https://stackblitz.com/edit/angular-error-initial
Programmattically assign the value to your FormControl...
ngAfterViewInit(){
this.animalControl.setValue(this.animals[2]);
this.animalControl.markAsTouched();
console.log('FormControl Value: '+JSON.stringify(this.animalControl.value))
}
Stackblitz
https://stackblitz.com/edit/angular-error-initial-atr11t?embed=1&file=app/select-hint-error-example.ts
Revision
Use of ngModel with FormControl has been decprecated and is removed from Angular 7... you should start getting use to accessing values from the FormControl.
console.log('FormControl Value: '+JSON.stringify(this.animalControl.value))
https://next.angular.io/api/forms/FormControlName#use-with-ngmodel
This has been deprecated for a few reasons. First, developers have
found this pattern confusing. It seems like the actual ngModel
directive is being used, but in fact it's an input/output property
named ngModel on the reactive form directive that simply approximates
(some of) its behavior. Specifically, it allows getting/setting the
value and intercepting value events. However, some of ngModel's other
features - like delaying updates withngModelOptions or exporting the
directive - simply don't work, which has understandably caused some
confusion.
In addition, this pattern mixes template-driven and reactive forms
strategies, which we generally don't recommend because it doesn't take
advantage of the full benefits of either strategy. Setting the value
in the template violates the template-agnostic principles behind
reactive forms, whereas adding a FormControl/FormGroup layer in the
class removes the convenience of defining forms in the template.
To update your code before v7, you'll want to decide whether to stick
with reactive form directives (and get/set values using reactive forms
patterns) or switch over to template-driven directives.
How do I access properties of parent or other elements in Polymer?
For example my top-most element is "my-app".
Now I am in an element called "my-element-1", how would I access/reference any properties from "my-app" using Javascript?
Further, if I am in "my-element-2", would I be able to access/reference properties of "my-element-1"?
EDIT: There are many reasons why I want to do this, and I certainly believe there are better ways than having to do this. But, I do not know the ways.
1: The first use case that comes to mind is that I use "iron-pages", which is located in a parent element. Each "page" is therefore a child of that parent.
The parent definitely knows which "page" is "selected" because the "iron-pages" is a property of the parent.
However, each "page" does not know if it is selected or no longer selected because the "selected" attribute is only known to the parent.
In this use case, I just want to be able to know from within the "page" whether it is still selected or not.
2: The second use case is probably a general design pattern. I want to maintain a set of GLOBAL properties, which can then be accessed anywhere from within the Polymer app. My assumption is that it should be store within "my-app" as the root element.
1 In a closed parent-child pair like with the iron-pages you can take advantage of the selectedAttribute and selectedClass properties. By setting one of them you can let the child "know" that it's selected.
2 The other example isn't so simple. For keeping shared state you can use the oxygen-state element which was mentioned on Polymer's Slack recently. Similar solution is possible with iron-meta which lets you access global variables.
The more robust solution to connecting elements app-wide is the Flux pattern. It appeared as a solution to a similar problem - communication between elements on a page. If elements are to communicate freely, it quickly can become unwieldy. You can read a number of related questions on SO: Flux architecture with polymer elements, Binding to global variables in Polymer, Polymer 1.0: How to pass an event to a child-node element without using <iron-signals>?
A simplification of this is to use events to notify any interested element of changes elsewhere in your application. A naive way is to listen for events on the document. Then there is also the iron-signals element.