I'm bringing #294 back from the dead!
Now that we have state slice and modules, I thought it was kinda awkward module views are hard to use.
Introducing views!
https://gist.github.com/Pyrolistical/f3a4b1577741dd847aef73300f8f11c3

(I didn't re-cap the demo, but you can run the new code yourself it works like this!)
I wrote a HoA withViews and added a views parameter to the view function. This allows us to write stuff like
withViews(app)({
view(state, actions, {left, right}) {
return <main>
<left name="Left" />
<right name="Right" />
<p>Sum {state.left.count + state.right.count}</p>
</main>
},
modules: {
left: counter,
right: counter,
}
}, document.getElementById('app-entry'))
where counter is just a module
const counter = {
state: {
count: 0
},
view(state, actions, views) {
return <div>
<h1>{state.name} {state.count}</h1>
<button onclick={actions.down} disabled={state.count <= 0}>-</button>
<button onclick={actions.up}>+</button>
</div>
},
actions: {
down(state) {
return {
count: state.count - 1
}
},
up(state) {
return {
count: state.count + 1
}
}
}
}
I feel this is a missing piece to the modules story. Should we integrate this into the core or leave this as a HoA? You can see the source for withViews: https://gist.github.com/Pyrolistical/f3a4b1577741dd847aef73300f8f11c3#file-index-html-L83
@Pyrolistical What happens if instead of calling Counter(), you simply assign Counter to both left and right?
Still works lol, I was wondering about that....I'll simplify
@Pyrolistical Please add two more examples, (1) (easy) add a button to the main view that adds +1 to both left and right, and (2) (hard) a button that adds a new counter, so no more right or left, but a collection of counters.
I welcome you into the @zaceno and I war.
I am thankful you choose my side :tada:
@zaceno's thoughts : https://github.com/Zaceno/hyperapp-module-views
Mine : https://github.com/Swizz/hyperapp-module-view
Bonus : The HOA snippet to enhance modules https://gist.github.com/Swizz/3fdfd182e0000027632708947aa5c570/0f3c666f722f26cf343a7af874618ea2746d143a
@Swizz any links to the war history?
Perhaps also interesting (or at least "clever") I figured out yesterday. You can have module views without the HOA (or at least for as long we still have thunks which won't be for long from the looks of it ;) )
const counter = {
state: {
value: 0
},
actions: {
up: state => ({value: state.value + 1}),
down: state => ({value: state.value -1}),
view: (state, actions) => _ => <p>
<button onclick={actions.down}>-</button>
{state.value}
<button onclick={actions.up}>+</button>
</p>
}
}
app({
modules: {counter},
view: (state, actions) => <div>
<actions.counter.view />
</div>
})
All is happening on Slack, but we reached the fact we are not considering modules the same way.
I think this two statements will sum up the "war"
@zaceno : I agree we鈥檙e talking about two different things. You鈥檙e talking about composing mini apps that could work on their own into a big app. (I鈥檇 also like that by the way). I鈥檓 talking about grouping views that relate to the same state and actions into a module. They just seem so close, it can鈥檛 be entirely irreconcilable
@Swizz : So. You have summarized the view problem into only one question. What will be our definition of a module ? Is using a module using an utilities box, or adding a small app into our tree under a namespace ?
If many of us are claiming this feature, one way or another. We should consider asking this question (the bold one) to the community.
@Pyrolistical I am looking forward to see the two additional examples! 馃檹
The way I see it, this sort of module views are basically the same as getters/computeds The only thing is that they return a vtree rather than a number or string. (And a "traditional" getter only needs access to the state, not actions, I suppose) My point being: rather than having support for something named "views" specifically, I'd rather have support for general getters, and we can use getters for "pre-wired views" as well as other things.
@JorgeBucaran here are the goods, i appended to the gist.
I ran into #437 and #438, so I included #426 at the end of the gist to get around it.

Just a new action:
addBoth(state, actions) {
actions.bleft.up()
actions.bright.up()
// this or if you want a single update
// return {
// bleft: {
// ...state.bleft, // not needed in this case, but generally is
// count: state.bleft.count + 1
// },
// bright: {
// ...state.bright, // not needed in this case, but generally is
// count: state.bright.count + 1
// }
// }
}

This one was more tricky, I added a new many module, which takes in a module and allows you to add more
function many(module) {
return {
state: {
substates: []
},
view(state, actions) {
return state.substates.map((substate, i) => {
const subactions = {}
for (const action in module.actions) {
subactions[action] = () => {
const updatedSubstate = module.actions[action](substate)
actions.update({
substates: [...state.substates.slice(0, i), updatedSubstate, ...state.substates.slice(i + 1, state.substates.length)]
})
}
}
return module.view({
...substate,
...state.extraProps(i)
}, subactions)
})
},
actions: {
push(state, actions) {
return {
substates: [...state.substates, {
...module.state
}]
}
},
update(state, actions, data) {
return data
}
}
}
}
usage looks like:
modules: {
counters: many(counter)
}
the view looks like:
<h1>C. Counters, counters everywhere</h1>
<counters extraProps={(index) => ({name: `counter[${index}]`})} />
<button onclick={actions.counters.push}>Add Counter</button>
<p>Sum {state.counters.substates.reduce((total, {count}) => total + count, 0)}</p>
where counters is the from views
The reason why I went down this rabbit hole was lazy load modules!

function lazyLoad(modulePath) {
let loadedModule
const placeholderModule = {
init(state, actions) {
fetch(modulePath)
.then((response) => response.text())
.then((data) => {
loadedModule = eval(data)
actions.loaded()
})
},
state: {
loading: true
},
view(state, actions) {
if (!loadedModule) {
return h("p", {}, "loading...")
}
return loadedModule.view(state, actions)
},
actions: {
loaded(state, actions) {
return {
loading: false
}
}
}
}
return placeholderModule
}
usage
modules: {
someModule: lazyLoad('someModule.js')
}
which then leads to another question....this doesn't work for actions. currently there is no way to add actions dynamically
which then leads to another question....this doesn't work for actions. currently there is no way to add actions dynamically
Correct. There is no way to add actions dynamically.
Actually, #431 is a request to support dynamic actions.
However, it is possible to dynamically add actions by recreating the logic of initAction and calling it in a Thunk.
Oh, interesting.
Some API ideas for modules that can export one or more views.
view: {
Title: (state, actions) => (
<main>
<h1>{state.title}</h1>
</main>
)
}
view: {
Title: (state, actions) => components => (
<main>
<h1>{state.title}</h1>
</main>
)
}
view: {
Title: (state, actions) => components => props => (
<main>
<h1>{state.title}</h1>
</main>
)
}
I am going to close here as I have no intention to add views to modules in the near future, but I welcome HOA authors to give it a shot and show me this is the way of the future. 馃憢馃榿
EDIT: I welcome PRs to master too, if this can be implemented easily I'd love to know.
We can't do a proper PR as we have not decided yet how to consider modules :
@Swizz You are welcome to create a PR with your preferred API proposal if you want. At first I was opposed to #440, but then you convinced me and that's the way things are now! So, who knows? 馃帀
I don't want to make the decision on this. I need to talk a lot more with the community and @zaceno.
Here this is not API design but the final way modules will be for all/most the Hyperapp community
@JorgeBucaran @Swizz Should we open an issue to track discussions/proposals for modules?
@Mytrill @Swizz I closed here because:
...I have no intention to add views to modules in the near future, but I welcome HOA authors to give it a shot and show me this is the way of the future. 馃憢馃榿
and also:
...I welcome PRs to master too...
But who knows, in the long run we may want to add views to modules after all. For this to happen, there must be an API proposal that shows how module views improve our lives and not complicate them. I think before we can get there, though, we need to just build stuff.
You are right. I would prefer to use my modules into a real project first.
Most helpful comment
@JorgeBucaran here are the goods, i appended to the gist.
I ran into #437 and #438, so I included #426 at the end of the gist to get around it.
(1) (easy) add a button to the main view that adds +1 to both left and right
Just a new action:
(2) (hard) a button that adds a new counter, so no more right or left, but a collection of counters.
This one was more tricky, I added a new
manymodule, which takes in a module and allows you to add moreusage looks like:
the view looks like:
where
countersis the fromviews