Best way of executing js function located in parent page from custom element - aurelia

Aurelia: I have a custom element that should execute a function that located in the parent page. the custom element doesn't "know" what function it should execute - it depends on the parent page, and currently I send the name of the function as an attribute to the custom element (the attribute - on-focus-out-action-name):
<form-input field-name="firstName" title="First Name" on-focus-out-action-name ="validateInput()" />
I manage to run the function when it has no params, but when I want to send params (simple string type which is also sent as attribute) - no success
Is there a better way to do it ?
The best way was if I could pass the function as an object (Dependency Injection ?)

You should use the call binding command for this. The way to pass parameters to a function when using the call bind is a bit wonky, but once you understand it, it's easy.
In the custom element, you will pass an object to the bound function. Each property of this function will be matched to the named parameters in the binding. Let's look at it in action. In the page VM, I'll have a function:
pageFunction(paramOne, paramtwo) {
//.. stuff happens
}
This function will be called by a custom element. So in the page view, we will write the binding like this:
<my-element some-func.call="pageFunction(paramOne, paramTwo)"></my-element>
Inside my-element's VM, we can call the bound function, and pass the parameters to it like this:
this.someFunc({paramOne: this.someProp, paramTwo: this.otherProp});
I've created a runnable gist example here: https://gist.run/?id=864edc684eb107cdd71c58785b14d2f9

I prefer using a CustomEvent for this, this makes it clear in the template what is going on.
In your component you dispatch the event like this - the "details" can be any object/data you want:
let event = new CustomEvent('on-focus-out-action-name', {
detail: <some-data-you-want-to-send>,
bubbles: true
});
this.element.dispatchEvent(event);
You'll also need to inject the element in the constructor (you'll also need to use autoinject or inject Element manually)
constructor(private element: Element) {}
Your parent template would then look something like this:
<form-input field-name="firstName"
title="First Name"
on-focus-out-action-name.delegate="validateInput($event)" />
And in your parent component, you use the data you sent like this:
validateInput(event) {
let data = event.detail;
// do stuff
}
Check out this blog post for more details https://ilikekillnerds.com/2015/08/aurelia-custom-elements-custom-callback-events-tutorial/

I managed to do it in Aurelia: This is the custom element with the foucusout.trigger which calls to the focusoutAction in the appropriate timing:
<template>
<label>
${title} - ${fieldName}<input title.bind="title"
focusout.trigger="focusoutAction()" focusin.trigger="focusInAction()"
class="${cssClass}" />
</label>
</template>
This is the usage of the custom element in the parent view with the focusout.call attribute:
<form-input field-name="firstName" title="First Name"
place-holder="Enter first name"
on-focusout.call="validateInput(nameOfElement)" />
And this is the relevant code in the view model of the custom control:
#bindable onFocusout;
focusoutAction() {
var args =
{
nameOfElement: this.fieldName
};
this.onFocusout(args);
}

Related

How to refactor repetitive attributes in Vue.js

Suppose I have a form and many fields in it. I want to subscribe to change for each form field. I will have to add #change="doSome" for every field. If I have many fields it gets somewhat repetitive. How do I refactor it?
You can listen for the change event on the form tag itself instead of listening on the individual inputs.
<form #change="doSomething"> will run the function doSomething() when something inside the form has changed eg: if you type in an input and release focus
In the doSomething function, you want to find out what element changed. We get this info from the event parameter provided from the input event:
methods: {
doSomething(event) {
this.lastEvent = event.target.value;
}
}
You can see this in effect on this Codepen example
If the form element is a child of an element inside a component like so:
<template>
<div>
<form></form>
</div>
</template>
the #changeevent-listener will not work as there is nothing that changes on the root element (div) on the component.
In this case, we need to add the .native modifier, like so: #change.native="doSomething".

Passing a function reference through data attribute in Vue

I am trying to pass a function into recaptcha to be used as a callback. I need to write:
data-callback="function"
In Vue how do I add the function reference?
I've tried:
data-callback="{{ this.submitFocus }}"
data-callback="this.submitFocus"
I'm using Vue 2
Recaptcha2 uses the data-callback string to call a globally available function.
From what I can see in the documentation, it doesn't look like there's a programmatic way to set this so you might have to use something like this
beforeMount () {
window.submitFocus = () => { // using arrow function to preserve "this"
this.submitFocus()
}
},
beforeDestroy () {
delete window.submitFocus
}
with
data-callback="submitFocus"
in your template. The attribute value just needs to match the function added to window.
data-callback is an html attribute of a DOM element, it's just a string. It does not know about the context of your object instance, ie. this.
So, you can't use this when setting the attribute for your ReCaptcha, it will only understand functions that can be called without this.
If you had a function defined as
function submitFocus(){ ... }
globally, you could get ReCaptcha to call it by setting data-callback to submitFocus without the reference to this.
data-callback="submitFocus"

