Skip to content

Instantly share code, notes, and snippets.

@johanalkstal
Last active May 21, 2017 17:09
Show Gist options
  • Save johanalkstal/b225d7827e2a35bd6229ef519eb3591f to your computer and use it in GitHub Desktop.
Save johanalkstal/b225d7827e2a35bd6229ef519eb3591f to your computer and use it in GitHub Desktop.
Mithril stream experiment
// @flow
export const UPDATE_USER = 'UPDATE_USER'
export const updateUser = (payload: Object) => ({
type: UPDATE_USER,
payload
})
// @flow
import m from 'mithril'
import { model, update } from './state'
import home from './views/home'
import layout from './views/layout'
import signIn from './views/signIn'
import { vmUpdateUser } from './view-models'
/**
* Application routes and their components.
*/
export default {
'/': protectedRoute(home),
'/sign-in': route(signIn, vmUpdateUser())
}
/**
* Creates a RouteResolver object that will render the given view.
* @param {Object} view - the view to render.
* @param {Function} viewModel - a function that returns a view model.
* @returns {Object} a RouteResolver.
*/
function route (view: { view: Function }, viewModel?: Function) {
return {
render () {
return m(layout, m(view, {
vm: viewModel ? model.map(viewModel): null
}))
}
}
}
/**
* A route that is only accessible to authenticated users.
* Redirects to the sign in view when unauthenticated.
* @param {Object} view - the view to render.
* @param {Function} viewModel - a function that returns a view model.
* @returns {Object} a RouteResolver.
*/
function protectedRoute (view: { view: Function }, viewModel?: Function) {
return Object.assign({}, route(view, viewModel), {
onmatch () {
return model().isAuthenticated ? view : m.route.set('/sign-in')
}
})
}
// @flow
import stream from 'mithril/stream'
import { UPDATE_USER } from './actions'
const { assign } = Object
const initialModel = {
isAuthenticated: false,
user: {
firstName: 'Johan',
lastName: 'Alkstål'
}
}
/**
* The application model stream.
*/
export const model = stream(initialModel)
/**
* The update function that updates the model stream.
* @param action {Object} - An action object.
*/
export const update = (action: Object) => {
switch (action.type) {
case UPDATE_USER:
model(assign({}, model(), {
user: assign({}, model().user, action.payload)
}))
return
default:
model(model())
}
}
// @flow
import m from 'mithril'
import styles from './user.css'
export default {
view: (viewnode: Object) =>
m('div', [
m('h1', { class: styles.title }, 'User'),
m('p', { class: styles.text }, viewnode.attrs.vm().userName),
m('input', {
type: 'text',
placeholder: 'First name',
value: viewnode.attrs.vm().user.firstName,
oninput: (event) => {
viewnode.attrs.vm().actions.updateFirstName(event.target.value)
}
}),
m('input', {
type: 'text',
placeholder: 'Last name',
value: viewnode.attrs.vm().user.lastName,
oninput: (event) => {
viewnode.attrs.vm().actions.updateLastName(event.target.value)
}
})
])
}
// @flow
import { update } from '../state'
import { updateUser } from '../actions'
// Actions.
const updateFirstName = (firstName: string) => update(updateUser({ firstName }))
const updateLastName = (lastName: string) => update(updateUser({ lastName }))
/**
* View model for user data.
* @param actions {Object} - Optional actions for the view model.
* returns {Function} A stream callback that expects the model as its parameter.
*/
export const vmUser = (actions?: Object) => (model: Object) => ({
user: model.user,
userName: `${model.user.firstName} ${model.user.lastName}`,
actions
})
/**
* View model for updating user data.
* It has actions provided to the view model by default.
* returns {Function} A stream callback that expects the model as its parameter.
*/
export const vmUpdateUser = () => vmUser({
updateFirstName,
updateLastName
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment