:metal: KnockoutJS Goodies Monorepo
Super-duper flexible component based router + middleware framework for developing wicked awesome single page apps with KnockoutJS
$ npm install @profiscience/knockout-contrib-router
…or…
$ yarn add @profiscience/knockout-contrib-router
app.js
import * as $ from 'jquery'
import * as ko from 'knockout'
import { Route, Router } from '@profiscience/knockout-contrib'
const loading = ko.observable(true)
Router.use(loadingMiddleware)
Router.useRoutes([
new Route('/', 'home'),
new Route('/users', [
new Route('/', [loadUsers, 'users']),
new Route('/:id', [loadUser, 'user']),
]),
])
/**
* Optionally use object-shorthand
* Router.useRoutes({
* '/': 'home',
* '/users': {
* '/': [loadUsers, 'users'],
* '/:id': [loadUser, 'user']
* }
* })
*/
ko.components.register('home', {
template: `<a data-bind="path: '/users'">Show users</a>`,
})
ko.components.register('users', {
viewModel: class UsersViewModel {
constructor(ctx) {
this.users = ctx.users
}
navigateToUser(user) {
Router.update('/users/' + user.id, { with: { user } })
}
},
template: `
<ul data-bind="foreach: users">
<span data-bind="text: name, click: $parent.navigateToUser"></span>
</ul>
`,
})
ko.components.register('user', {
viewModel: class UserViewModel {
constructor(ctx) {
this.user = ctx.user
}
},
template: `...`,
})
function loadingMiddleware(ctx) {
return {
beforeRender() {
loading(true)
},
afterRender() {
loading(false)
},
}
}
// generators are also supported if you're a pioneer of sorts
// function * loadingMiddleware(ctx) {
// loading(true)
// yield
// loading(false)
// }
// TypeScript? Good for you! Just add ~water~ these lines
// declare module '@profiscience/knockout-contrib-router' {
// interface IContext {
// user?: MyUserTypeDef
// users?: MyUserTypeDef[]
// }
// }
function loadUsers(ctx) {
// return promise for async middleware
return $.get('/api/users/').then((us) => (ctx.users = us))
}
function loadUser(ctx) {
// if not passed in via `with` from Users.navigateToUser
if (!ctx.user) {
return $.get('/api/users/' + ctx.params.id).then((u) => (ctx.user = u))
}
}
ko.applyBindings({ loading })
index.html
<router data-bind="css: { opacity: loading() ? .5 : 1 }"></router>