Currently, XmlNode is like
type XmlNode =
| ParentNode of XmlElement * XmlNode list // An XML element which contains nested XML elements
| VoidElement of XmlElement // An XML element which cannot contain nested XML (e.g. <hr /> or <br />)
| EncodedText of string // XML encoded text content
| RawText of string // Raw text content
I would propose to expand this with new union case:
type XmlNode =
...
| ManyNodes of XmlNode list // many nodes without nesting
While expanding "ManyNodes", the child nodes would be at the same level as parent. This would allow creating functions that "spread" their output at siblings. Currently, you have to use List.concat for behaviour like this.
Example implementation in this commit https://github.com/vivainio/StaticSharp/commit/29a1f9af6a4080817590108ab3709104563722be (on my vendored fork of GiraffeViewEngine)
Hi,
Can you maybe quickly paste me a more concrete example of where in an app that would be useful and how you are doing it currently with Giraffe which makes it I guess more difficult or less elegant at the moment? Because I think you should be able to just spread output at siblings from a function via yield or yield! but maybe that's not what you want or maybe that's different from what you are trying to address here, so I just want to make sure I fully understand your proposal.
Mind that currently each node in the view engine represents some actual HTML content (div, span, etc.) and something like many would sort of break that current design and user expectation. I am not saying that's a deal breaker, but I want to consider all possible options to achieve what you want and maybe not have to break the existing design if possible.
@vivainio hey just wanted to ping you regarding this issue. Maybe if you have 5 min time and see my previous message. If this is still relevant then I'd be happy to figure out what to do next from here and if there's some work required then I might be able to get it into the next release which is already in the cooking. Thanks!
@dustinmoris hey, I've let it wallow for a while, but I'm not sure what you mean by "yield!". I am composing DOM by normal lists, not CE's.
So I have
let all =
div [] [
span [][]
many [
div[] []
div[] []
]
span [][]
]
Where many [] could be replaced by function call like so:
let all =
div [] [
span [][]
myparagraphs()
span [][]
]
How would you do it instead, without introducing higher depth at "myparagraphs" level? concatting lists?
Hi @vivainio
Currently you can do this:
let myparagraphs() =
[
div[] []
div[] []
]
let ids = [ 1; 2; 3 ]
let all =
div[] [
div [] [
// The list of items of this div is being dynamically created,
// therefore the items are being returns via `yield` or `yield!`
// if a list of items is to be flattened
yield span [][]
yield! ids |> List.map (fun i ->
let id = i.ToString()
div [ _id id ] [ rawText id ])
yield! myparagraphs()
yield span [][]
]
// This div has a list of static items, so here no yield required again
div [] [
span [] []
span [] []
span [] []
]
]
Maybe the usage of yield in this context is not super elegant, but it sort of indicates how the list is being constructed. This is not a GiraffeViewEngine feature, but just how F# let's you mix in a dynamically generated list into an existing list declaration.
This would be the same for any other normal F# collection type:
let moreIds = [ 4; 5; 6 ]
let ids =
[
yield 1
yield 2
yield 3
yield! moreIds
]
@dustinmoris that's really nice! I didn't actually know that.
Still, it requires you to prefix all the other elements with "yield" which reduces the appeal a bit, but it's definitely improvement over concatting in the outside structure
@vivainio
Yes, it's not super elegant, but this is sort of the benefit and curse of a very expressive language like F#. I see it the same as for example having to explicitly |> ignore a function which returns something which I don't want to use. On one hand it is very verbose and can be seen like noise, on the other hand it shows me and any other reader of the code exactly what is going on.
In the case of yield you are basically dynamically populating the elements of a list, so you either generate the list statically or dynamically and if dynamically then all items must be added that way. If you have a long collection and you see yield at the top, at least you know what is going on further down as well.
What you are asking for is essentially a language feature which would allow us to dynamically add items in a list declaration in a less verbose way, that's why I tried to highlight that this is not a Giraffe thing but a general F# thing.
However, if your items are at the very top or bottom of a list (e.g. comments in a comment system being added dynamically at the bottom), then you could also just merge two lists in order to avoid the yield:
let myparagraphs() =
[
div[] []
div[] []
]
let all =
div[] [
div [] ([
span [] []
span [] []
span [] []
]
|> List.append (myparagraphs()))
]
I think this can probably be closed now?
@dustinmoris yes, though I'd still like the feature so that I can remove my private fork :)
@vivainio How about this, render your node list as fragment first then wrap it with rawText:
/// var fragment: XmlNode list -> XmlNode
let fragment = renderHtmlNodes >> rawText
/// var gtag : XmlNode list
let gtag =
[ script [ _src "xxxxxxxx"; _async ] []
script [] [ rawText """xxxxxxxxxxxxxx""" ]
]
let myHead =
head [] [
meta [] []
fragment gtag
meta [] []
]
That's a great idea that covers all the use cases I think.
On Tue, Jan 29, 2019, 6:07 AM Zeeko <[email protected] wrote:
@vivainio https://github.com/vivainio How about this, render your node
list as fragment first then wrap it with rawText:let fragment = renderHtmlNodes >> rawText
let gtag =
[ script [ _src "xxxxxxxx"; _async ] []
script [] [ rawText """xxxxxxxxxxxxxx""" ]
]
let myHead =
head [] [
meta [] []
fragment gtag
meta [] []
]—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/giraffe-fsharp/Giraffe/issues/299#issuecomment-458400306,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAiCCz32u4opMDOb2wcIXF2iuEuY1Mooks5vH8kWgaJpZM4WR5r0
.
@ZeekoZhu That's a great way of doing this :)
Closing this, made redundant by @ZeekoZhu suggestion