GithubHelp home page GithubHelp logo

baseprime / grapnel Goto Github PK

View Code? Open in Web Editor NEW
466.0 466.0 40.0 484 KB

The smallest JavaScript router with named parameters, HTML5 pushState, and middleware support

Home Page: http://grapnel.js.org

JavaScript 94.09% HTML 0.52% CSS 4.89% Shell 0.49%

grapnel's People

Contributors

baseprime 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

grapnel's Issues

Route handler fires twice on initial load in Safari when using pushState

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!

an `.off` method to unregister an event listener

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

Follow semver

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 :

  • require('grapnel').Grapnel now is undefined with Webpack and browserify
  • anchor, hash and Grapnel().router are removed

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)

"navigate" event triggers twice if `pushState : false`

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.

r.js can't replace root.define

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;
});

Using navigate with state object

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?

How can I pass a controller?

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?

Support for ie8

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

Normal route/uri support?

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

Middleware support

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');
});

Possible to access click event data within a route?

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.

https://jsfiddle.net/nt0j49x7/1/

Does not work with require() (Webpack)

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

Triggering a route even if it's not in previousState

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 :)

Bug in latest release

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.

Home Route

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'); });

[documentation] Confusion concerning usage of pushState with hashbang as fallback

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.:

  • The pushState option doesn't default to a feature detection (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.
  • The documentation states "If pushState is enabled, you can navigate through your application with router.navigate"
    router.navigate though also works without pushState disabled - any other behavior would be weird anyhow, especially when developing for supporting both ways concurrently.

Using History API

How about using the new HTML5 History API and then gracefully degrees to hashes in browsers that don't support it?

Support for optional wildcard in routes

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.

event.stopPropagation() only stops next route, not any further routes after that

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.

Back Button

Is there a way to know in a route handler if the route was triggered via back button navigation?

Errors about this.path(...).trigger not being a function...

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.gets. So there is nothing special in there.

Working with nginx

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;
}

Middleware router.use()

Hoping to add router.use() method to allow middleware to be mounted on a given (or all) routes.

support `replaceState`

it would be nice to provide an easy method to wrap history.replaceState and trigger an event

Navigation without triggering route matching

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().

Support for More HTTP verbs?

Seems like Grapnel only responds to GET, can we support the full gamut? I'd like to see at least POST request support. Thanks.

Ability to cancel navigation from change event

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:

  • Navigating to another part of the app with unsaved changes
  • Navigating back to a step when you've gone too far (using manually typed URL or back button)
  • Navigating to a step (using manually typed URL or forward button) without completing required fields

is there a way to set priority?

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.

Calling router.get() on the same route more than once

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?

Feature Request : options.root applicable for hashbang urls too

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 :(

stop propagation works only if there is no middleware

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

Hashchange handling not working with Browserify

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.

Named parameter with preceding wildcard messes up params.

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" }

No routes matched

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.

Stop Propagation when more than one URL is match

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 :)

Destroying the router, in order to stop listening to its registered routes

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

Duplicate match issue

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.

preventDefault

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?

Having some problems listening to events and stopping propagation

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?

Hashbang detection when sharing URL

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. :)

multiple events not triggered when delimited by a space

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);

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.