Giter VIP home page Giter VIP logo

cherrytree's Introduction

Cherrytree is a flexible hierarchical router that translates every URL change into a transition descriptor object and calls your middleware functions that put the application into a desired state.

Installation

The size excluding all deps is ~4.83kB gzipped and the standalone build with all deps is ~7.24kB gzipped.

$ npm install --save cherrytree

In a CJS environment

require('cherrytree')

In an AMD environment, require the standalone UMD build - this version has all of the dependencies bundled

require('cherrytree/standalone')

Docs

Demo

See it in action in this demo.

Plugins

To use cherrytree with React, check out cherrytree-for-react.

Usage

var cherrytree = require('cherrytree')

// create the router
var router = cherrytree()
var handlers = require('./handlers')

// provide your route map
router.map(function (route) {
  route('application', {path: '/', abstract: true}, function () {
    route('feed', {path: ''})
    route('messages')
    route('status', {path: ':user/status/:id'})
    route('profile', {path: ':user'}, function () {
      route('profile.lists')
      route('profile.edit')
    })
  })
})

router.use(function render (transition) {
  transition.routes.forEach(function (route, i) {
    route.view = handlers[route.name]({
      params: transition.params,
      query: transition.query
    })
    var parent = transition.routes[i-1]
    var containerEl = parent ? parent.view.el.querySelector('.outlet') : document.body
    containerEl.appendChild(view.render().el)
  })
})

router.use(function errorHandler (transition) {
  transition.catch(function (err) {
    if (err.type !== 'TransitionCancelled' && err.type !== 'TransitionRedirected') {
      console.error(err.stack)
    }
  })
})

// start listening to URL changes
router.listen()

Examples

You can clone this repo if you want to run the examples locally:

A more complex example in it's own repo:

Features

  • can be used with any view and data framework
  • nested routes are great for nested UIs
  • generate links in a systematic way, e.g. router.generate('commit', {sha: '1e2760'})
  • use pushState with automatic hashchange fallback
  • all urls are generated with or without # as appropriate
  • link clicks on the page are intercepted automatically when using pushState
  • dynamically load parts of your app during transitions
  • dynamic segments, optional params and query params
  • support for custom query string parser
  • transition is a first class citizen - abort, pause, resume, retry. E.g. pause the transition to display "There are unsaved changes" message if the user clicked some link on the page or used browser's back/forward buttons
  • navigate around the app programatically, e.g. router.transitionTo('commits')
  • easily rename URL segments in a single place (e.g. /account -> /profile)

How does it compare to other routers?

  • Backbone router is nice and simple and can often be enough. In fact cherrytree uses some bits from Backbone router under the hood. Cherrytree adds nested routing, support for asynchronous transitions, more flexible dynamic params, url generation, automatic click handling for pushState.
  • Ember router / router.js is the inspiration for cherrytree. It's where cherrytree inherits the idea of declaring hierarchical nested route maps. The scope of cherrytree is slightly different than that of router.js, for example cherrytree doesn't have the concept of handler objects or model hooks. On the other hand, unlike router.js - cherrytree handles browser url changes and intercepts link clicks with pushState out of the box. The handler concept and model hooks can be implemented based on the specific application needs using the middleware mechanism. Overall, cherrytree is less prescriptive, more flexible and easier to use out of the box.
  • react-router is also inspired by router.js. React-router is trying to solve a lot of routing related aspects out of the box in the most React idiomatic way whereas with cherrytree you'll have to write the glue code for integrating into React yourself (see cherrytree-for-react plugin). However, what you get instead is a smaller, simpler and hopefully more flexible library which should be more adaptable to your specific needs. This also means that you can use a react-router like approach with other React inspired libraries such as mercury, riot, om, cycle, deku and so on.

CI

Build Status build status

Browser Support

Sauce Test Status

Cherrytree works in all modern browsers. It requires es5 environment and es6 promises. Use polyfills for those if you have to support older browsers, e.g.:

Acknowledgement

Thanks to Marko Stupić for giving Cherrytree a logo from his http://icon-a-day.com/ project!

