PushLocationProxy and intercepting clicks/links

416 views
Skip to first unread message

glenn gillen

unread,
Mar 15, 2011, 1:50:44 PM3/15/11
to Sammy.js
Quick questions to sanity check what I'm doing:

Should the PushLocationProxy intercept clicks for routes Sammy is
aware of? I was expecting it to event.preventDefault() on any route
I'd defined and execute my function, instead it seems to navigate to
the intended page and *then* execute the routes. I'm trying to
implement something akin to the github repo explorer where a partial
in inserted in the main content area without a full refresh, but the
path/history is updated to allow history and refreshes to both work as
expected.

If that's how it is meant to work and someone has a working example
I'd be most appreciative. Alternatively I'll post a gist of what I've
done so far later tonight when I've got the code in front of me.

Glenn

glenn gillen

unread,
Mar 16, 2011, 4:59:12 PM3/16/11
to Sammy.js
> I'd be most appreciative. Alternatively I'll post a gist of what I've
> done so far later tonight when I've got the code in front of me.

Here's what I've got:

var app = $.sammy('.wrapper', function() {
this.setLocationProxy(new Sammy.PushLocationProxy(this));
this.get('/login', function() {
this.partial("/login.js");
});
});

$(document).ready(function() {
app.run();
});

What I want is for all requests to /login to be intercepted, a request
to /login.js be executed instead, and the resulting code/html to be
inserted into the partial. At the moment the browser is rendering /
login, and then executing the Sammy app and re-inserting the content
from /login.js into the partial.

Help?

Glenn

Aaron Quint

unread,
Mar 22, 2011, 2:19:20 AM3/22/11
to sam...@googlegroups.com
Hey All

Heres an oft-requested and oft-put off feature thats finally working and is actually (once used) very exciting. Glenn's questions and prodding have pushed me to share it with the community. You can check out the `non-hash` branch and play with it at home:


A little while ago a PushLocationProxy made it into master. It had basic support for pushState (aka HTML5 History), but it didnt really work well because of some unrelated Sammy internals surrounding how we handle paths and 'locations'. It did, however, give me a lot of really great ideas and provided the foundation for something I've wanted to do for a while now, namely figure out a way to transparently handle either '/' urls (and pushState) and '#/' style URLs (hashchange). I _think_ I've got a pretty solid first pass going.

Heres a demo to play with (best viewed in the latest and greatest Chrome, FF4, or Safari 5)


The cool thing about that above, is its just a couchapp (a soca app to be exact) and it transparently responds to either full urls or hashes. With some simple rewrite rules and a vhost, it works the same when you reload the app as when you left off even with full paths.

The big change that powers this is the new DefaultLocationProxy (was the HashLocationProxy). This allows Sammy to handle both full paths and hash based paths. If you click a link within Sammy's element, it checks to see if that link will match a route and if so, pushes the state (if available) and triggers the route. 

There are a couple of backwards incompatible changes here, the biggest being that if you're checking `path` in an EventContext (i.e. in the body of a route or event) its now the full path (starting at / instead of at #). This doesn't really matter if you're just using basic routing, but if you ever call `this.path` explicitly (like in a before filter) you might have to make some changes. 

If you want to try it out (beyond the example above), pull down the latest version from the `non-hash` branch. I suggest defining your routes like

