Code Monkey home page Code Monkey logo

router's Introduction

Aurelia

License: MIT npm version CircleCI TypeScript Twitter

Backers on Open Collective Sponsors on Open Collective Discord Chat

Aurelia 2

This is the Aurelia 2 monorepo, containing core and plugin packages, examples, benchmarks, and documentation for the upcoming major version of everybody's favorite modern JavaScript framework, Aurelia.

Introduction

Aurelia is a modern, front-end JavaScript framework for building browser, mobile, and desktop applications. It focuses on aligning closely with web platform specifications, using convention over configuration, and having minimal framework intrusion. Basically, we want you to just write your code without the framework getting in your way. ๐Ÿ˜‰

Aurelia applications are built by composing a series of simple components. By convention, components are made up of a vanilla JavaScript or Typescript class, with a corresponding HTML template.

//app.js
export class App {
  welcome = "Welcome to Aurelia";

  quests = [
    "To seek the holy grail",
    "To take the ring to Mordor",
    "To rescue princess Leia"
  ];
}
<!-- app.html -->
<form>
  <label>
    <span>What is your name?</span>
    <input value.bind="name & debounce:500">
  </label>

  <label>
    <span>What is your quest?</span>
    <select value.bind="quest">
      <option></option>
      <option repeat.for="q of quests">${q}</option>
    </select>
  </label>
</form>

<p if.bind="name">${welcome}, ${name}!</p>
<p if.bind="quest">Now set forth ${quest.toLowerCase()}!</p>

This example shows you some of the powerful features of the aurelia binding syntax. To learn further, please see our documentation.

Feeling excited? Check out how to use makes to get started in the next section.

Note: Please keep in mind that Aurelia 2 is still in beta. A number of features and use cases around the public API are still untested and there will be a few more breaking changes.

Getting Started

First, ensure that you have Node.js v8.9.0 or above installed on your system. Next, using npx, a tool distributed as part of Node.js, we'll create a new Aurelia 2 app. At a command prompt, run the following command:

npx makes aurelia

This will cause npx to download the makes scaffolding tool, along with the aurelia generator, which it will use to guide you through the setup process. Once complete, you'll have a new Aurelia 2 project ready to run. For more information on Aurelia's use of makes, see here. If you aren't interested in taking our preferred approach to generating a project, you can also see the examples folder in this repo for pure JIT setups (no conventions) with various loaders and bundlers.

Documentation

You can read the documentation on Aurelia 2 here. Our new docs are currently a work-in-progress, so the most complete documentation is available in our getting started section. If you've never used Aurelia before, you'll want to begin with our Quick Start Guide.

Contributing

If you are interested in contributing to Aurelia, please see our contributor documentation for more information. You'll learn how to build the code and run tests, how best to engage in our social channels, how to submit PRs, and even how to contribute to our documentation. We welcome you and thank you in advance for joining with us in this endeavor.

Staying Up-to-Date

To keep up to date on Aurelia, please visit and subscribe to the official blog and our email list. We also invite you to follow us on twitter. If you have questions, have a look around our Discourse forum. For chat on Aurelia 2, join our new Aurelia 2 community on Discord. If you'd like to join the growing list of Aurelia sponsors, please back us on Open Collective.

License

Aurelia is MIT licensed. You can find out more and read the license document here.

router's People

Contributors

aluanhaddad avatar alxandr avatar andreykl avatar andy-scott avatar bfil avatar bigopon avatar bryanrsmith avatar cmichaelgraham avatar davismj avatar dkent600 avatar eisenbergeffect avatar fkleuver avatar gheoan avatar ixonal avatar jagonalez avatar jdanyow avatar jeroenvinke avatar jmvtrinidad avatar jods4 avatar jwahyoung avatar jwx avatar kyeotic avatar manuel-guilbault avatar micahzoltu avatar mt-pl-swf-dev avatar plwalters avatar roustalski avatar simonfox avatar strahilkazlachev avatar thanood avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

router's Issues

Dynamic Routes

What I like about this framework is ability to load JS on the fly when you need it. Very good for large application as the one I plan to create.

In my mind I have modular application. And each module has it's own set of routes. But it always start with module name. Lets say I have module acl. Then routes will be acl/rules, acl/rule/:id and so on.

So I always know where to look. I can analyze first element of the current route and know what module routes lo load. But.

  1. How can create event where I do something before any route even analyzed so I can grub route, analie and load routs of that module that supposedly have to be in acl/routes.js.
  2. How can I load and append to list of all routes before route is processed?

I think of some global main route and all the rest rotes make a children. Then in that route on canActivate() I can do something. But I am not sure if I am on the right track.

Add Arbitrary Meta To a Route

It would be great to be able to add and then access arbitrary meta such as a sub-title or icon reference without having to maintain a separate map (one that is likely to get out of sync).

activate() multiple resolvs

I cannot figure out how I would resolve few HTTP requests? I return only one right?

activate(){
  return this.http.jsonp(url).then(response => {
    this.images = response.content.items;
  });
}

But what if I need to resolve 4 http get requests before show navigation? I need to get items, categories, user parameters, ... all before display route.

generating a sitemap with the router

Hey,
I'm trying to create a sidebar with routes grouped under categories and couldn't find an immediate way to do so. The sidebar would look like:

Some Heading
   <route to page 1>
   <route to page 2>

Some Other Heading
   <route to page 3>
   <route to page 4>

When I asked this on gitter someone mentioned using nested routers, but it seems as if router.childRecognizer isn't populated until the actual route is activated. There seems to be two logical ways to represent this:

  • nested routers
  • a route config that takes a hierarchical object definition

Personally, I prefer the nested router approach, since I could easy move the definitions of my routes to sub folders where the logic for those specific "modules" exist. In either scenario it would be fantastics to be able to write e.g.

<div repeat.for="heading of router.navigation">
   ${heading}
   <li repeat.for="item of router.navigation[heading]">${item.label}</li>
</div>

Or something along those lines. Is something like this possible today?

Default viewModel with child router breaks child routes

This issue happens when the default route for the main router is a view model which has a child router.
When the default view model is loaded the default route on the child router loads. However, any other routes will not load. This can be tested by swapping the welcome and child routes in the aurelia-skeleton to set the child routes as the default. Then clicking on the child route for flikr will load the main route. If a route is declared in the child router which does not exist in the main router, a route not found exception will occur.

Support navigating to named routes

Possibly related to #4. I think it's relatively rare to actually have a URI when you need to navigate within an app, and building URIs is a pain. It would be great if the router provided a convenience method similar to router.navigate() that took a route name and a route params object and built the URI for you.

router.navigateToRoute('user-details', { userId: '123', foo: 'bar' }, { replace: true });

This would probably require a new (optional?) name property on configured route objects.

Let child routers run off empty URL fragments

As part of implementing authentication, I was trying to have a login page that looked different to the authenticated part of the application. To do this I set up a default route that went to a layout page which then had a child router that contained the other routes; for example, 'dashboard', 'orders', 'returns' etc.

In my main router configuration I had a default route and a login route:

{ route: '', moduleId: './layout', nav: false },
{ route: 'login', moduleId: './login', nav: false }

Then in the layout module I had defined a child router with a 'dashboard' route:

{ route: 'dashboard', moduleId: './dashboard', nav: true, title: 'Dashboard' }

When trying to navigate to #dashboard I was receving the following error: Route Not Found: dashboard.

I think this is a fairly common scenario and may not even be used for authentication, somebody could have a landing page that looked completely different to the rest of their site and expect something like this to work.

Perhaps the framework could check the root router for the route first, then do the child checks taking into account an empty fragment for the default route and then go through the same logic it currently uses to find child routes now and then if no route is found, it throws a "Route not found" error.

Stop injecting `Router` from having side effects

We talked about this in aurelia gitter, but injecting router should not cause side-effects. Instead configuring the router should do this. Until a router is configured, it should just delegate all method calls to it's parent.

Unconfigured child-router breaks navigation if named `router`

Here's a strange one - so I injected a grandchild Router instance into a VM but didn't configure it - I wanted it just so I could call router.navigate("childroute") on it without needing to pass a router down from parent -> view - but it didn't work.

Debugging it looks like in navigation-plan.js the plan is built for the child router even though it's not configured. This results in a route of blank "" being inspected by the navigation plan builder.

A similar issue was fixed in #34 but it looks like that fix only works (I may be wrong about this) for scenarios where you aren't calling navigate directly on an non-configured child router?

Changing the name of my grandchild router to something other than router on the VM fixes the issue, however, to prevent a difficult to debug situation it might be worth adding a fix to the navigation plan builder code:

Adding a check to see if any child router is configured in navigation-plan.js seems to fix the issue:

 if (viewPortPlan.strategy !== REPLACE && prevViewPortInstruction.childRouter && prevViewPortInstruction.childRouter.isConfigured) {

This correctly prevents the unconfigured router from contributing to the navigation pipeline. Not sure if it's the right place though - is the router supposed to delegate any calls across gaps (i.e. should a configured router delegate navigation past an unconfigured router that sits between it and another configured one?)

Allow navigation from to be executed from the app router

Currently, if you do router.navigate('foo'), it redirects you relative to that router (as it logical), but there should be some way to tell it to redirect you to foo relative to root. I suggest this can be done in two manners. One, with the options object passed to navigate (something like router.navigate('foo', {useAppRouter: true})), and router.navigate('/foo') where the slash at the beginning indicates I want it to be from the root.

I also think that the useAppRouter should take precedence over the slash start though, so that if you explicitly set useAppRouter: false, and pass in /foo it would still redirect relative to the router. I can't really picture a use-case for it, but I'm sure somebody can, and I don't really see a reason it shouldn't be supported.

Does the router support multiple 'active' views that are not in a visible view port?

have a question regarding the router and/or the page lifecycle.

I have a current Silverlight application (using Prism) that has multiple top level modules (think tabs). Each module contains it's own region and consists of a large set of business screens (30+ screens per module). Each module has a landing page with a menu as an entry point to the screens for that module.

Using these module regions we can have multiple 'active' views in the app at any one time, as switching between modules doesn't actually close an open form (the shell is essentially just showing/hiding these views). Navigating back to any form presents the data in the same state it was left in. This allows me to navigate away from an open, dirty form with data in a potentially invalid state to another module without triggering a dirty validation message. Navigating back to the original module presents whichever view was most recently active in the state it was last left in.

Is multiple active pages (on different routes) a scenario can be handled with the current router or page life cycle implementation? Maybe I would need some method of persisting the current state of a viewmodel for a screen that perhaps falls outside the traditional activate and deactivate events?

Perhaps there could be additional hooks into the lifecycle that listen to navigation events and respond differently to different to different navigation types: modifying URL manually triggers dirty check, clicking in-app back button triggers dirty check, changing module does not etc.)

The attached images approximate what I'm attempting to say. http://myapp/module1/view-1 is in a dirty state, but I can still navigate to module 2 and create/destroy any of the views in module 2. Navigating back to module 1 shows view-1 in the dirty state it was left in.

mod1-home
mod-1-view-1
mod-2-home
mod-2-view-2

canDeactivate() is triggered twice

canDeactivate() is triggered twice when you navigate to another route where canActivate returns new Redirect('...').

Is there any workaround?

authorize pipeline step should occur before instantiating the view.

As a developer, I would like certain parts of my application to be able to make assumptions about authentication and let the navigation pipeline deal with ensuring authentication. In order to do that however, the authorize pipeline event must evaluate before instantiating the view, otherwise it is possible for my views to be instantiated for unathorized users.

This problem is compounded by the fact that if an exception is thrown from a view constructor, all routing fails.

Currently, if I have a class hooked up to the authorize pipeline step that always redirects, the view that the route would have routed to is still instantiated every time.

router.configure(config => {
    config.addPipelineStep('authorize', AuthorizeStep);
    config.map([
        { route: '', moduleId: './logged-in' },
        { route: 'login', moduleId: './login' }
    ]);
}
export class AuthorizeStep {
    static inject() { return []; }
    constructor() {}
    run (routingContext, next) {
        if (routingContext.nextInstruction[0].config.route == 'login')
            return next();
        return next.cancel(new Redirect('login'));
    }
}
export class LoggedIn {
    constructor() {
        console.log('This should be unreachable, but it is!');
    }
}

The use case I currently have for this is in the constructor of my VM, I do some things that require authorization. The authorize pipeline step handler is setup to ensure that the user will never see the page if they are not authorized. However, the VM is constructed before the authorize pipeline step is executed which means logic in the constructor will fail.

I can, of course, work around this by moving my logic out of the constructor but this is not only counter intuitive, but it also means my class's complexity goes up because I can no longer assume that the class is always instantiated in a state that is reasonable for the VM (authenticated).

As a side note, it also seems wasteful to construct the VM if it isn't going to be used, especially since a VM constructor may do expensive operations that will simply be thrown away as soon as the authorize step fails.

Reusing view models in routes doesn't call activate

I'm trying to reuse a view model that switches behavior based on the settings passed in router.

When navigating between the two routes that reuse the same view model, however, it does not call activate twice, so I can't get the metadata for subsequent routes.

Specifically for my use case, I'm trying to create a routable view model that takes some model from an API and allows someone to perform CRUDL operations on it - like an admin view. In this case, each model has its own route, but those all route to the same view model.

Thanks

Support lazy document.title updates

I often want to update the document.title after navigation when data loads in a VM. For example, if I have a blog app with route posts/:id that routes to a Posts view model, I'll want to fetch the post from the server, and the update the document title with the post's title. It looks like right now the document title is calculated and committed in NavigationContext.CommitChangesStep. It would be great to split that logic out a bit so it could be easily run on demand from a router consumer, or easily customized if someone doesn't like the way titles are built by default.

Router refactoring

I named this issue rather ambiguously because it will be covering a few points that I find somewhat hard to take apart, or name properly.

Currently, when you call router.navigate it does all of the work, and builds a instruction that is sent through the router pipeline. The Redirect navigation command is a simple wrapper around this call.

I propose inverting this relationship, and renaming Redirect to NavigateCommand, and adding a new method to the router named process, that would work as following:

The router.navigate would be turned into a simple convenience method, simply calling

navigate(url, opts) {
  var cmd = new NavigateCommand(url, opts, this);
  this.process(cmd);
}

The process command would simply forward the call to the App Router, which in turn would invoke

process(navigationCommand) {
  navigationCommand.navigate(this);
}

And the functionality that is currently in the navigate method would be moved to the NavigateCommand class instead. This would enable one to create their own navigation commands, that did other things (and could even extend NavigateCommand to make things such as NavigateToLoginCommand that handled capturing return address etc.).

Add `redirect` support to the router

Allow the router to recognize redirect keys in routes, and perform the redirect action.

Example:

config.map([
  { route: 'blog',      moduleId: './blog/routes/index', nav: true, title: 'Blog' },
  { route: '',          redirect: '/blog' }
]);

This would make the empty route '' actually redirect to #/blog (with path changes). This could be added quite early to the router pipeline, to reduce the overhead of having to go through the entire pipeline.

Exposing parent router can cause infinite loop

Consider this class

import {Router} from 'aurelia-router';
import {Parent} from 'aurelia-framework';

export class Home {
  static inject() { 
    debugger;
    return [Router]; 
  }
  constructor(router){
    this.router = router;
  }
}

This might be done if using the router for navigation methods, instead of as a state controller. This will cause an infinite loop, as the route-loader finds the exposed router property, and creates a child navigation context. If this class is the default route, the child will be itself.

Nested states and nested views

This week my team and I finished big project in Durandal and see some shortcomings with router and view composition that angular's ui-router's nested states and nested views would would solve.

Angular 2 is going in wrong direction by resigning from two way databinding, so the only choice is to try to convince you to implement nested states and view like ui-router does :).

Any luck implementing this?

Two redirects in router and child router

I saw strange behaviour of aurelia router when i was using router with one child router and both of them use redirect parameter in router configuration. I have prepared forked repository of skeleton-navigation repository where my problem is implemented:
https://github.com/milunka/redirect-problem

For quick review, this is what i am talking about:
app.js - this viewmodel has defined only one route where empty route is redirecting to defined route

import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';

@inject(Router)
export class App {
  constructor(router) {
    this.router = router;
    this.router.configure(config => {
      config.title = 'Aurelia';
      config.map([
        { route: 'child',       moduleId: './appChild',      nav: true, title:'Welcome' },
        { route: '', redirect: 'child' }
      ]);
    });
  }
}

appChild.js - in this viewmodel is again empty route which redirects to defined route

import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';

@inject(Router)
export class AppChild {
  constructor(router) {
    this.router = router;
    this.router.configure(config => {
      config.title = 'Aurelia';
      config.map([
        { route: 'welcome',       moduleId: './welcome',      nav: true, title:'Welcome' },
        { route: '', redirect: 'welcome' }
      ]);
    });
  }
}

After i try to access application root (http://localhost:9000) i receive error Illegal module name "", same error is displayed when i try to access route after first redirect (http://localhost:9000/#child). Only url which works is whole route to welcome page (http://localhost:9000/#child/welcome).

I think that both url (http://localhost:9000 and http://localhost:9000/#child) may works, but maybe i just didn't understand redirect parametr in router configuration.

config.mapUnknownRoutes and views

I want to use

      config.mapUnknownRoutes(instruction => {
        instruction.config.moduleId = '/en/main-router';
        instruction.config.view = 'main-router.html';
        return instruction;
      });

but it is searching for '/en/main-router.html' view.

Any idea?

Discussion: Correct time in the pipeline to construct a ViewModel.

From a "principle of least surprise" point of view, I believe that the navigation current pipeline is more confusing than it should be.

Note: The current system works. Someone who already understands it is able to do whatever they want as long as they are careful and remember the pipeline ordering. This discussion is purely about user expectations and making the API easy to understand and do what the user most expects.

As a user, if I have a view and a view model, my expectation is that the view model will only be created if it is needed. This is supported by the current API for a view model that contains constructor, canActivate, activate, canDeactivate, deactivate. Since I know (from other documentation/experience) that VMs are created for every activation, this leads me to believe that the lifecycle for a view goes something like:

  1. constructor
  2. canActivate
    1. activate
    2. canDeactivate
      1. deactivate

Current

I believe the current system looks like this:

  1. resolveRoute
  2. canDeactivate (old VM)
  3. constructor (new VM)
  4. authorize
  5. modelbind (???)
  6. canActivate (new VM)
  7. deactivate (old VM)
  8. activate (new VM)
  9. precommit (???)
  10. commit (???)

Proposed

I believe a more intuitive flow would be:

  1. resolveRoute
  2. authorize
  3. canDeactivate (old VM)
  4. canActivate (new VM)
  5. deactivate (old VM)
  6. constructor (new VM)
  7. activate (new VM)

Reasoning

Route resolution comes first because almost every other step requires knowing your destination.

authorize comes next because it is likely to change the route, and as mentioned previously many other steps may depend on knowing what the upcoming route is. For example, canDeactivate may return different results depending on what the next route is. Maybe you can navigate away from A to B but you can't navigate away from A to C.

canDeactivate and canActivate are called back to back because they are effectively guard clauses on the pipeline. If either returns false, no more work should be done and many of the other steps don't make sense. What does it mean to construct a view that is never activated? What if my view construction is expensive (blocks on server round-trip)? As a user, my expectations are that construction will occur when the view is actually needed, not before it is needed.

deactivate comes next because at this point we have asserted that we are navigating. It is possible that an error could occur but I believe being redirected to an error page is more appropriate at this point than staying on the current page and having the button appear to do nothing (from a user's point of view).

constructor is next because again, at this point we have asserted that we are moving on to the next page and it is time to get the view model all setup.

activate comes next because it is really a second constructor. In this proposed model I actually see little benefit to having activate and constructor. Seems like you could get rid of activate in preference for a well established pattern (construction).

Difficulties

There are of course some technical difficulties here. The primary one that @EisenbergEffect brought up to me earlier is that canActivate is a member of the VM which means the VM must be constructed first. I believe this is a mistake for the reasons mentioned above. I would much prefer to see a decorator like

@canActivateClass(MyViewCanActivateChecker)
export class MyView { }

This model allows users to check for activatability of their class before the class is constructed. This keeps the semantics of what a constructor does and when it is called in line with traditional OO patterns. The disadvantage here is if your canActivate does some expensive work that you don't want to have to repeat. Perhaps canActivate returns an object that will be dependency injected into the constructor or falsy otherwise?

The other problem is the behavior when the constructor fails. By the time the constructor fails, you have already deactivated the previous page so now you are in a state where you can't load the next page. Currently, if activate fails the user has a constructed but not-activated VM. This implies that unless activation occurs before deactivation, there needs to be a way to redirect the user to an error page when there is a failure between deactivate and activate. The above proposed change would only increase the number of methods that could (constructor and activate) result in such an error page being hit.

Change routes titles in activate method

Hi,

I have a dynamic route without title, but I'd like to set the title after loading the displayed resource. So I've tried this :

In parent component's router :

{
   route: [':id'],
  moduleId: 'accounts/account',
  nav: false,
  title: ''
}

And in accounts/account controller :

canActivate (params, queryString, routeConfig) {
  return new P((resolve, reject) => {
    this.AccountsService
      .getById(params.id)
      .then(account => {
        this.account = account;
        routeConfig.title = account.name;
        console.log(routeConfig);
        resolve(true);
      })
      .catch(() => reject(false));
  });
}

In console, routeConfig.title is well set, but document.title does not change :(

An easier way to hide and show nav items using the router

I have my main routes in app.js and I wanted a way to hide and show these from the nav based on certain criteria that would change over time (an example of this could be a login/register route which shows in the nav when logged in, but then not shown when you are logged in). When setting up the routes it would be great if the value of nav: boolean could be computed from a function - which is more than likely to be in the same file - (something which you can obviously already do) and then when you call router.refreshNavigation, this function be evaluated again and the value of nav change which would then reflect on the UI. I have a workaround for this in my app but I am not entirely happy with the implementation (using the additional settings you can supply with the router).

Passing data for bindings via route model.

I was trying to add some additional attributes to the route model so they could be exposed in the HTML bindings.

Here is a simple example:

{
  route: ['', 'home'],
  moduleId: 'home',
  styles: 'fa-home',
  nav: true
}
<a href.bind="item.href" class="fa ${item.config.styles}"></a>

It at first I tried to access the styles attribute via the route model directly in the bindings like item.styles. That didn't work and after looking through the code I found that the config is saved as part of the route model under the config key.

There should be some way to pass custom application data via a route config to make it easier to customize the route bindings. Maybe a route model attribute of data could be reserved for application specific route metadata.

bubble rejected activation promises

It would be nice if there was a way to give the router a handler for activation promise rejections. This would reduce boilerplate error handling code in activate methods.

Code like this:

public activate(...): Q.Promise<any> {
    var deferred = Q.defer();

    doSomething()
        .then(() => deferred.resolve())  // yay
        .fail(reason => {
            deferred.reject(reason);     // cancel activation.
            return Q.reject(reason);     // "rethrow"
        })
        .done();  // terminate the promise.  ensure error bubbles up to the unhandled exception handler  (Q.onerror).

        return deferred.promise;
}

Could be simplified to something like this:

<hook error handler to router>

public activate(...): Q.Promise<any> {
    return doSomething();
}

Add Router events like in Durandal

I liked the emitted events from the Durandal Router, as in certain situations, especially for E2E tests, those were quite helpful to understand what the current state of page routing is.

Just for the sake of completeness I'll post them here again:

  • router:navigation:complete
  • router:navigation:cancelled
  • router:navigation:processing
  • router:route:activating
  • router:route:before-config
  • router:route:after-config
  • router:navigation:attached
  • router:navigation:composition-complete
  • router:route:not-found

In case of E2E tests I'd be specially interested in the router:navigation:complete and router:navigation:composition-complete event, as they would allow to work easier with scenarios like Page transitions and dynamic composition.

Fail to use multilevel path in System.baseUrl

Hi!

If I try to use multilevel directory in System.baseUrl for having my templates and js files being stored several levels deep from the root, I got an error in console and nothing works. With this:

    System.baseUrl = '/assets/app';
    System.import('aurelia-bootstrapper');

and

<body aurelia-app="test">

I got an error: Uncaught TypeError: Illegal module name "/assets/app/test"

If I do this modification:

//    System.baseUrl = 'assets';
    System.config({
        "baseURL": "/assets/app/"
    });
    System.import('aurelia-bootstrapper');

/assets/app/test.js file is loaded, but /assets/app/test.html one is not. In console network tab i see browser trying to load /test.html

Tried using <base href="/assets/app" > as @goranobradovic suggested in gitter, but with no luck.
It may be not an issue of aurelia itself, but I actually cannot tell what's responsible, so please report this elsewhere if required.

Thanks!

specify Viewmodel at runtime

It would be great if we could decide at runtime which moduleId to load, based on whatever condition is provided. The current route entry looks like this where you have to provide the moduleId as a string during configuration phase.

{ route: ['','welcome'],  moduleId: 'welcome',      nav: true, title:'Welcome' },

Lets say though the welcome screen shows a dashboard for all users (incl. anonymous), but as soon as you login you get a different screen.

So same route path but different model implementation, as of separation of concerns. So in this case it would be helpful if the moduleId could accept a function.
As of the gitter discussion it was proposed to introduce another property, e.g. moduleStrategy, which if set would override the moduleId definition. so an example would be

{ route: ['','welcome'],  moduleStrategy: (options) => { 
  if(options.whatever) {
    return  "welcome";
  } else {
    return "welcome2";
  }
 }, nav: true, title:'Welcome' },

Perhaps even make moduleStrategy accept a Promise, which is resolved later on

Child Route Throws Route Not Found

I'm trying to create a child route in a PoC app that I'm building for my company. I have parent routes setup in the app.js file:

import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

@inject(Router)
export class App {
    constructor(router) {
        this.router = router;
        this.router.configure(config => {
            config.title = 'Parent';
            config.map([
                { route: ['', 'welcome'], moduleId: './welcome', nav: true, title: 'Welcome' },
                { route: 'accounts', moduleId: './accounts', nav: true }
            ]);
        });
    }
}

The accounts view model contains a router that maps a child route.

import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
import {HttpClient} from 'aurelia-http-client';

@inject(HttpClient, Router)
export class Accounts {
    heading = 'Accounts';
    accounts = [];
    url = 'http://purple/service/accounts';

    constructor(http, router) {
        this.http = http;
        this.router = router;
        this.router.configure(config => {
            config.map([
                { route: 'vehicles', moduleId: './vehicles' }
            ]);
        });
    }

    activate(){
        return this.http.get(this.url).then(response => {
            this.accounts = response.content;
        });
    }
}

This is my first go at creating a nested view using aurelia, but the way I understand it is that since the 'vehicles' route is setup inside the accounts view model then the vehicles view should be inserted into the accounts view tag.

But when I navigate to the Accounts state, I get the following error in the console 'Route Not Found: '. As I looked through the source code for router, I noticed that this error is also supposed to log the url. In this instance the url is an empty string.

Here is the html for the accounts view:

<template>
    <section>
        <h2>${heading}</h2>
        <div class="row">
            <div class="col-md-2">
                <ul class="well nav nav-pills nav-stacked">
                    <li repeat.for="account of accounts" if.bind="account.Display">
                        <a href.bind="vehicles/account.Id">${account.Display}</a>
                    </li>
                </ul>                     
            </div>
            <div class="cold-md-10">
                <router-view></router-view>
            </div>
        </div>
    </section>
</template>

Thanks for the help.

better documentation of absolute vs relative module ids

starting at the examples i got bitten by just using a absolute module id in a router for a app that was actually being renamed

it took a while to figure i need to use ./about instead of about as module id

the matter was worse, because i serve a spa shell at all non-static non-api locations, and all i got was a strange syntaxerror

redirect route doesn't work.

In the previous version of Aurelia I was able to do

{ route: '', redirect: 'about' },

After updating, that not only doesn't redirect, but it causes my router to fail to initialize, taking down my entire application.

Uncaught Error: Error instantiating App.
------------------------------------------------
inner error: Error: Invalid route config.
    at validateRouteConfig (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router.js:9:13)
    at AppRouter.addRoute (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router.js:220:13)
    at RouterConfiguration.configureRoute (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router-configuration.js:142:20)
    at Array.<anonymous> (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router-configuration.js:88:23)
    at RouterConfiguration.exportToRouter (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router-configuration.js:110:30)
    at AppRouter.configure (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/router.js:99:22)
    at new App (http://localhost:9000/output/app.js:26:18)
    at Object.reflect.construct (http://localhost:9000/jspm_packages/github/zloirock/[email protected]/modules/es6.reflect.js:81:26)
    at ClassActivator.invoke (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/metadata.js:259:28)
    at Container.invoke (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/container.js:211:37)
"aurelia-bootstrapper": "github:aurelia/bootstrapper@^0.11.0",
"aurelia-dependency-injection": "github:aurelia/dependency-injection@^0.6.0",
"aurelia-framework": "github:aurelia/framework@^0.10.0",
"aurelia-http-client": "github:aurelia/http-client@^0.7.0",
"aurelia-router": "github:aurelia/router@^0.7.1",

Route Helper: Expose current route to view

Feature request for a route.current method or something similar to be used within a view. Currently, relative hyperlinks inside a child view do not respect parent/child router relationships (for good reason) but this breaks encapsulation.

Use cases for this include nested views and viewmodels where relative links would update the view relative to its parent (rather than relative to the app).

ViewModels Singleton Instance

When navigating between views the viewmodel always gets instantiated. Need a way to make view models singletons to preserve state when a user navigates various routes. This will give the app more of a desktop feel.

The documentation currently states to use Metadata.singleton() but this does not work either.

canActivate = false doesn't change the url back to the previous route

Having a module with canActivate() { return false; } does prevent the view from being displayed, however, the URL in the browser still retains the address of the "illegal" route.

Sample repo that causes this error: https://github.com/Alxandr/potential-aurelia-bug/blob/master/dist/admin/_common.js#L7

This clearly shows the problem, because multiple attempts at clicking the link to the welcome route, should have succeeded, but canActivate is only run once, and the URL isn't changed back to it's original one.

router.navigation includes invalid hrefs when using nav:true on a dynamic route

If you fail to specify an href on a route using nav: true, the default is to copy the route pattern, which may include dynamic segments. This will result in invalid hrefs being included in navigation links.

Eg, changing this skeleton-navigation route to

{ route: 'flickr/:tag', moduleId: './flickr', nav: true }

will result in <a href="/flickr/:tag">Flickr</a> in the skeleton's navigation menu.

I think the router should protect users from such mistakes, and ideally never return a bad href. We could...

  1. Validate the route config and throw an error if nav: true appears on a dynamic route,
  2. Silently ignore the nav: true on dynamic routes,
  3. Maybe try to run .generate() on the navigation routes using the current or parent context? This is probably too error-prone to use.

click.delegate from within the repeat.for loop does not find a function

I have an array of Bootstrap Alerts and try to delegate the click event, to close the clicked Alert.

The first example below works but within the repeat.for loop, the error is triggered:

Uncaught Error: closeAlert is not a function                    ast.js:58
ensureFunctionFromMap                                           ast.js:58
evaluate                                                        ast.js:573
(anonymous function)                                            listener-expression.js:59
execute._prototypeProperties.createDirectEventCallback.value     event-manager.js:61

View

<!-- _todo: click.delegate works here but not in a repeat loop below, exception raised: closeAlert us not a function-->
<div class="alert alert-warning alert-dismissible" role="alert" click.delegate="closeAlert(0)">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
      <strong>${Alerts[0].type}</strong> ${Alerts[0].msg}.
</div>

<div repeat.for="alert of Alerts" class="alert alert-${alert.type} alert-dismissible" role="alert" click.delegate="closeAlert(0)">
      <button type="button" aria-label="Close"  class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span></button>
        <strong>${alert.type}</strong> ${alert.msg}
</div>

Model

import {HttpClient} from 'aurelia-http-client';

export class Login{
  static inject() { return [HttpClient]; }
  constructor(http){
    this.heading = 'AlertTest;
    this.http = http;

    this.Alerts = [];
    this.addAlert({type: 'warning', msg: 'Close me'});
  }

  addAlert (alert) {
    this.Alerts.push(alert);
  }

  closeAlert (index) {
    alert(index);
    this.Alerts.splice(index, 1);
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.