Search code examples
javascripturl-routingractivejs

Ractive.js routing


So I've been porting my app over to ractive. I'm currently serving up each page from Express using Swig... to render out a ractive template client side. This seems a bit nuts when I could serve up one page and use ractive to do all the client side rendering.

I understand that Ractive doesn't ship with a router, and indeed leaves one out by design (to give flexibility etc - it makes sense). I've googled, and trawled through Stack overflow and see a number of third party router libraries are recommended...

However, I can't find any tutorials or advise on best practice regarding routing with rative. So my question is - are there any available?

Thanks

** EDIT **

Following on from martypdx comment - here are the routes I need:

/home <!-- a static page -->
/list <!-- a list of items -->
/list/:itemID <!-- a link to an items detail page -->
/contact <!-- a static contact page -->

In express I've built a simple api that handles all the db stuff.. all the basic CRUD stuff. I'm using socket.io to send all the data back and forth.


Solution

  • Ractive.js and routing? It's pretty simple actually, no magic needed.

    <!-- main.js -->
    {{# route('/') }}<home />{{/}}
    {{# route('/about') }}<about />{{/}}
    {{# route('/404') }}<not-found />{{/}}
    
    <script>
    component.exports = {
      data: {
        route: function(path){
          // Your pattern matching algorithm here. Return true if match.
          // You can use location.pathname, location.search and location.hash
        }
      },
      components: {
        home: /* home constructor */,
        about: /* about constructor */,
        'not-found': /* not-found constructor */,
      }
    };
    </script>
    

    You have other options, like computed properties:

    {{# isHome }}<home />{{/}}
    {{# isAbout }}<about />{{/}}
    {{# isNotFound }}<not-found />{{/}}
    
    <script>
    
    function router(path){
      return function(){
        // Your pattern matching algorithm here. Return true if match.
        // You can use location.pathname, location.search and location.hash
      }
    }
    
    component.exports = {
      computed: {
        isHome: router('/'),
        isAbout: router('/about'),
        isNotFound: router('/404'),
      },
      components: {
        home: /* home constructor */,
        about: /* about constructor */,
        'not-found': /* not-found constructor */,
      }
    };
    </script>
    

    As for passing down the data, you also have a lot of options. You can use oninit that runs when the component is created and ready to render (or in this case, when a section becomes truthy, ie. {{# isHome }} when isHome is true). Here's an example of <home /> fetching data oninit:

    <!-- home.js -->
    <h1>Home</h1>
    <div>{{someDynamicData}}</div>
    
    <script>
    var SomeDataStore = require('stores/some-data-store');
    component.exports = {
      oninit: function(){
    
        // Let's say your data comes from an AJAX call
        $.get(...).then(function(response){
          this.set('someDynamicData', response);
        }.bind(this));
    
        // Or say it's from a listenable data store (like Reflux)
        SomeDataStore.listen(function(){
          this.set('someDynamicData', SomeDataStore.getSomeDynamicData());
        }); 
      }
    }
    </script>
    

    Or you can have the routing component fetch and pass it down (and the routing component "owns" the data). This works well with the computed approach since you can observe computed values and fetch when the appropriate view appears.

    <!-- main.js -->
    {{# isHome }}<home data="{{homeData}}" />{{/}}
    {{# isAbout) }}<about data="{{aboutData}}" />{{/}}
    {{# isNotFound }}<not-found data="{{notFoundData}}" />{{/}}
    
    <script>
    component.exports = {
      ...
      oninit: function(){
        this.observe('isHome', function(isHome){
          if(!isHome) return;
    
          // still the same here, you can use any data source, as long as you
          // set to a data property in the end
          this.get(...).then(function(response){
            this.set('homeData', response);
          }.bind(this));
    
        });
    
        this.observe('isAbout', function(isAbout){
          if(!isAbout) return;
    
          ...
    
        });
      }
    };
    </script>