scope based ref in Aurelia - aurelia

I have ref for form which is a custom element
<form ref="domRef" ...>
I have ref for field too, which is another custom element(being used inside the form)
<input type="text" ref="domRef" .....>
but inside attach() of form's view model I am getting this.domRef is input's reference.
attached(){
console.log(this.domRef);
}
So, as the execution goes on domRef is being overridden by the latest one. Why?
Why domRef's are not different for different scopes?
I cannot use different name for ref as all are being generated dynamically.
Please help me on this if there is any alternative.
Update
After Ashley's Answer:
Custom Element Form has its own VM and Custom Element Field has its own VM too.
Views:
<template>
<form ref="domRef">
<compose view-model="resources/elements/field" ..... containerLess>
</compose>
</form>
</template>
<template>
<input type="text" ref="domRef"></input>
</template>
View-Models:
export class Form{
..
attached(){
console.log(this.domRef); //returns Input's Ref Which is not correct
}
}
export class Field{
..
attached(){
console.log(this.domRef); //returns Input's Ref Which is correct
}
}
Then if domRef belongs to the current VM why is it happening?

The scope is your VM, not any HTML element, so this.domRef is gonna be set to the last element that Aurelia set that property to.
If the name is being generated dynamically, couldn't you just change the name generation code?

After digging out everything, I got the solution. i.e initializing the domRef at the time of constructing.
export class Form{
constructor(){
this.domRef = null;
}
attached(){
console.log(this.domRef); //returns Form's Ref Which is correct
}
}
export class Field{
constructor(){
this.domRef = null;
}
attached(){
console.log(this.domRef); //returns Input's Ref Which is correct
}
}
Strange but Worked.

Related

call function located in parent from a template with a parameter in Aurelia

Still newbe to Aurelia FW.
I want to execute a function located in a parent page from a template located in that parent. I have no problem doing that. The problem is that I want to take a variable\property value located in the template and use it as a parameter in the function.How can I "share" this property between the parent and the template ?
I'm assuming that binding should be the answer.
Here's the relevant code:
This is the template instance in the parent. The relevant function to run is changeStatus:
<radio-button-switch is-active.bind="account.IsEnabled" change-state-
fuction.call="changeStatus(state)"></radio-button-switch>
This is the function in the parent:
changeStatus(statusVariable) {
//TODO something with statusVariable
}
This is the template HTML:
<template>
<input type="checkbox" change.delegate="changeState($event.target.checked)">
</template>
And this is the relevant code of the template (I want to execute the changeState function with the isChecked as parameter):
import { bindable } from 'aurelia-framework';
export class radioButtonSwitch {
#bindable changeStateFuction;
changeState(isChecked)
{
this.isElementActive = isChecked;
this.changeStateFuction(isChecked);
}
}
If I got you right, you need to create "arguments object":
If you need to invoke the function with arguments, create an object
whose keys are the argument names and whose values are the argument
values, then invoke the function with this "arguments object".
So, in your code it should be like this:
this.changeStateFuction({ status: isChecked });

Call a custom attribute method from the view model

I have a custom attribute with a method to show and hide some HTML content, I've attached the attribute to an element in a view model.
How can I call a method defined in the custom attribute from the view model?
To access the custom attribute's view-model, just put the custom attribute on the element a second time, but this time put .ref="viewModelPropertyName" on to the attribute. Then, in the parent view-model, you can access methods on the attribute using viewModelPropertyName (or whatever name you gave it). You can see an example of this here: https://gist.run/?id=9819e9bf73f6bb43b07af355c5e166ad
app.html
<template>
<require from="./has-method"></require>
<div has-method="hello" has-method.ref="theAttribute"></div>
<button click.trigger="callMethod()">Call method</button>
</template>
app.js
export class App {
callMethod() {
const result = this.theAttribute.someMethod('blah');
}
}
has-method.js
export class HasMethodCustomAttribute {
someMethod(foo) {
console.log('someMethod called with foo = ' + foo + ', this.value = ' + this.value);
return `Hello ${foo}`;
}
}
There are some ways to do it, but I believe the ideal would be binding a property from your custom-attribute to your view-model. For example:
MyCustomAttribute {
#bindable showOrHide; //use this to show or hide your element
}
MyViewModel {
visible = false;
}
Usage:
<div my-custom-attribute="showOrHide.bind: visible"></div>
So, whenever you change visible you will also change showOrHide.
Nevertheless, is good to remember that Aurelia already has a show and if custom-attributes:
<div show.bind="visible" my-custom-attribute></div>
<div if.bind="visible" my-custom-attribute></div>
Make sure if you really need to create this behaviour in your custom-attribute.
This can be done without the need for a ref. Here is an example that shows how.
It calls a showNotification method on the custom attribute from the custom element using the custom attribute.
In the custom attribute:
#bindable({ defaultBindingMode: bindingMode.twoWay }) showNotificationCallback: ()=> void;
bind() {
this.showNotificationCallback = this.showNotification.bind(this);
}
showNotification() {
// Your code here
}
In the custom element view (Note the absence of parens in the value of this binding):
<div notification="show-notification-callback.bind: showSaveSuccessNotification;></div>
In the custom element view-model:
// Show the save success view to the user.
if (typeof this.showSaveSuccessNotification=== 'function') {
this.showSaveSuccessNotification();
}

Aurelia - call function on nested component

