-
Star
(145)
You must be signed in to star a gist -
Fork
(34)
You must be signed in to fork a gist
-
-
Save joakimbeng/7918297 to your computer and use it in GitHub Desktop.
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Building a router</title> | |
| <script> | |
| // Put John's template engine code here... | |
| (function () { | |
| // A hash to store our routes: | |
| var routes = {}; | |
| // An array of the current route's events: | |
| var events = []; | |
| // The element where the routes are rendered: | |
| var el = null; | |
| // Context functions shared between all controllers: | |
| var ctx = { | |
| on: function (selector, evt, handler) { | |
| events.push([selector, evt, handler]); | |
| }, | |
| refresh: function (listeners) { | |
| listeners.forEach(function (fn) { fn(); }); | |
| } | |
| }; | |
| // Defines a route: | |
| function route (path, templateId, controller) { | |
| if (typeof templateId === 'function') { | |
| controller = templateId; | |
| templateId = null; | |
| } | |
| var listeners = []; | |
| Object.defineProperty(controller.prototype, '$on', {value: ctx.on}); | |
| Object.defineProperty(controller.prototype, '$refresh', {value: ctx.refresh.bind(undefined, listeners)}); | |
| routes[path] = {templateId: templateId, controller: controller, onRefresh: listeners.push.bind(listeners)}; | |
| } | |
| function forEachEventElement(fnName) { | |
| for (var i = 0, len = events.length; i < len; i++) { | |
| var els = el.querySelectorAll(events[i][0]); | |
| for (var j = 0, elsLen = els.length; j < elsLen; j++) { | |
| els[j][fnName].apply(els[j], events[i].slice(1)); | |
| } | |
| } | |
| } | |
| function addEventListeners() { | |
| forEachEventElement('addEventListener'); | |
| } | |
| function removeEventListeners() { | |
| forEachEventElement('removeEventListener'); | |
| } | |
| function router () { | |
| // Lazy load view element: | |
| el = el || document.getElementById('view'); | |
| // Remove current event listeners: | |
| removeEventListeners(); | |
| // Clear events, to prepare for next render: | |
| events = []; | |
| // Current route url (getting rid of '#' in hash as well): | |
| var url = location.hash.slice(1) || '/'; | |
| // Get route by url or fallback if it does not exist: | |
| var route = routes[url] || routes['*']; | |
| // Do we have a controller: | |
| if (route && route.controller) { | |
| var ctrl = new route.controller(); | |
| if (!el || !route.templateId) { | |
| // If there's nothing to render, abort: | |
| return; | |
| } | |
| // Listen on route refreshes: | |
| route.onRefresh(function () { | |
| removeEventListeners(); | |
| // Render route template with John Resig's template engine: | |
| el.innerHTML = tmpl(route.templateId, ctrl); | |
| addEventListeners(); | |
| }); | |
| // Trigger the first refresh: | |
| ctrl.$refresh(); | |
| } | |
| } | |
| // Listen on hash change: | |
| this.addEventListener('hashchange', router); | |
| // Listen on page load: | |
| this.addEventListener('load', router); | |
| // Expose the route register function: | |
| this.route = route; | |
| })(); | |
| </script> | |
| <script type="text/html" id="home"> | |
| <h1>Router FTW!</h1> | |
| </script> | |
| <script type="text/html" id="template1"> | |
| <h1>Page 1: <%= greeting %></h1> | |
| <p><%= moreText %></p> | |
| <button class="my-button">Click me <%= counter %></button> | |
| </script> | |
| <script type="text/html" id="template2"> | |
| <h1>Page 2: <%= heading %></h1> | |
| <p>Lorem ipsum...</p> | |
| </script> | |
| <script type="text/html" id="error404"> | |
| <h1>404 Not found</h1> | |
| </script> | |
| </head> | |
| <body> | |
| <ul> | |
| <li><a href="#">Home</a></li> | |
| <li><a href="#/page1">Page 1</a></li> | |
| <li><a href="#/page2">Page 2</a></li> | |
| </ul> | |
| <div id="view"></div> | |
| <script> | |
| route('/', 'home', function () {}); | |
| route('/page1', 'template1', function () { | |
| this.greeting = 'Hello world!'; | |
| this.moreText = 'Bacon ipsum...'; | |
| this.counter = 0; | |
| this.$on('.my-button', 'click', function () { | |
| this.counter += 1; | |
| this.$refresh(); | |
| }.bind(this)); | |
| }); | |
| route('/page2', 'template2', function () { | |
| this.heading = 'I\'m page two!'; | |
| }); | |
| route('*', 'error404', function () {}); | |
| </script> | |
| </body> | |
| </html> |
thank you!!! very helpful!!!
hello. i was wondering if there is a way to reload a current template and its controller?
@rlynjb I've updated the gist to include event handling and refreshing of routes. I've removed the Object.observe as well because it's already deprecated.
You register event listeners with the $on function in a controller, and you can trigger a refresh/rerender with the $refresh function. See the /page1 route in the example code above.
I still don't consider this a production ready solution though. For a more complete but still small router library have a look at: page
Put John's template engine code where it says:
// Put John's template engine code here...
The link to the code is in the first comment.
Here is the full code with the templaeting engine: https://github.com/vitiral/notes/blob/ad75620f9b5445b18369408a7b23666e84624c0d/js/router/index.html
Thank you, its really good.
You can put this code to show you a 404 message when it can't find the route
try {
if (el && route.controller) {
el.innerHTML = tmpl(route.templateId, new route.controller());
}
} catch(e) {
console.error('ruta no defined')
el.innerHTML = ` <p style='
display: flex;
position: fixed;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: white;
z-index: 10000;
font-weight: bold;
color: red;
margin: -10px 20px;'>
404 page not Found, <a href="#"> go / </a>
</p>`
}

John Resig's micro templating engine is found here