Reputation: 85
I have a custom element that displays some text in a span, and if the user decides to edit the text, I hide the span and display a text input. When the user focuses off the text input, I hide the input and display the span again with the updated text. This all works great.
When I use the custom element on a page, I need to be able to trigger a function on the page to take the updated text from the custom element to update the text in a database.
When the user completes editing the text and I re-display the span, I don't know how to bubble out that event so I can call a function on the page to save the change. If I was using a button or something, I could set the delegate for the button click, but don't know how to accomplish this since I'm not using a button.
Hope this makes sense! Thanks in advance!
Here is some of the stripped down code.
custom-element.html:
<i if.bind="icon" class="${icon}"></i><span if.bind="!(allowEditTitle && isEditingTitle)" maxlength>${title}</span>
<input ref="titleInputEditor" type="text" value.bind="title" if.bind="allowEditTitle && isEditingTitle" maxlength="100" />
custom-element.ts
@bindable title: string;
@bindable allowEditTitle: boolean = false;
isEditingTitle: boolean = false;
toggleEditTitle() {
let me = this;
if (this.allowEditTitle === true) {
this.isEditingTitle = !this.isEditingTitle;
}
if (this.isEditingTitle == true) {
window.setTimeout(function () {
$(me.titleInputEditor).focus();
$(me.titleInputEditor).select();
$(window).one('click', function () {
if (me.isEditingTitle == true) {
me.toggleEditTitle();
}
});
$(me.titleInputEditor).one('click', function (e) {
e.stopPropagation();
});
$(me.titleInputEditor).one('focusout', function () {
me.toggleEditTitle();
});
});
}
}
app.html
<custom-element title="Default Text"
allow-edit-title.bind="true"
on-???????.call="saveTextFunction">
</custom-element>
Upvotes: 2
Views: 840
Reputation: 8047
Jesse's answer (EventAggregator) is the way to go if you need to pass information between two elements which are located on arbitrary positions in the app (e.g. "bubbling up" won't always work)
Schadensbegrenzer's answer will also work for your particular case, but I personally think passing a function via a bindable is more meant for dynamically passing behavior down to custom elements, rather than cross-element communication.
What I'd do in your case is actually let an event bubble up. You can accomplish this with a CustomEvent
like so:
$(me.titleInputEditor).one('focusout', function () {
// it'd be slightly cleaner to inject `Element` in your CustomElement
// and dispatch this event on that element instead
me.titleInputEditor.dispatchEvent(new CustomEvent('input-changed', {
detail: me, // if you want to pass this CustomElement instance up to the listener
bubbles: true
}));
me.toggleEditTitle();
});
<custom-element title="Default Text"
allow-edit-title.bind="true"
input-changed.delegate="saveTextFunction">
</custom-element>
Upvotes: 2
Reputation: 3622
You can use the aurelia-event-aggregator
to accomplish this. You can publish an event on your focusout
, and subscribe to that event somewhere else in your code (most likely a service somewhere) that will update your database.
custom-element.ts
import { autoinject, EventAggregator } from "aurelia-framework";
@autoinject
export class CustomElement {
constructor(private ea: EventAggregator)
...
$(me.titleInputEditor).one('focusout', function () {
me.ea.publish('input-changed', me.title);
me.toggleEditTitle();
});
...
}
service.ts
import { autoinject, EventAggregator } from "aurelia-framework";
@autoinject
export class Service {
constructor(private ea: EventAggregator)
public attached() {
this.ea.subscribe('input-changed', (value) => {
// update database using value
});
}
}
For more documentation on the event-aggregator, refer here.
Upvotes: 1
Reputation: 950
From what I see you try to bind a function. That's done with the .call binding, that's correct.
Add a @bindable attribute to your custom element and call it from the appropriate palce
@binable onUpdateChanges: Function;
updateText(){
if(this.onUpdateChanges)
this.onUpdateChanges();
}
in app.html bind it like this:
<custom-element title="Default Text"
allow-edit-title.bind="true"
on-update-changes.call="saveTextFunction()">
</custom-element>
in app.ts add the saveTextFunction
saveTextFunction(){
//Do stuff to update/save/persist something
}
in case you want to pass args you need to register onUpdateChanges in your custom-element slightly differently
custom-element.ts
updateText(){
if(this.onUpdateChanges)
this.onUpdateChanges({text: what_ever_value_you_want_to_pass});
}
app.html
<custom-element title="Default Text"
allow-edit-title.bind="true"
on-update-changes.call="saveTextFunction(text)">
</custom-element>
app.ts
saveTextFunction(text:string){
//Do stuff to update/save/persist something
}
Upvotes: 1