FAQ

  • Why is cherrytree written as one word? You got me, I'd say that represents the wabisabi nature of the library.

Want to work on this for your day job?

This project was created by the Engineering team at Qubit. As we use open source libraries, we make our projects public where possible.

We’re currently looking to grow our team, so if you’re a JavaScript engineer and keen on ES2016 React+Redux applications and Node micro services, why not get in touch? Work with like minded engineers in an environment that has fantastic perks, including an annual ski trip, yoga, a competitive foosball league, and copious amounts of yogurt.

Find more details on our Engineering site. Don’t have an up to date CV? Just link us your Github profile! Better yet, send us a pull request that improves this project.

cherrytree's People

Contributors

blikblum avatar davidstrada avatar failpunk avatar greenkeeperio-bot avatar jamesplease avatar kidkarolis avatar nathanboktae avatar oliverwoodings 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

cherrytree's Issues

Returning stuff in the model hook can get very confusing

If it overwrites the context of the route or shadows context of the parent routes.

Proposal: returning in the model hook should only be used for blocking, not for anything else. If someone wants to block and then put the return data somewhere, they can do that manually. This is much simpler and much more transparent.

In general, the context stuff needs to be simplified greatly.

Nail down the update semantics

existance of update method should indicate that the route won't be reloaded, returning false overrides that behaviour?

Make events look like events

There are a couple of events in the routes: willTransition, error and queryParamsDidChange - consider making it explicit that they're events by putting them into route.events.willTransition or so like the original router.js does it.

Events are special in that they're called from the right most route to the top, and the propagation of them can be stopped so it's worth making them look different from the rest of the hooks.

Improve the API of model()

I want to name the models so that the child routes could easily access them via the name, but I can't figure out a simple way to do that, e.g. this is clunky:

model: function () {
  var model1 = new Model();
  var model2 = new Model();
  return when.join(model1.fetch(), model2.fetch()).then(function () {
    return {
      model1: model1,
      model2: model2
    }
  });
}

With the above snippet, child routes can do this.get("model1") and they'll get the model1 since it's been "named" in the context - the return value of model.

Document `locationOptions`

The documentation for where to use pushState and other location options should be updated to explicitly say that the these options should be inside a locationOptions key inside the options. That will save some development time to someone else :)

When using router.handlers getting a TypeError

Hey there :

Context:

