Element.ref binding

I have a prompt.html template

div class=“modal fade” tabindex=“-1” role=“dialog” element.ref=“modalElement”

and class
export class Prompt {
modalElement: Element = null;

If users start at the root of the site and work their way into a route, modalElement get it’s DOM value correctly. Everything works,

If they “Refresh” (big No No) the page in the browser it will fail :frowning:

So if the user hits \Site
and clicks foo \site#\foo
and picks “1” \site#\foo\1
all Works

Then browser refresh \\site#\foo\1
The Template fails as the DOM ref or modalElement is NULL

Keep in mind that “Prompt” is an outer layer element in the DOM, initially defined in app, hidden as a generic modal popup. It’s “Show” method passes the question and options. but the lack of the DOM reference after refresh the real issue.

Also… I cant confirm, but I don’t think this was an issue months (years) ago… there might have been a Aurelia V1 mod that effected this.

Any Suggestions? Many Thanks in Advance and afterwards (I can use a beer too) :wink:

Is the all code touching modalElement the same code?

(sorry for the lag) and thanks again for your assistance :slight_smile:

so… lets post the prompt class and some relevant code


  <div class="modal fade" tabindex="-1" role="dialog" element.ref="modalElement">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4 class="modal-title">${title}</h4>
        <div class="modal-body">
        <div class="modal-footer">
          <button repeat.for="button of buttons" type="button"
                  class="btn btn-${$first ? 'primary' : 'default'}"


export class Prompt {
  modalElement: Element = null;

  submit(button) {

  show(title, message, buttons) {
    this.title = title;
    this.message = message;
    this.buttons = buttons;
    let modal = $(this.modalElement);
    modal.modal({ show: true, backdrop: 'static' });
    modal.on('hide.bs.modal', () => {
    return new Promise(r => this.resolve = r);


  <require from="./resources/prompt"></require>

  <prompt view-model.ref="prompt"></prompt>



import { Prompt } from './resources/prompt';

export class App {
  public prompt: Prompt;

  bind() {
    this.container.registerInstance(Prompt, this.prompt);

Usage (ie foo.ts)

  canDeactivate() {
    if (!this.hasChanges) {
      return Promise.resolve(true);
    return this.prompt.show('Navigate', 'You have unsaved changes. Do you want to save them?', ['Save', 'Discard', 'Cancel'])
      .then(button => {
        if (button === 'Save') {
          return this.submit().then(() => true);
        return button === 'Discard';

Notice we pass in with Prompt.Show all the parameters for the Dialog and Choices. Since you can only have one modal at a time it’s sort of a singleton. Note the Bind() in app.ts which is suspicious…

Again, just don’t understand why the browser Refresh of a subpage would be different then routing click-by-click to the same point with something defined in the app.js

Also note: jDanyow initially helped me with this project and I believe this was working for many initial version of Aurelia… (Who tests refresh in a spa… :roll_eyes:)
I’ve reached out to him for help, but he’s crazy busy

Thanks again,

This looks likely to be a timing issue related to bindings of the prompt element.ref vs the router canDeactivate invocation. I’m not sure how to resolve this without having to trying it out. Maybe don’t call canDeactivate on a refresh (use a state to track whether the app has finished loading/starting or not)?

1 Like