question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Multi page applicatiion

See original GitHub issue

I’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:closed
  • Created 5 years ago
  • Comments:29 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
cmeerencommented, May 14, 2019

Of course I could just pass the information needed for a particular page to its update and view functions, but that would require to have some “page specific” logic in App.fs.

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 or view 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 whole AppModel.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 be None, and very little in the child view function would make sense. If you instead make the child view function accept SignedInUser (non-option), then first of all it becomes simpler by itself, and secondly you can only call it when you actually have a SignedInUser (so that you can do proper view composition using Option.map/pattern matching, rendering something different if it’s None).

This way its all treated the same way, so wiring up a new page becomes a no-brainer.

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 child update/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! 😃

2reactions
cmeerencommented, May 7, 2019

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 the view. 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:

  • You split Model by writing a separate child model for the form inputs (and other form state)
  • You split 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)
  • You split 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 state
  • You split view 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 child update 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 and view functions, and the parent update 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.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found