Multi page applicatiion
See original GitHub issueI’m trying understand how a multi page application is going to look.
Logically, a multi page application should be split on files where each file represents a page or a subset of the application. However, in the AllControls
sample I’ve seen that the navigation stack and all pages are maintained in the same file.
It makes me wonder, how will a complex application will look like? Will it have only one file with init
, update
and view
and ViewElements will be split on modules, or it will have a few modules with init
, update
and view
?
Issue Analytics
- State:
- Created 5 years ago
- Comments:29 (4 by maintainers)
Top Results From Across the Web
Single-page application vs. multiple- ...
Multi -Page Application ... Multiple-page applications work in a “traditional” way. Every change eg. display the data or submit data back to server...
Read more >Single Page Apps vs. Multi-Page Apps
As the name suggests, a multi-page application is an app that has more than one page with static information(text, images, etc.) and links...
Read more >Single Page Applications vs Multiple ...
Multi page applications are the traditional web applications that reload the entire page and displays the new one when a user interacts with...
Read more >When to use Multi-Page Apps? - Bits and Pieces
Multi -Page Applications ... As its name suggests, MPAs consist of more than one page. With MPAs, any data change or transfer to...
Read more >Single-page application vs Multi-page application: What Is ...
Examples of Multi page applications are eCommerce websites, blogs, forums, other sites that sell products and various services. eBay and Amazon ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Only if passing parts of the app state as parameters is considered page-specific logic. 😃
I didn’t mean that you needed the parent
update
orview
to perform child-specific transformations. You can just pass directly the parts of the parent model that the child needs. And often it’s easy to pass too much rather than exactly what’s needed, which is a perfectly valid thing to do in order to simplify. For example, if a page needs just some of the signed-in user info, pass it the wholeAppModel.SignedInUser
object even though it might not need all of it.Furthermore, building on your authentication-related case: What if the user isn’t signed in? Then
GlobalModel.SignedInUser
might beNone
, and very little in the childview
function would make sense. If you instead make the childview
function acceptSignedInUser
(non-option
), then first of all it becomes simpler by itself, and secondly you can only call it when you actually have aSignedInUser
(so that you can do proper view composition usingOption.map
/pattern matching, rendering something different if it’sNone
).I get what you mean, but you might need to extend
GlobalState
when adding a new view. That would be the same as just passing whatever is needed as params to the childupdate
/view
. And you have globally forced the added complexity of updating two models instead of one.Personally I think that passing the required params is clearer, simpler, and safer. But I appreciate that that different people think differently. Do let us know if you try both and find out that
GlobalState
is actually better in some way! 😃I agree with basically everything you just said. 😃
Of potential relevance to the topic at hand; I had an epiphany last night while working with/on Elmish.WPF. I’ll write a lengthy issue on the Elmish.WPF repo describing it (because it requires some breaking changes to Elmish.WPF), but I’d like to share my insights in this conversaion because I think it might be useful. The gist of it is this:
The problem with state duplication/synchronization in components is only really a problem if you have a separate
view
(or for Elmish.WPF,bindings
) function for that component which does not know about the parent model (i.e., does not receive relevant parts of the parent model as parameters), because this means that any required parent state (whether duplicated or derived) must be stored in the child model so it can be used in theview
. Cue all the previously mentioned state duplication/synchronization woes.Thankfully, by following Richard Feldman’s advice in this comment, you can split the model, message, update, and view separately and think of terms of “which information do I need” and end up with something that is almost its own separate MVU component, with a lot of the same benefits (understandable/testable in relative isolation) but avoids the drawbacks (state duplication/synchronization).
As an example, let’s say you have a complicated form with a lot of inputs. Understandably, you might not want all those fields directly in your root state, or all the messages as cases directly in your top-level message type. What you can do is:
Model
by writing a separate child model for the form inputs (and other form state)Msg
by writing a separate message type for the form messages, and a wrapper for those messages in the main message type (just as with a normal sub-component)update
by delegating all child messages to a separate update function for the child model and the child message type, and which also accepts any needed part of the global stateview
like you normally split it, accepting the child model, and any needed parts of the parent model.Splitting all of these by themselves and thinking in terms of which information they need avoids the problem of state duplication/synchronization, and lands you with something that is as easily testable as a separate component.
For example, let’s say you have some autocomplete in the form, based on a global list of entities in your main model. This autocomplete is the responsibility of the
view
, which must then accept that list of entities (along with the child model). And as another example, let’s say that a particular branch of the child update function also needs access to a global list of entities. This must then be passed to the childupdate
function, so that any branch that needs it has the latest list of entities at the time of an update.If we had designed the form as a completely separate sub-component (not accepting any “extra” data in any functions), the model would need a duplicate of the parent’s entity list to be used in the
update
andview
functions, and the parentupdate
would have to remember to update the child model whenever the relevant parts of the parent model changed. It’s very easy to miss.Care must of course be taken to not store any derived state in the child model, too (the same goes for the parent model by itself, of course, but it’s more insidious for child models). For example, a list of pre-processed entity names must not be stored for the autocomplete. If this was done, that would mean that if the parent model’s list of entities is updated, the child model’s entity name list becomes out of date (because the child model is only updated for child messages, so it doesn’t know that the parent model’s list of entities is updated). Any derived state must be the responsibility of the
view
function (which must then accept the relevant parent state, and thus always sees the up-to-date data).(Note that in some cases, I’ve seen derived state as a proposed solution for avoiding repeating expensive computations, but AFAIK a sprinkling of memoization and lazy views solve this without the need for duplicated state.)
Let me know if this was unclear.