Pass Function as Property to Vue Component

I am trying to make my Vue Component reusable but there is a part in it which requires to run a function on button click which I have defined in the parent component.
The component's button will always run a parent function and the parameter it passes is always the same (its only other property).
Right now I am passing 2 properties to the component: 1) an object and 2) the parent function reference, which requires the object from 1) as a parameter.
The Child-Component looks like this (stripped unnecessary code):
<button v-on:click="parentMethod(placement)">Analyze</button>
Vue.component('reporting-placement', {
props: ['placement', 'method'],
template: '#reporting-placement',
methods: {
parentMethod: function(placement) {
this.method(placement);
}
}
});
The parent is making use of the child like this:
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement"></reporting-placement>
methods: {
analyzePlacement: function(placement) {
this.active_placement = placement;
},
}
As you can see, the child has only one property, placement, and the callback reference. The placement must be put in as a parameter to the reference function from the parent.
But since the parent defines the parameters, the child shouldn't concern itself with what it needs to pass to the parent function. Instead I would prefer to already pass the parameter along in the parent.
So instead of
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement"></reporting-placement>
I would prefer
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement(placement)"></reporting-placement>
(including appropriate changes in the child).
But passing the parameter along does not work that way.
Is it possible (maybe in other syntax) to 'bind' the variable to the function reference so that it is automatically passed along when the callback is called?
Info: I don't get an error message if I write it down as above but the whole Vue screws up when I pass the parameter along to the component.
Hope the issue is clear :-) Thanks a lot!
By reading your proposal I've found out that you are overusing the props passing.
Your concern that child component should not have any knowledge about the way that the parent component uses the data is completely acceptable.
To achieve this you can use Vue's event broadcasting system instead of passing the method as props.
So your code will become something like this:
Vue.component('reporting-placement', {
props: ['placement', 'method'],
template: '#reporting-placement',
methods: {
parentMethod: function(placement) {
this.$emit('reporting-placement-change', placement)
}
}
});
And you can use it like this:
<reporting-placement v-bind:placement="placement" #reporting-placement-change="analyzePlacement($event)"></reporting-placement>
But if you need the data which is provided by the method from parent it's better to consider using a state management system (which can be a simple EventBus or event the more complex Vuex)
And finally, if you really like/have to pass the method as a prop, You can put it in an object, and pass that object as prop.

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 });

accessing dojo attach point outside templated widget

I have a dojo attach point for list item which is inside a templated widget.I need to access the dojo attach point outside the widget in order to assign onclick to the list item created by the template.How do I do it?
Well, if you want to attach an event handler to it, you have to provide a function. You can override functions/properties from outside using getters and setters.
I also suggest using data-dojo-attach-event if you only need the node for attaching event handlers. For example by using: data-dojo-attach-event="onclick: myFunction". By doing this, it needs a function called myFunction in your templated widget, provide a default function (in your widget) for example:
myFunction: function() {
/** Stub */
},
And then you can do something like this from outside:
myWidget.set("myFunction", function(evt) {
console.log("Someone clicked on the list item");
});
Because the myFunction event handler is overriden, it will execute the function provided in the setter.
You could also directly access the attach points from outside using:
myWidget.listItemNode
When you have a data-dojo-attach-point="listItemNode". However, I don't think it's recommended to use it this way because now your widget is tightly coupled (you use the internal functionality of the widget).
HTML template:-
<div data-dojo-attach-point="infoDialog" >
</div>
Save this as "Template.html"
---------------------------------------------
load this html file using "dojo\text" plugin in your widget i.e.
"dojo/text!./templates/Template.html",
and store as template in widget main function
-----------------------------------------------
assign it as template string to the widget.
templateString: template,
now this template attachpoint(infoDialog) will be the part of your current widget scope.
-----------------------------------------------
attach event:-
this.infoDialog.onclick=function(){
alert("hello world")
};