Last active
July 22, 2016 07:01
-
-
Save wyqydsyq/5f1ece098f313cd744110a276f144a76 to your computer and use it in GitHub Desktop.
Isomorphism
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {div, form, fieldset, legend, label, input, button, i, strong} from '@cycle/dom'; | |
import {makeHTTPDriver} from '@cycle/http'; | |
import isolate from '@cycle/isolate'; | |
import xs from 'xstream'; | |
import classes from 'dependencies/classes'; | |
import styles from '../form/styles.less'; | |
import LabelInput from 'components/label-input'; | |
const dataIni = { | |
email: '', | |
password: '', | |
remember: null | |
}, | |
stateIni = { | |
alerts: [], | |
submitting: false, | |
data: dataIni | |
}; | |
function action (type, data = {}) { | |
return { | |
type, | |
effect: data | |
} | |
} | |
function intent ({DOM, HTTP}) { | |
return { | |
input$: DOM.select('input').events('input').map(ev => action('input', {target: ev.target.name, value: ev.target.value})), | |
submit$: DOM.select('button').events('click').map(ev => action('submit', {target: ev.target.name})), | |
responses$: HTTP.select('user') | |
.map(response$ => response$.replaceError(error => { | |
let res = error.response; | |
res.error = true; | |
return xs.of(res); | |
})) | |
.flatten() | |
.map(res => action('response', { | |
success: (typeof res.error == 'undefined' || !res.error), | |
text: ((typeof res.error == 'undefined' || !res.error) && res.body.length) ? 'User created.' : 'An unknown error occured, please try again later.' | |
})) | |
} | |
} | |
function model (intent) { | |
return xs.merge( | |
intent.input$, | |
intent.submit$, | |
intent.responses$ | |
).map(action => state => { | |
switch (action.type) { | |
case 'input': | |
state.data[action.effect.target] = action.effect.value; | |
break; | |
case 'submit': | |
state.submitting = true; | |
state.alerts = []; | |
break; | |
case 'response': | |
state.submitting = false; | |
let alert = { | |
title: action.effect.title || '', | |
text: action.effect.text | |
}; | |
if (action.effect.success) alert.className = 'alert-success'; | |
else alert.className = 'alert-danger'; | |
state.alerts.push(alert); | |
break; | |
} | |
return state; | |
}).fold((state, method) => method(state), stateIni) | |
} | |
function CreateUser (sources) { | |
let actions = intent(sources), | |
state$ = model(actions), | |
render = (state) => { | |
let emailField = LabelInput({state, props: { | |
name: 'email', | |
type: 'email', | |
label: 'Email' | |
}}), | |
passwordField = LabelInput({state, props: { | |
name: 'password', | |
type: 'password', | |
label: 'Password' | |
}}); | |
return form({class: classes(styles.form, styles.formHorizontal)}, [ | |
fieldset([ | |
// show alerts if there's any | |
state.alerts.length ? div('.alerts', | |
state.alerts.map(alert => div({class: classes(styles.alert, styles[alert.className || 'alert-info'])}, [ | |
(typeof alert.title != 'undefined' && alert.title) ? strong(alert.title): '', | |
alert.text | |
])) | |
) : '', | |
legend({class: classes(styles.legend)}, 'Create User'), | |
emailField.DOM, | |
passwordField.DOM, | |
div([ | |
button({class: classes(styles.submit), props: {type: 'button', disabled: state.submitting}}, [ | |
state.submitting | |
? i({class: classes(styles.fa, styles.faSpinner, styles.faSpin)}) | |
: i({class: classes(styles.fa, styles.faUserPlus)}), | |
state.submitting ? ' Creating...' : ' Create' | |
]) | |
]) | |
]) | |
]) | |
}, | |
vtree$ = state$.map(render); | |
actions.submit$.map(action => { | |
console.log(action); | |
}); | |
return { | |
DOM: vtree$, | |
HTTP: xs.combine(state$.take(1), actions.submit$).map(([state, action]) => ({ | |
url: '/users', | |
category: 'user', | |
method: 'POST', | |
type: 'application/x-www-form-urlencoded', | |
send: state.data | |
})), | |
state$ | |
} | |
}; | |
export default sources => isolate(CreateUser)(sources); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {div, h1, p, strong, pre, code, hr} from '@cycle/dom'; | |
import {makeHTTPDriver} from '@cycle/http'; | |
import xs from 'xstream'; | |
import classes from 'dependencies/classes'; | |
import CreateUser from 'components/create-user'; | |
function Welcome (sources) { | |
let response$ = sources.HTTP.select('user') | |
.map(res$ => res$.replaceError(error => { | |
let res = error.res; | |
res.error = true; | |
return xs.of(res); | |
})) | |
.flatten() | |
.startWith({body: []}).take(1) // response$ never completes on server without .take() | |
.map(res => { | |
console.log('users: ', res.body); | |
return res.body; | |
}), | |
getUsers = { | |
url: '/users', | |
category: 'user', | |
method: 'GET' | |
}, | |
createUser = CreateUser(sources), | |
render = (users) => { console.log(users) | |
return div([ | |
h1('Welcome to your new Cycle.js + Hapi application!'), | |
p('Your ORM has the following users:'), | |
pre([ | |
code(JSON.stringify(users, null, "\t")) | |
]), | |
hr(), | |
createUser.DOM | |
]) | |
}; | |
return { | |
DOM: response$.map(render), | |
HTTP: xs.merge( | |
// get users initially | |
xs.of(getUsers).take(2), | |
// handle create POST requests | |
createUser.HTTP, | |
// map create POST requests to another get to update data | |
createUser.HTTP.mapTo(getUsers) | |
) | |
} | |
}; | |
export default Welcome; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment