-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
router #137
Comments
https://jsfiddle.net/fend25/e1LLu72c/ guys, feel free to comment) |
Haha, thought yesterday how great it would be having some views bound to routes. :D A good basic router implementation would be this: My approach would use regexes as parameter of a directive: <div al-router-view="/main/?">
<a href="/user/1">User 1</a>
</div>
<div al-router-view="/user/:id/?">
<a href="/main">Back to overview</a>
</div> This would allow to define the urls directly in the template, no additional code would be needed for basic features. But there should definitivly be an API to do redirects, handle url errors etc. |
Oleg's reference implementation (ancient one) |
Ok, i just build an absolute simple, quick n dirty views implementation: Some explanation: the al-router-view directive could be extended to watch the expression. <div al-router-view="myVar.main">Main</div>
<div al-router-view="myVar.user">User</div> scope.myVar = {
main: '/main',
user: '/user'
} Comments are welcome. |
I think it should be separated into two different API's:
The first should have unified interface to change current location and update/rewrite state. Also it should be presented in root scope and should broadcast events. Events could be stopped to prevent page reloading when there is unsaved changes or so. View should allow to use functions as route value. This functions return async operations to load template and resource specified by route. This is needed to render state dependent routes. By default view listening scope's <al-app>
<al-view al-view.routes = "routes"/>
</al-app> alight.bootstrap('al-app', {
routes : {
// Simple template route
'/': 'index.html',
// State dependent route
'/home': function(route, scope){
if (scope.user) {
return {
scope: {
user: user
},
template: '/users/home.html'
}
} else {
return '/sign-in.html'
}
},
// Async route
'/users/:id' : route => {
var user = users.get(route.id);
var promise;
if (! user) {
promise = loadUser(route.id).then(user => {
if (! user) {
// User not found, show 404 error page
return '/errors/404.html';
} else {
// User found, use user page template
return {
scope: {
user
},
template: '/users/item.html'
}
}
});
} else {
promise = Promise.resolve(user);
}
return promise;
}
}
}); Also view could has |
@rumkin Can I use common header and footer for "/users/*" in your approach.
It's
scope.onSwtichToUser = route => {
var user = users.get(route.id).then
var promise;
if (! user) {
promise = loadUser(route.id).then(user => {
if (! user) {
// User not found, show 404 error page
return '/errors/404.html';
} else {
// User found, use user page template
return {
scope: {
user
},
template: '/users/item.html'
}
}
});
} else {
promise = Promise.resolve(user);
}
return promise;
} What do you think? "Configuring" in html can be easier. |
@lega911 Yep, it could be done with several view elements. Like this: <al-view al-view-routes="routes.head">
<al-view al-view-routes="routes.body">
<al-view al-view-routes="routes.footer"> It's not so elegant as HTML configuration but it simply to manage from code. I think configuring in HTML is the most proper way to do this for MVVM app. But it should be also configurable and controllable from code.
|
I think it's better to configure router with html and implement fully controllable router and view interfaces with focus on usability. |
Yes, but we can make it like this (with the event directive): and "on-lose-focus" is an event (new CustomEvent), which you can catch even with jQuery. #123 |
Maybe it's better to avoid
|
Maybe, but custom events can be more flexible. |
May it possible to implement url handling like express.js? My approach uses a global routes object, where regexes are defined. <div id="home"></div>
<div id="players"></div>
<div al-route="/users/:user"></div> alight.routes = {
'/home': (scope) ->
return '#home'
'/players/:id': (scope, id) ->
scope.id = id
return '#players'
}
alight.d.al.route = (scope, exp, element, env) ->
alight.routes[exp] = ->
return element What do you think? |
I agree
should it work like al-if with dependency on url? |
What about this one? <div>
<div al-route:group @switch-to="onSwtichToAuth()">
<div al-route="/login" al-ctrl="App"/>
<div al-route="/register" al-ctrl="App"/>
</div>
<div al-route:group @switch-to="onSwtichToApp()" @lose-focus="showPreventWindow()">
<Header/>
<div al-route="/posts" al-ctrl="Posts"/>
<div al-route="/feed" al-ctrl="Feed"/>
<div al-route="/user">
<div al-ctrl="userList"></div>
<div al-route="/user/:userId" al-ctrl="userItem" @lose-focus="checkIfUserSaved()">
<div al-if="!user">Loading...</div>
<div al-if="user" al-include="/templates/userItem.html"></div>
</div>
</div>
<div al-route="*">
No page 404
</div>
<Footer/>
</div>
</div> when url = "/user/:userId", then controller so, visible html for /user/:userid would be <div>
<div al-route:group @switch-to="onSwtichToApp()" @lose-focus="showPreventWindow()">
<Header/>
<div al-route="/user">
<div al-ctrl="userList"></div>
<div al-route="/user/:userId" al-ctrl="userItem" @lose-focus="checkIfUserSaved()">
<div al-if="!user">Loading...</div>
<div al-if="user" al-include="/templates/userItem.html"></div>
</div>
</div>
<Footer/>
</div>
</div> visible html for /user would be <div>
<div al-route:group @switch-to="onSwtichToApp()" @lose-focus="showPreventWindow()">
<Header/>
<div al-route="/user">
<div al-ctrl="userList"></div>
</div>
<Footer/>
</div>
</div> visible html for /login would be <div>
<div al-route:group @switch-to="onSwtichToAuth()">
<div al-route="/login" al-ctrl="App"/>
</div>
</div> |
I agree. It looks exciting. And it should to allow to load subroutes with al-include for complex components. |
@rumkin al-include is deprecated, look at http://angular-light.readthedocs.org/en/latest/directive/html.html , it's more flexible :) |
@lega911 I will. But you're using al-include in your example. Let's focus on modular structure, easy of use and flexibility. And thus it should to use al-html to load subroutes. |
Yes, you have a pretty good understanding of what i mean. :-)
I do not understand how this works. What i also thought about are subroutes: <div al-route="/user">
<div al-route="/:id"></div>
<div al-route="/details"></div>
</div> Which would be matched when accessing: Maybe it would be great to also handle redirects in the global route object: alight.routes = {
'/home': '/'
'/players/:id': (scope, id) ->
scope.id = id
return '#players'
} which would redirect "/home" to "/". |
It's easy, one rule - if there is a route that maches to url, then it should be visible, then all parent DOM should be visible, other route-elements should be hidden. <div al-route>
<header />
<div al-route="/profile"></div>
<div al-route="/user/:id/view"></div>
<div al-route="/user/details"></div>
<footer />
</div> for url "/user/details" we will have: <div al-route>
<header />
<div al-route="/user/details"></div>
<footer />
</div> In your approach we have to copy-past header and footer for "/profile". |
Hm. This is a bit confusing for me. <div al-route="/profile">
<h1>My user profile</h1>
</div> So when accessing /profile i will see this: <header />
<div al-route="/profile">
<h1>My user profile</h1>
</div>
<footer/> Am i right? |
Yes. <div>
<header />
<div al-route="/profile"></div>
<div al-route="/user/:id/view"></div>
<div al-route="/user/details"></div>
<footer />
</div> In a simple, "group route" just holds all child routes, like |
Not exactly, it renders all DOM, except routes that don't match to URL |
I like the approach. :-) |
Prototype: http://plnkr.co/edit/lZkXWXg5xk1J147nb1jX with no regex yet. |
There is an regex router with params and tail capturing: |
I took it for my example |
You can get arguments using event <div al-route="/user/:name/view" @route-to="username=$event.value.name"> |
One more example: http://plnkr.co/edit/pQBycumCKE1DJb3oVaPE?p=preview
<div al-route="*">
404 - Not found
</div> |
Why not providing these variables automatically in a scope variable? scope.$route.name Parsing from URL shouldn't be so difficult: "/user/:id/name/:name/view/:action".match(/:\w+/g) |
It can give you collisions for routes "/users/:name", "/users/root" they both will be visible on url="/users/root" |
Hm, what about this: One defines his application e.g. /users/ <div al-route="/users/:id"></div>
<div al-route="/users/:id/social"></div>
<div al-route="/users/detail/:id/edit"></div>
<div al-route="/users">CatchAll for /users instead of al-route:default, e.g. when one removes paramters from the url</div>
<a href="/login">Redirects to a page /login</a>
<a href="/groups">Redirects to a page /groups</a> This would be definitively more flexible than handling urls by using an extra directive. <img @click="alight.go(/users/1234)" /> This would give more flexibility than a directive, because the directive is bound to a specific event, e.g. click event. But what when a view should be shown when something is hovered? <img @mouseover="alight.go(/image/1234/large)" />
<div al-route="/image/:id/large">
<img src="/resources/images/{{$route.id}}-large.jpg" />
</div> This is a simple preview box of a large image, when the user hovers over a thumbnail image. |
Better API proposal: <img @click="$route.go(/users/1234)" /> |
Because template directives are all about one thing - inserting templates and I can reuse the code, no double code.
a good idea, it's done <a @click="$route.go('/user/ubuntu/view')" href>User Ubuntu</a> <br/> But you have a limitation here - you should use al-ctrl if you want to have "scope.$route" |
I can place $route in every scope, but I think it's can be bothersome e.g. my timepicker component doesn't need it. |
"/user" - is a simple route, need a key-symbol, maybe "/user/*"? |
Nope, i explicitly mean /user. |
It seems we talk about different things, your route "/user" works. |
No, we talk about the same. What do you think about making the al-link directive as additional attribute, e.g. like: <a href="/user/1234/detail" al-link>User 1234</a> If al-link has no parameters, it tries to fetch the link from href attribute or from action attribute (for form elements) or from src attributes (for images). Is this a compromise? :-) |
I don't like this mark-way, what if I want "/user" as usuall route, it has no sign for this. <div al-route="/users/:id"></div>
<div al-route="/users/:id/social"></div>
<div al-route="/users/detail/:id/edit"></div>
<div al-route:catch="/users**">CatchAll for /users #1</div>
<div al-route:catch="/users**">CatchAll for /users #2</div>
<div al-route:catch="/**">CatchAll #3</div> While al-route works in parallel (a few routes can be visible), al-route:catch depends on an order - only first al-route:catch will be visible, so for "/users/wrong/link/here" You can propose another sign for this |
You can do this. The idea is NOT to define a DEFAULT route using a special directive or directive modifier. <div al-route="/users/details/:id"></div>
<div al-route="/users/details/:id/social"></div>
<div al-route="/users/details/:id/edit"></div>
<div al-route="/users">This is your /users route</div>
<div al-route="/users/.*">Catch, when no other route matches /users/(.*)</div> Another example: <div al-route="/community/users/:id"></div>
<div al-route="/community/users/:id/social"></div>
<div al-route="/community/users/detail/:id/edit"></div>
<div al-route="/community/:anything">This is the catcher, anything matches anything except users/*</div> In this example any parameter, that does not match a given route, needs to be handled after other urls. We only need to define a processing priority, to ensure, that urls containing parameters are handled last. This can be done when adding new routes to the global routes object.
I understand, what you like to do, but we do not need a directive modifier for this behaviour. |
It doesn't work now, because all routes work in parallel, e.g. if url = "/users/details/id123" then this DOM will be visible: <div al-route="/users/details/:id"></div>
<div al-route="/users/.*">Catch, when no other route matches /users/(.*)</div>
<div al-route="/users/details/details/:id"></div>
<div al-route="/users/details/:id/details"></div> which one should be a last? (what if url = "/users/details/details/details")?
We have two modes for routes: parallel and sequence, and we need to know which where. <div al-route="/users/root"></div>
<div al-route:catch="/users/props/.*">props</div>
<div al-route:catch="/users/.*">Catch, when no other route matches /users/(.*)</div>
<div al-route:catch="/users/info/.*">info</div> <!-- similar to props -->
<div al-route="/users/details/:id"></div>
<div al-route="/users/:id"></div> For url="/users/root", result DOM: (It's Important case - two blocks are visible.) <div al-route="/users/root"></div>
<div al-route="/users/:id"></div> <!-- Double of route --> url="/users/details/id123", result DOM: <div al-route="/users/details/:id"></div> url="/users/props/id123", result DOM: <div al-route:catch="/users/props/.*">props</div> url="/users/info/id123", result DOM: <div al-route:catch="/users/.*">Catch, when no other route matches /users/(.*)</div> It's Important case, you have What is your Skype? (mine: lega911), it will be easier ;) |
/users/details/:id Why? Because static defined routes are more specific than routes containing a variable.
Your html: <div al-route:catch="/users/.*">Catch, when no other route matches /users/(.*)</div>
<div al-route:catch="/users/info/.*">info</div> <!-- similar to props --> First We need to sort segments by length and we need to resolve position of all dynamic segments (variables like
Talking wouldn't be that easy for me, because i need some time to think about what the best approach would be. If it is ok for you we can chat, but then we also could use the git chat application that you provided in another issue. :-) |
I meant for chatting, it seems no one visits gitter
Ok, I see logic, but using the order of routes is easier, because you don't need to think of "static defined", count of segments etc. More important - how can you do this:
These two blocks should be visible together for url "/users/root". |
There are problems when adding a Route using JavaScript. So you need to sort the routes before processing them or it wont work. To your second question: i see no case when two routes should be visible. There is exactly one visible DOM segment. |
No, it exactly why ngRouter is dead, there are a lot of cases. |
Wouldn't it be more flexible to apply al-if instead of al-route? <div al-if="$route.is('/users/:id')"></div> But yes, i understand your case. |
It's not easy to implement "confirm on exit" for this. I've also added al-route-out2 - it is fired on url chaning, |
Wouldnt it be better to implement custom events? <div @al.route.out="left()"></div> |
It's good idea, I'll try it <div @route-out="confirmToOut($url)"></div> What about al-route and al-route:catch? Need good names for them. |
I'd recommend to prefix framework specific events.
The problem is, it should not do a segment less catchall only (e.g. catching We also need to discuss al-link vs. using the href attribute. |
yes
Do you mean, al-route:catch="/" should catch all "/.*"? |
All, except already defined routes. |
I'd like to push on this, because it will be a great feature. Do you need any further information to implement this? |
I will append it to package "ext" |
Where will it be found? In the regular repository? |
Yes |
Is it ready? I'd like to play with it. :-) |
Hello! I was quite busy, published it here: https://github.com/lega911/alight/blob/master/ext/router.es.js |
Great to have you back. :-) |
Not yet, but it will be there in next version |
https://gist.github.com/fend25/3619d6d730039c30a34d
The text was updated successfully, but these errors were encountered: