Try binding the view of the ListModel:

1
2

    ...
let myDoc = mylist.View.DocSeqCached (fun (k,s) -> p [] [text (k + ": " + s)])
By on 1/7/2018 3:18 PM ()

This does indeed work, although the reason why it does is a bit subtle.

The original problem is indeed with the mylist.Doc method. It is actually equivalent to the versions of DocSeqCached that take an additional "key" function, where the key is the ListModel's key.

1
2
3

mylist.Doc(fun (k, s) -> ...)
// is equivalent to:
mylist.View.DocSeqCached(fst, fun (k, s) -> ...)

So if you are adding an item with an already existing key (aka changing an item), then it is not redrawn at all. The purpose of this version of DocSeqCached is to be used when items might be added or removed, but not changed. Obviously this is not what you want here. Instead you have two options:

• If you are okay with completely redrawing the p element every time the corresponding item changes, then Adam's solution is correct: myList.View.DocSeqCached(fun (k, s) -> ...). This simply doesn't use the fst key function, and checks equality on the whole pair to decide if it needs redrawing.

• If you want to create the p element only once per item and have its contents react to the change, then you can use the other overload of mylist.Doc, which takes a View<'T> instead of a 'T:

1
2
3
4
5
6
7

mylist.Doc(fun k v ->
p [] [
textView (v.Map(fun (k, s) -> k + ": " + s))
// or equivalently, with the new .V syntax:
text (fst v.V + ": " + snd v.V)
]
)

Here, when you change an item, the function is not called again, but the value of the view v is changed. So the p element stays and its text content reacts.

By on 1/8/2018 3:42 AM ()

Still a question regarding your choice for .Doc: Why would you not redraw item changes? Is there a performance penalty (next to the redraw effort)? Do you see situations where the user would not want to redraw changes?

By on 1/9/2018 12:41 AM ()

Sorry, forget my previous question, I think I understand it now. BTW the new V syntax is really cool.

By on 1/11/2018 3:19 AM ()

Hi Loïc, thanks a lot for this detailed answer. I am still digesting it, and may come back later with questions/comments on it.

But I have an immediate question: If your answer explains why I don't see the text "AJAX callback" in the "110" case of my example (as I would have expected, and actually see with Adam's version), why does then (for example) the "101" case show the changed text "after AJAX call" instead of the original "before AJAX call"??

To me the output of my example indicates that the behavior is different for the async and sync cases. How is this explained by the different type of caching that you mention?

By on 1/8/2018 9:08 AM ()

why does then (for example) the "101" case show the changed text "after AJAX call" instead of the original "before AJAX call"??

That's because the "101" case is set to "after AJAX call" before the doc is rendered.

In case this wasn't clear: The ListModel's content does always get updated regardless of when the change is made. The difference is whether .Doc() or .DocSeqCached() then reflects this change in the rendered HTML. Your initial code displays whatever the item's value was at the time of first rendering the doc. It only ignores changes done after that, which is why only the asynchronous changes were ignored.

In fact, even moving the "after AJAX call" items after the call to RunById will not make them ignored, because RunById itself is asynchronous (changes are batched for performance), so the doc is still not yet rendered. They will be ignored if they are run in an on.afterRender:

1
2
3
4
5
6
7
8

        div [
on.afterRender (fun _ ->
] [myDoc] |> Doc.RunById "main"