I want to call a child component's function from its parent. I have a way to do it, but I want to know if I'm missing a better way.
From Ashley Grant's blog post about accessing a custom element's viewModel from a custom attribute, I see that Aurelia adds au to the element and you can access the viewModel through that. So, if I add a nested component with a ref, like this:
<template>
<nested-element ref="childElement"></nested-element>
</template>
I can call a function on it like this:
this.childElement.au.controller.viewModel.someFunction();
This feels roundabout. I was hoping I would be able to access a nested element's viewModel through the parameters to a hook that the parent implements, such as created(owningView, myView) but I can't find a path to it.
Have I missed a better way?
Edit: I forgot to add that I need a return value from the function I'm calling, so having access to the viewmodel itself is preferable
ref gives you the element. view-model.ref gives you the element's view model.
<template>
<nested-element view-model.ref="childViewModel"></nested-element>
</template>
Call it like this in the parent view-model:
this.childViewModel.someFunction();
If you only have one instance of the nested-element or don't care if multiple nested-elements respond to the event. Then you could use standard Javascript event functionality for this:
bar.html
<template>
<h1>${value}</h1>
<input type="text" value.bind="value"></input>
<foo>text</foo>
</template>
bar.ts
import {bindable} from 'aurelia-framework';
export class Bar {
#bindable value;
public valueChanged(newValue, oldValue) {
var event = new CustomEvent("some-event-name", { "detail": { message: "Hello from Bar", oldValue, newValue } });
document.dispatchEvent(event);
}
}
foo.html
<template>
<h1>${value}</h1>
</template>
foo.ts
import {bindable} from 'aurelia-framework';
export class Foo {
constructor() {
document.addEventListener("some-event-name", (e) => {
console.log('hello here is Foo, recieved event from Bar : ', e);
}, true);
}
}

How to set a parent property from within a custom element in Aurelia?

A few days ago I asked this question 2 way databinding in Aurelia custom elements - bind custom element to parent viewmodel
Now I need to be able to reuse the allSelectableValues from my custom element (my-custom.js) in my parent element (create.js).
I need this for a custom value converter I have on create.js which contains some Ids which I need to display names for instead, by looping through the array of elements, currently fetched and residing in my custom element.
**create.html**
<td>${d.SomeID | allSelectableValuesMapping}</td>
and
**value-converters/all-selectable-values-mapping.js**
export class AllSelectableValuesMappingValueConverter {
toView(value) {
for(let item in allSelectableValues) {
if (item.SomeID == value){
return item.Name;
}
}
}
}
In the ideal world I'd have hoped something like this would have worked:
**my-custom.js**
async attached() {
this.allSelectableValues= await await this.myService.getAllValues();
this.parent.allSelectableValues = this.allSelectableValues;
}
But my custom element have no idea of the parent which is requiring it.
Does anyone have an idea how to set the parent's allSelectableValues equal to the custom element's allSelectableValues from within the custom element? Or is there another, better way of achieving it, while still maintaining the two-way databound custom element?
Something like this ?
Please take extra note of the #customElement('CustomElement') declarator above the export class CustomElement line of code.
Custom Element View Model
import {inject} from 'aurelia-framework';
import {customElement} from 'aurelia-framework';
import {bindable} from 'aurelia-framework';
#customElement('CustomElement')
export class CustomElement {
#bindable arrItems
}
Custom Element HTML
<template>
<div repeat.for="item of arrItems">$(item.someProperty}</div>
</template>
Parent View Model
export class ParentViewModel {
parentArrItems = [];
}
Parent HTML
<template>
<require from="customelement"></require>
<CustomElement arrItems.bind="parentArrItems"></CustomElement>
</template>

Two-way binding between parent and child custom element in Aurelia

I thought I was trying to do something very simple but I just can't make this work. This entire example is on plunkr
I have a very basic custom element that present a #bindable data member that it displays and monitors with a changed event. It look like this:
import {bindable} from "aurelia-framework";
export class ChildElementCustomElement {
#bindable childData;
childDataChanged(value) {
alert("Child Data changed " + value);
}
}
and the view:
<template>
<div style="border: solid 1pt green;">
<h2>This is the child</h2>
This is the child data : ${childData}
</div>
</template>
The parent shows the child element but I want a member in its view model that's bound to the child so any change in the parent member is automatically reflected in the child. Here's the parent code:
import {bindable} from "aurelia-framework";
export class App {
parentData = "this is parent data";
}
and the view:
<template>
<h1>Two-way binding between parent and child custom elements</h1>
<require from="./child-element"></require>
<child-element childData.bind="parentData"></child-element>
<hr/>
<label>The following is the parent data:</label>
<input type="text" value.bind="parentData"></input>
</template>
What I'd like to see is any updates typed in the input field will automatically appear in the child (plus the changed event fires) but the child doesn't appear bound at all! I've also tried swapping bind for two-way just in case the convention has bound one-way but that still hasn't worked.
Please highlight my stupidity :) because currently I'm stuck thinking this should just work.
The default convention for #bindable is to turn the camel-cased property names to attribute names using the naming convention 'myProperty' -> 'my-property' (dash-casing).
From the documentation:
#bindable({
name:'myProperty', //name of the property on the class
attribute:'my-property', //name of the attribute in HTML
changeHandler:'myPropertyChanged', //name of the method to invoke when the property changes
defaultBindingMode: bindingMode.oneWay, //default binding mode used with the .bind command
defaultValue: undefined //default value of the property, if not bound or set in HTML
})
The defaults and conventions are shown above. So, you would only need
to specify these options if you need to deviate.
So you need to write child-data.bind in your HTML
<child-element child-data.bind="parentData"></child-element>
Plunkr