router.map(function() {
 this.route("launch", { path: "/launch"});
});`

`router.handlers['launch'] = Route.extend({ before, model,after})`

By doing it this way i get this error Uncaught TypeError: Cannot set property 'launch' of undefined

If i change it to (works):
router.routes["launch"] = Route.extend({});

However I've read the docs and they say the former it's the way to go, also notice you committed the change 2a8a0ca

I'm on version "cherrytree": "^0.5.0", Installed via NPM.
Using Browserify browserify router-amd.js > router.js,

Not sure what's wrong here, also I'm following this tut --> http://requirebin.com/?gist=aa3edb9fb05fa01c59f0#

2.0

Code

  • fix the scripted build process - smth fails currently
  • polish the location implementations, make sure they make sense (consider introducing makeHref and makePath on the location object itself)
  • improve handling of uncaught transition errors
  • fix #destroy(), currently the link delegation does not get removed
  • clone query and params when setting them on transition and router.state
  • make it possible to use native/global Promise
  • lower level createRoute function that takes location, qs and Promise implementations as params and this way avoids pulling them in as dependencies
  • throw a more specific error when trying to transitionTo/generate a route that is not a leaf node
  • move custom location to router options away from listen()
  • update upstream dependencies
  • rename history.js to browser.js

Docs

  • add the size of the lib to README
  • create a backbone example (the backbone-route workflow)
  • create examples folder where various middlewares are demonstrated
  • write a blogpost about the journey to 2.0

Other

  • create cherrytree-for-backbone not now, for now cherrytree-for-react will do
  • create cherrytree-for-react. Done https://github.com/KidkArolis/cherrytree-for-react
  • proof of concept isomorphic example now now, for now the server-side-react example will do

Some issues with lists

Right now I'm trying out CherryTree as a possible replacement for Zim. However, I have found two issues with it.

With every rich-text editing app I've tried, when at the front of an item in a bullet list (IE: the caret is before the text, but after the bullet and space), pressing tab will indent the bullet item's level. CherryTree does not follow this pattern, and instead indents the text while leaving the bullet alone. In order to indent the list item's level, you need to indent before the bullet.

The second issue is that when you are on a list item's line and press return, the indent level is not preserved.

So, if you had a list like this:

  • One
    • Two
      • Three

And then hit return, the indentation level goes back to the first one, instead of matching the indent level of the third item. So, you would get this:

  • One
    • Two
      • Three
  • Four

Instead of this:

  • One
    • Two
      • Three
      • Four

These are the only main issues I have so far that I find problematic. Of course, it is entirely possible that the real issue is that CherryTree doesn't support proper list indentation levels, in which case this bug report can be treated as a feature/enhancement request.

Explore a way to activate a state right after it's model is fetched

Explore a way to activate a state right after it's model is fetched without waiting for child states. Atm it's possible to just call this.activate() in the model manually, but if a transition away from this route occurs, cherrytree won't deactivate the route as it was never entered (?). Latest versions of router.js has the substates, so those might be the answer.

Can't run hello-world example

First it's missing jquery as a dependency, but after adding that, I'm still getting:

~/github/cherrytree/examples/hello-world 826b1df|master  ✹✭
 6:14PM ᐅ npm start

> [email protected] start /Users/nblack/github/cherrytree/examples/hello-world
> webpack-dev-server --colors --no-info --port=8000 --content-base .

http://localhost:8000/webpack-dev-server/
webpack result is served from /
content is served from /Users/nblack/github/cherrytree/examples/hello-world
Hash: 9590aaacd90b7edeaa10
Version: webpack 1.10.5
Time: 1033ms
        Asset    Size  Chunks             Chunk Names
    bundle.js  272 kB       0  [emitted]  main
bundle.js.map  343 kB       0  [emitted]  main
chunk    {0} bundle.js, bundle.js.map (main) 260 kB [rendered]
    [0] ./index.js 4.36 kB {0} [built]
    [1] ./~/jquery/dist/jquery.js 248 kB {0} [built]
    [2] /Users/nblack/github/cherrytree/index.js 56 bytes {0} [built] [1 error]
    [3] ./style.css 1.17 kB {0} [built]
    [4] ./~/css-loader!./style.css 999 bytes {0} [built]
    [5] ./~/css-loader/cssToString.js 352 bytes {0} [built]
    [6] ./~/style-loader/addStyles.js 5.51 kB {0} [built]

ERROR in /Users/nblack/github/cherrytree/index.js
Module not found: Error: Cannot resolve module 'babel' in /Users/nblack/github/cherrytree
 @ /Users/nblack/github/cherrytree/index.js 3:17-40

does it also need loader: 'babel?optional=runtime' like the main project? Sorry I have no experience with webpack.

Solve the dependency issues

Either vendorize router.js, route-recognizer and use a light promise shim instead of rsvp (it's unlikely someone wants router.js and route-recognizer separately, but they could also be exposed from within cherrytree).

Alternatively, help router.js, route-recognizer and rsvp sort their stuff out in terms of global/amd/cjs/bower/npm builds.. none of those are working together at this point.

Throw an exception when shadowing context

If the parent route has a key "foo" in the context and the child route tries to set "foo" in the context - throw an exception. This is most likely an error. You typically never want to overwrite / shadow the context.

And it's easy to do so accidentally by returning some raw $.fetch in the model hook which resolves to an object with many attributes whose names might clash with the names of resources. In those cases you meant to return {} or {myObject: $.fetch(...)} instead of the result of the fetch.

willTransition is not called on first call of transitionTo

I may just have a misunderstanding of the purpose of willTransition here, but I would have thought that willTransition would be called whenever a transition occurs?

Example of what I am seeing:

router.addRoute("foo", Route.extend({
  willTransition: function () {
    console.log('bar');
  }
});

router.startRouting();
router.transitionTo("foo");
router.transitionTo("foo");

The above example will only log bar once.

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.