baseprime / grapnel Goto Github PK
View Code? Open in Web Editor NEWThe smallest JavaScript router with named parameters, HTML5 pushState, and middleware support
Home Page: http://grapnel.js.org
The smallest JavaScript router with named parameters, HTML5 pushState, and middleware support
Home Page: http://grapnel.js.org
There seems to be an issue in only Safari that causes the matching route handler to fire twice when the page is first loaded when using pushState. It seems related to the window's popstate
event being fired on page load.
Here is an example of the code causing the issue:
var router = new Grapnel({ pushState : true });
router.get('/test/:id?', function(req) {
console.log('Route handler fired', req.params.id);
});
Tested using Safari 8.0.2.
Awesome plugin, keep up the great work!
If your interface has an .on
method to register event listeners, it really should support some way to unregister them. And to be consistent with expectations, this should be .off(event, callback)
.
For example, I'm trying to plug the router into a Kefir stream:
Kefir.fromEvents(this.router, 'navigate')
But the Kefir.fromEvents
function will throw an error like this:
Uncaught Error: target don't support any of addEventListener/removeEventListener, addListener/removeListener, on/off method pair
This would be a nice enhancement would be very beneficial for routing.
Hello,
since the release v0.5.2 to the release v0.5.6, there were new features and break in the API.
If I upgrade v0.5.2 to v0.5.6, my app doesn't work. It's not important, I'll find why.
Some changes may cause a break :
These changes are logical, Grapnel evolves well :)
But are not simple patch or bug fix. (It's minor or major changes).
Please can you follow the semver for allowing the peace of mind when we added a rule in package.json, like "grapnel": "^0.5.0"
Thanks for your work!
(sorry for my bad english)
If pushState : false
is used in the options, the "navigate" event is being triggered two times. This is the case in both Chrome 41 Ubuntu 14.10 and Firefox 36.0.4 Ubuntu 14.10 after calling Grapnel#navigate
. When using pushState : true
the event is only triggered once as expected.
I use grunt-contrib-requirejs for move all code in one file but r.js wich is using for compilations don't understand following code
root.define(function(require, exports, module){
root.define.amd.grapnel = true;
return Grapnel;
});
and can't replace it into something like this
root.define('Grapnel',[],function(require, exports, module){
root.define.amd.grapnel = true;
return Grapnel;
});
If have a case where I need to send a state obj when calling navigate
example:
var stateobj = {modal: true, id: "e943", color: "#333"};
router.navigate("/product.html", stateobj);
I'd also like to process this state object inside my 'navigate' event listener.
router.on('navigate', function(event){
// process stateobj here, open modal or whatever
});
Is this possible somehow - or would you consider such an enhancement?
Hello,
Grapnel supports custom hash ? It's for use #!
instead.
https://developers.google.com/webmasters/ajax-crawling/docs/getting-started
So I am currently building a new frontend structure for my app, and I wanted to use Grapnel for that. The structure involves using controllers. Actually, the app is driven by Laravel 5.1, but some parts are actually simpler via Webpack and JSX. So Laravel simply serves the page template that I want to drop my Controller's rendered output into.
My HTML is something like this (simplified):
<div class="docs sidebar" id="DocBar">
</div>
<div class="docs body" id="DocBody">
</div>
Now I want to do something like this with Grapnel:
app.get("/docs", function(req, e, next){
require.ensure([
"BIRD3/App/Controllers/DocsController"
], function(require){
var Controller = require("BIRD3/App/Controllers/DocsController");
next(new Controller); // <----
}, "DocsController");
});
Basically, I want to keep going untill the PJAX data is loaded and then run the .render()
method in my class.
Is there a way I can use next()
like I did above, or do I need to use events to make this work?
No Support for ie8...yes I know why bother, I wish some of my client thing the same.
I am preparing a push request that provides minimal support for ie8 on hashchange mode
Is it possible to use to use grapnel with "document.location.pathname" changes? I do not intend to use hashes in my URLs.
Also, is there a browser support list?
Thanks
Hello,
it is possible with Grapnel to make a middleware?
Like:
// Middleware
router.get('product/*', function(req, event, next) {
console.log('middleware called');
next();
});
// Basic route
router.get('product/:id', function(req, event) {
console.log('route called');
});
Hey, I'm currently in the process of trying to integrate grapnel into a javascript app that uses knockout and I've hit a bit of a wall.
I've got quite a few buttons in this app and they all run various functions that fire off modals and the such. Knockout has this rather neat feature where it passes the context on a click event to the handler. Rather than rip this entire app to pieces and start again I was wondering if there was any way to get hold of this data on a click event and pass it into a route handler?
I've put together a very cut back knockout example to try and help explain what I want to achieve.
Hi,
'use strict';
var Grapnel = require('grapnel');
var router = new Grapnel({hashBang: true});
router.get('hello', function() {
console.log('route ', arguments);
});
This code works if Grapnel is added with <script src="/assets/js/grapnel.js"></script>, but does not work with require('grapnel')
(Webpack).
Any idea please?
Thanks
Hello!
Thanks for a great router ๐
I have an issue where navigating using browser history upon page load is not triggering navigate().
It has to do with this piece:
root.addEventListener('popstate', function(e){
// Make sure popstate doesn't run on init -- this is a common issue with Safari and old versions of Chrome
if(self.state && self.state.previousState === null) return false;
self.trigger('navigate');
});
self.state.previousState
is null on page load; thus when my browser history contains a route which is on my app domain and is listenable and I navigate to it, it wont trigger. Is there a workaround for this? Removing if(self.state && self.state.previousState === null) return false;
solves it for me, but judging by the comment it's not a very good idea to remove it :)
I'm using isomorphic routing with Grapnel and grapnel-server which was working great. After I upgraded to Grapnel v0.6.3 (from 0.6.2) my pages stopped to respond. At first, I thought there was a bug in my code, so I started to debug and found the real cause of the problem.
In you last commit 5a838d4 you changed a lot of functionality in grapnel.js
, that eventually broke grapnel-server.
The wareShouldRun
function is now accepting wrong data https://github.com/bytecipher/grapnel/blob/server-router/index.js#L122. In previous version, the req
attribute was holding the entire connect
's request (a huge object), and here's how it looks now:
{ params: {},
keys: [],
matches: [],
match: [ '/admin/users', index: 0, input: '/admin/users' ] }
So the page now hangs, because req.method
is always undefined
. The res
object is now also became quite a srinked.
Hope this info will be helpful to you.
There is currently no way to register a "home" route, to be triggered when the hash is empty?
router.get("#", function(){ console.log('fired every time we have no hash value'); });
This looks like a great library though I would almost have ignored it and would have moved on to "Aviator" instead.
The documentations line which made me try out this library anyhow was "Supports routing using pushState or hashchange concurrently
". Though, in a few places the documentation makes the impression that one had to choose between one of the two.
E.g.:
'pushState' in window.history
). Using this as the option's value seems solid though this should probably mentioned in the documentation to show that this is indeed working fine.If pushState is enabled, you can navigate through your application with router.navigate
"How about using the new HTML5 History API and then gracefully degrees to hashes in browsers that don't support it?
As of right now, it seems the following route syntax does not work:
router.get('settings/*?', handler)
Since there is currently support for optional parameters, as well as wildcard parameters, it would be awesome if this plugin could support optional wildcards. I know this can already be accomplished with a RegEx, but using the nicer syntax would be that much better.
This would be particularly useful if you wanted to handle a bunch of routes with a common context (eg. /settings
, /settings/general
, /settings/users/add
) with one event handler.
It seems stopPropagation only stops the next route, at least that seems to be what it was doing for me. I'm not sure if this is the best way to solve the issue, but this worked for me:
changing
if(event.parent() && event.parent().propagateEvent === false) return self;
to
if(event.parent() && event.parent().propagateEvent === false) {
event.propagateEvent = false;
return self;
}
Which means next time we jump to the third route the parent will now have the propagate set to false, and the subsequent handler will also be prevented.
Is there a way to know in a route handler if the route was triggered via back button navigation?
So I am trying to set up Grapnel for good now. Thign is, I think I am doing something wrong.
This script:
// Commons
import "./Support/Common";
// Modules
import Grapnel from "grapnel";
import oo from "o.o";
import Compatibility from "BIRD3/Support/Compatibility";
import Routes from "./Support/Routes";
var pushRouter = new Grapnel({
pushState: true,
root: "/"
});
var hashRouter = new Grapnel({
pushState: false,
hashbang: true
});
// Hacky way to enable hashbang AND pushState routing.
var app = pushRouter;
app.get = function(){
hashRouter.get.apply(hashRouter, arguments);
Grapnel.prototype.get.apply(this, arguments);
}
oo("[data-pjax]").click(function(e){
if(hasPushState) {
// This is a PJAX link. So hold it right there.
e.preventDefault();
app.navigate(oo(e.target).attr("href"));
}
});
Compatibility(function(err){
if(err) { console.log(err); }
console.log("Loading routes...");
Routes(app);
});
app.on("navigate",function(){
console.log(arguments);
console.log("Navigating...");
})
app.navigate();
fails:
Uncaught TypeError: this.path(...).trigger is not a function(โฆ)
And I realized, there is no hint in the readme, about how I can let Grapnel trigger the first navigation.
The Routes function is just assigning app.get
s. So there is nothing special in there.
Hello,
How would I get this to work with an nginx static file server, where I don't want hashbangs between navigating. Currently it seems that doing <a href="/my/route">
triggers the page to actually navigate to that route through nginx.
I should also mention that my nginx is serving from the path /welcome
:
// index.js
<base href="/welcome/">
// router.js
var router = new Grapnel({ root: '/welcome/' })
router
.get('intro/:step', function (req, e) {
})
// nginx.conf
location /welcome {
try_files $uri /index.html;
}
Hoping to add router.use()
method to allow middleware to be mounted on a given (or all) routes.
it would be nice to provide an easy method to wrap history.replaceState
and trigger an event
Great library. Thanks for your work!
Is there a way to set the url hash without triggering the route handlers?
I've tried path(newPath), but that triggers them just like navigate(newPath).
I can imagine doing it with preventDefault() in a pretty hackish way - I'd need to have an ignoreMatching
flag of some sort, but turning this on and off safely seems rather fragile. (eg, I'd need to clear the flag whether or not a match was found, and worry about event orders.)
Is there a more efficient way to do it? Some other libraries just have a silently
option for navigate()
.
Seems like Grapnel only responds to GET, can we support the full gamut? I'd like to see at least POST request support. Thanks.
It would be useful to be able to stop navigation using something like preventDefault
or by returning false
in listeners to the change
event.
A few use cases:
I would like to set priority for conflicting routes like these
router.get('/doSomethingElse', doSomethingElse);
//displays the JST that matches the pagename in the url
router.get('/:page?', function (req) {
var p = req.params.page || 'homepage';
document.getElementByID('main').innerHTML = JST[p](getData(p));
});
problem is doSomethingElse is never called. so I have to check params inside my other route.
It would be nice to be able specify route priority.
Hi,
I noticed that calling router.get
twice on the same route made that the callback function is executed twice once that route is accessed. What would be ideal, is that when calling get
on an route already defined in the router, it would replace the existing callback by the new one.
If that is not possible, as an alternative, is there a way to check if a route is already defined in the router, in order then to avoid re-executing get
on that route?
Thanks for the simple/awesome router.
I looked at the documentation/code and realized that the options.root is only applicable for the pushState mode.
Can this be made applicable to the regular non pushState mode too?
I am aware that router.context(), can help me here, router.context returns only a wrapper to router.get.
I think that the frag.get/set methods needs to be changed to use the options.root for the non pushState mode, but am not good with the pushState/JS tests and don't want to waste your time sending a pull request without test coverage :(
Use setInterval
to check hash on browsers not supporting window.onhashchange
path/to/:route
GET http://domain/#path/to/route
Params are
=> { route : "to" }
Instead of
=> { route : "route" }
this._router.add('posts/add',this._auth,(req,event)=> {
event.stopPropagation();
new PostNewView();
});
this._router.add('posts/:postId',(req)=>new SinglePostView({postId:req.params.postId}));
this works as expected only if this._auth is removed
Allow actions to be RegEx'd.
It seems to be assuming that root === window which isnt the case with browserify. So eventName ends up being 'navigate' instead of hashchange.
If I stick 'root = window' at the top of the iife it does work.
Maybe it'd be a good idea to check typeof window === 'undefined' and assign that to root.
I'm matching with
router.get('*/max/:maxNum', function(req) { console.log(
max is ${req.params.maxNum}); });
When I set the url to:
...#/why/max/88
I get
req.params = { 1: "88", maxNum: "why" }
That seems wrong. I should be getting just { maxNum: "88" }
Is it possible to check if no routes are matched?
If no routes are matched, i'm thinking of redirecting the user to a 404 page.
Hi,
First of all, your library is great, simple, flexible and very fast. In the last days I was requiring to stop the propagation of a match because I had to prepare an app when nothing is matched using the wildcard * but with the normal behaviour the wildcard will be matched also on each match. So I made a very simple change to stop the propagation of an event. When a hash is matched will stop the propagation so no other match will be triggered.
Here is the change: http://www.mergely.com/PfNmOxS5/
Best :)
when stopping or canceling a route handler, the URL should not update.
router.on('match', function(event){
if(event.route === '/about'){
event.preventDefault();
event.stopPropagation();
}
});
this cancels properly but the url still updates to http://example.com/about
Hi,
I have a modular website which instantiates several Grapnel routers, one per module.
I noticed that when one of those modules is re-created, although I re-instantiate the router via this.router = new Grapnel()
, the callback for a given route is executed as many times as that module has been created.
So, basically, I wonder if there is a way to destroy a Grapnel router, so that it stops listening to its registered routes.
Regards,
Christophe
I have two different routes that match both. Is there a way to fix the regex not to match both ?
For example:
<script>
var router = new Grapnel.Router();
// Create route
router.get('calendar/:year/:month/:day', function(req){
console.log(req.params);
});
router.get('calendar/project/edit/:id', function(req){
console.log(req.params);
});
</script>
For this requests #calendar/project/edit/10
i have to matches and the log output will be
{
day: "10"
month: "edit"
year: "project"
}
and
{
id: "10"
}
when i am expecting it to be just {id: "10"}
but it matches the first route too.
Nice software but we already have Page.js, see https://visionmedia.github.io/page.js/
Tell me, what's the difference between these two?
Even tho I have the following code, the page will still reload:
router.get('/about', function(req, event) {
event.preventDefault();
...
What am I missing please?
Hi there,
I am having some problems with Grapnel.
#1 Listen is not working
I tried using this example but I couldn't make it work.
https://github.com/EngineeringMode/Grapnel.js#declaring-multiple-routes
The only way I have to listen to url changes is using get:
router.get(MY.PATTERN, function(req, event) {
//do something
});
#2 I cannot event.stopPropagation inside get method
In order to restrict the user to only certain URLs I want to listen to any url change, if the url has a previous pattern match, it is correct, otherwise it is wrong. But I cannot find a way to stop the propagation.
router.get(MY.PATTERN, function(req, event) {
event.stopPropagation() //not working.
});
router.get(*, function(req, event) {
alert('wrong url!');
});
Any ideas / suggestions?
is there any way to stop the callbacks from firing - for example if you want to manually rewrite the href?
When client A uses a browser that does not support the pushState
mechanism, they may link the URL to another user to share it. That user then opens the URL - but from hat I can see in the code, the hashbang is not checked for, only i the mode is set to be the hashchange one.
Client A: http://example.com/#!one/route
Client B: http://example.com/one/route
Client B can share URL with client A - but possibly not vice versa.
A "if hashbang available, open that route" kind of fallback would be nice. :)
although the docs/comments suggest it can be done, multiple events cannot be called when separated by a space.
trigger Triggers an event
/**
* @param {String} event name (multiple events can be called when seperated by a space " ")
* @param {Mixed} [attributes] Parameters that will be applied to event handler
*/
router.trigger('event otherevent', eventArg1, eventArg2);
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.