Reflect open dialog in URL


#1

Hey there,

I’m asking myself how to reflect an open dialog in the url?
So for instance I navigate to /foo where I got a list of elements which can open an dialog on click.
When the dialog is open I want to have the URL /foo/123123

There are multiple ways to do this.
I guess I would prefer to create a ViewModel which has an canActivate function which returns a promise of the opened dialog.

class DialogView {
  
  static inject = [DialogService];
  
  constructor(dialog) {
    this.dialog = dialog;
  }
  
  canActivate(params) {
    // A rejection is required to tell the router to don't stay on this page
    return this.dialog.open(...).then(Promise.reject());
  }
}

The problem here is when you send this URL to someone and he closes the dialog -> where to get back in history because of not activatable view model.

Another way could be to don’t rely on the router directly but replace the URL programmatically.
This is fact has the trade off that the URL can’t open anything.

Doe you have any Ideas which would be the best way?


#2

I do a similar thing where I reflect the active tab in the query string, but all tabs are really on the same view. In the activate method I determine which tab should be active based on the query string, and then later do some show/hide stuff (which you could use to show the dialog).

Check that you haven’t changed the activation strategy (or you’ll load a new instance of the page instead of just changing the query string). This can be set in various places, such as the view-model’s determineActivationStrategy(); the router, etc. but if you haven’t changed it, it’s probably fine!

Secondly, use the activate or attached method to show the dialog initially, but you don’t have to return a promise from activate. Just decide what to do manually with .whenClosed.

activate(params) {
  if (params.dialog == "123123") {
    // open the 123123 dialog
    .whenClosed(() => {
      // stay on this page? navigate away?
    });
  }
}

If there is no dialog to display (i.e. the foo list page), when the user clicks the link /foo/123123, you could open the dialog ‘123123’ in a click delegate. You also need to “save” the dialog into the query string (for a later activate method to use). This won’t actually reload the page if you’re already on “foo”, it will just update the query string for you:

  this.router.navigateToRoute("foo", { dialog: "123123" }, { replace: true });

I can’t find the doc link, but I recall that “replace” is used to replace the item in history instead of adding a new one. Useful if you don’t want the back button to go through opening previous dialogs.

Hope that helps!


#3

So I did it as I wrote first.
I created a child route to a new view model. This view model got an canActivate function which opens the dialog if possible. It returns a promise which returns a redirect to the previous route or the parent view model if no previous route is available.

import {Redirect} from 'aurelia-router';
import {Entity} from 'app/model/entity';
import {DetailsDialog} from 'app/view/detail/dialog';
import {showDialog} from 'app/util/dialog';

export class Details {

  /**
   * Show the Entity Detail dialog
   * @param {string} code
   * @param {Object} route
   * @param {NavigationInstruction} instruction
   * @returns {Promise}
   */
  canActivate({code}, route, instruction) {
    let previousUrl = '';
    if (instruction.previousInstruction) {
      previousUrl = instruction.router.generate(
        instruction.previousInstruction.config.name,
        Object.assign({}, instruction.previousInstruction.params, instruction.previousInstruction.queryParams)
      );
    }
    return Entity.get(code).then(entity => {
      if (entity) {
        return showDialog(DetailsDialog, entity).then(() =>
          new Redirect(previousUrl)
        );
      }
      return new Redirect(previousUrl);
    });
  }
}

#4

Can you put the solution on gist to see how this work in practise?


#5

Here is a simple example