this.get('/push/:id', ...

instead of 

this.get('#/push/:id', ...

as the former will match against both the hash and non-hash version.

Also, as you can see in the example, if you do redirect('push/one') or link without the prefixed '/' Sammy will use pushState where available then fallback to using #/.

Have fun!
--AQ


--------------------------------
Aaron Quint
http://www.quirkey.com
--
You received this message because you are subscribed to the Google Groups "Sammy.js" group.
To post to this group, send email to sam...@googlegroups.com.
To unsubscribe from this group, send email to sammyjs+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sammyjs?hl=en.

deitch

unread,
Mar 22, 2011, 3:15:37 AM3/22/11
to Sammy.js
Whoa! At the risk of coming across as an idiot....

Aaron, since when does Sammy sanely handle / and #/? I had always
understood that Sammy handles #/, but that / would have the browser
reload or change page before Sammy could get near it. Is this HTML5
only?

I have a project I have been working on which uses
/abc
/abc/def
/abc/def#/one
/abc/def#/two

I had always assumed Sammy could handle the difference between the
third and fourth above, but that the first and second would have the
browser load new pages. Hence, I put in quite a bit of work to unify
different pages, whereas this would have been hugely easier just using
Sammy and a single page. Can it really do this?

Avi

On Mar 22, 8:19 am, Aaron Quint <aa...@quirkey.com> wrote:
> Hey All
>
> Heres an oft-requested and oft-put off feature thats finally working and is actually (once used) very exciting. Glenn's questions and prodding have pushed me to share it with the community. You can check out the `non-hash` branch and play with it at home:
>
> Commits:https://github.com/quirkey/sammy/commits/non-hash
> Compare:https://github.com/quirkey/sammy/compare/master...non-hash
>
> A little while ago a PushLocationProxy made it into master. It had basic support for pushState (aka HTML5 History), but it didnt really work well because of some unrelated Sammy internals surrounding how we handle paths and 'locations'. It did, however, give me a lot of really great ideas and provided the foundation for something I've wanted to do for a while now, namely figure out a way to transparently handle either '/' urls (and pushState) and '#/' style URLs (hashchange). I _think_ I've got a pretty solid first pass going.
>
> Heres a demo to play with (best viewed in the latest and greatest Chrome, FF4, or Safari 5)
>
> http://pushstate.ixxr.net/
>
> The cool thing about that above, is its just a couchapp (a soca app to be exact) and it transparently responds to either full urls or hashes. With some simple rewrite rules and a vhost, it works the same when you reload the app as when you left off even with full paths.
>
> The big change that powers this is the new DefaultLocationProxy (was the HashLocationProxy). This allows Sammy to handle both full paths and hash based paths. If you click a link within Sammy's element, it checks to see if that link will match a route and if so, pushes the state (if available) and triggers the route.
>
> There are a couple of backwards incompatible changes here, the biggest being that if you're checking `path` in an EventContext (i.e. in the body of a route or event) its now the full path (starting at / instead of at #). This doesn't really matter if you're just using basic routing, but if you ever call `this.path` explicitly (like in a before filter) you might have to make some changes.
>
> If you want to try it out (beyond the example above), pull down the latest version from the `non-hash` branch. I suggest defining your routes like
>
> this.get('/push/:id', ...
>
> instead of
>
> this.get('#/push/:id', ...
>
> as the former will match against both the hash and non-hash version.
>
> Also, as you can see in the example, if you do redirect('push/one') or link without the prefixed '/' Sammy will use pushState where available then fallback to using #/.
>
> Have fun!
> --AQ
>
> --------------------------------
> Aaron Quinthttp://www.quirkey.com

Steve Ross

unread,
Mar 22, 2011, 9:15:52 AM3/22/11
to sam...@googlegroups.com
Sorry not trying to sound like a jerk but, I think the whole point of this long post is that he has it figured out...

Which by the way is freaking cool.
--
Steve Ross
web application & interface developer
http://blog.stevensross.com
[mobile] (912) 344-8113
[ AIM / Yahoo! : zeriumsteven ] [googleTalk : nowhiding ]

deitch

unread,
Mar 23, 2011, 7:04:04 AM3/23/11
to Sammy.js
It really is. Is it HTML5 only, or really cross-browser?
> web application & interface developerhttp://blog.stevensross.com

Aaron Quint

unread,
Mar 24, 2011, 3:23:09 AM3/24/11
to sam...@googlegroups.com
history.pushState is only available in latest and greatest. However, the whole purpose of the way I tried to construct it was to make it as backwards compatible as possible. If you construct your routes and urls correctly, you can easily have an app that works on the latest browsers with pushState and on older browsers with # hashchange.

Here Sammy matches against the whole path as a regex so you have to be a little more careful about how you specify your paths.

For example:

get('/push/:id')
get('/pull/:id')

will match like

/push/one => push/one
/push/one#/push/two => push/two
/#/push/three => push/three
/push/one#/pull/one => pull/one
/pull/one#/push/two => push/two

If you're on an older (non HTML5) browser, trying to redirect to '/push/one' will reload the page. However, if you do
redirect('push/one')
Sammy will use the best behavior for your browser.

--AQ

--------------------------------
Aaron Quint
http://www.quirkey.com

deitch

unread,
Mar 25, 2011, 6:08:38 AM3/25/11
to Sammy.js
Aaron,

Totally cool. So all you really need to do is:
get('/push/:id');

and it will match both /push/three and #/push/three.
To catch the right behaviour, instead of having
<a href="#/push/three">Click</a> <!-- works for all but misses the
html5 benefit -->
or
<a href="/push/three">Click</a> <!-- works for html5 but messes up pre-
html5 -->

You do
<a href="#/push/three">three</a>

capture it before it goes, and use
redirect("push/three");

Is that right?

Avi

On Mar 24, 9:23 am, Aaron Quint <aa...@quirkey.com> wrote:
> history.pushState is only available in latest and greatest. However, the whole purpose of the way I tried to construct it was to make it as backwards compatible as possible. If you construct your routes and urls correctly, you can easily have an app that works on the latest browsers with pushState and on older browsers with # hashchange.
>
> Here Sammy matches against the whole path as a regex so you have to be a little more careful about how you specify your paths.
>
> For example:
>
> get('/push/:id')
> get('/pull/:id')
>
> will match like
>
> /push/one => push/one
> /push/one#/push/two => push/two
> /#/push/three => push/three
> /push/one#/pull/one => pull/one
> /pull/one#/push/two => push/two
>
> If you're on an older (non HTML5) browser, trying to redirect to '/push/one' will reload the page. However, if you do
> redirect('push/one')
> Sammy will use the best behavior for your browser.
>
> --AQ
>
> --------------------------------

glenn gillen

unread,
Mar 27, 2011, 6:57:14 AM3/27/11
to Sammy.js
Avi,

I've just started using it on a site, I've not fully regression tested
it in older browsers so Aaron may correct me here but...

> Totally cool. So all you really need to do is:
> get('/push/:id');

Working for me.

> and it will match both /push/three and #/push/three.
> To catch the right behaviour, instead of having
> <a href="#/push/three">Click</a> <!-- works for all but misses the
> html5 benefit -->
> or
> <a href="/push/three">Click</a> <!-- works for html5 but messes up pre-
> html5 -->

Correct!

> You do
> <a href="#/push/three">three</a>

Actually, as I understand it you do:

<a href="/push/three">three</a>

And SammyJS captures it, works out if your browser supports pushState
and if so handles it nicely and you end up with a URL bar that is free
of #/path syntax. If it's an older browser, it will replace your path
with the #/path syntax and continue as it used to.

If that's right, it's unbelievably awesome. People without javascript
enabled can still use the site fine (include search engines), people
with the latest and greatest browsers get an improved experience but
still end up with normal looking URLs they can share around with
anyone they like, and those people in between still get full support
with the #/path syntax filling the void. Massive props to Aaron for
getting it working :)

Glenn

deitch

unread,
Mar 29, 2011, 4:27:24 AM3/29/11
to Sammy.js
Wow. I don't have time to work on converting my current project for a
few more weeks, but really awesome. Kudos to Aaron!

deitch

unread,
Sep 27, 2011, 4:52:21 AM9/27/11
to sam...@googlegroups.com
I am a bit confused about something. When we used the #, we knew when we were changing the actual page, vs when we were just changing a route within Sammy in our SPA. With the new syntax, how do we actually change pages? Let's say I am at


I want to route by /route, whereas /page is resolved by the server. This was easy with


but harder with the new syntax. Does this require routes to know what page I am in, so I can ignore /page? Or do I use regex? How do I know when to move to a different page?
Reply all
Reply to author
Forward
0 new messages