Open greglearns opened 4 years ago
I think it would depend on what you want to do... for example, do you have specific data that you want the list to start with? or do you just want to create some random data? Do you want the list size to be able to be changed? If so, how does data for the new item get created?
I have a vector of objects, and it would be nice to
1) use the slider (or something like it) to define the number of elements that are in the list (i.e., one could use the slider to "add" a new object to the list)
2) each object in the list can be edited just like the UploadRequest
example.
Currently, a record can have fields, but it can't have a field that is a list. I'd love to be able to handle a list.
Ah yeah, the current list
is made for filling in sample data.
Doing something fancier for list controls would need a control for dealing with that, and would also need to figure out how the UI design would work for the general case of lists.
With the current package, you could have a List (Control Record)
in your model, and you'd have to make your own UI for the slider and handle updating the List yourself based on the slider changes.
The main part I can't figure out (even as I have been pouring over the code), is how a nested value gets propogated up after being changed by an event. For example,
type Parent {
child: Child
}
type Child {
childValue: String
}
How does the onInput event occuring on the childValue
, get recorded in the Parent object?
Essentially the Control String
contains within it a current value of type String, and Control Child
contains a current value of type Child
. The Control Parent
also contains the current Parent
value, but also embedded in its view are functions that when given a new String
value will construct a new Child
value (with the String changed and all other fields the same as their current values), and then a new Parent
value (with child
changed and all other fields the same as their current values).
In summary, the way this works is by hiding update functions in closures that are stored within the Controls
.... storing functions in the model is not recommended by the Elm architecture for production code, which is why this package is called "debug controls", as it uses some tricks that should be avoided for building robust applications. But anyway, that's how it works.
UPDATE: I think I'm getting it... once I've fully understood it, I'll try to add some documentation to the code. So far, one critical part to understanding things is to realize that most of the time in this type signature, view_ : (Control a -> msg) -> Control a -> Html msg
that the (Control a -> msg)
is not a Msg constructor (something that will be passed to update
), but is instead a function that creates a new field
that will get wrapped in other functions that enable the record to be updated, and which is only wrapped at the last step by the actual Msg contructor which will then be passed to the update
function. So basically, the function that is finally passed to the update
function will look something like:
view (field "lastChild" (...last child's control) (field "childValue" (string "newly typed text from onInput") pipeline)) ...
It's hard to write (and, I don't think I have it quite right either yet), so I can see why you'd have a hard time documenting how this works!
ORIGINAL QUESTION: The whole approach you are taking is very cool, and I love that it enables rapid prototyping to explore a design before verifying that producing it the "right" (Elm) way is worth the time. Hiding the update functions is a good idea, ... and I think that's where I'm getting hung up on how the update event in a child (string) gets propagated back up to the parent (Object)
For the child string
, I can see how that happens here (when the child
onInput event occurs, the Html.Events.onInput string
event handler creates a replacement string
that has the new text input:
string : String -> Control String
string initialValue =
Control
{ currentValue = \() -> initialValue
, allValues =
\() ->
[ initialValue
-- , ""
-- , "short"
-- , "Longwordyesverylongwithnospacessupercalifragilisticexpialidocious"
-- , "Long text lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
]
, view =
\() ->
SingleView <|
Html.input
[ Html.Attributes.value initialValue
, Html.Events.onInput string
]
[]
}
(Side note: why do the string
and stringTextArea
controls have allValue
fields with extra strings in them instead of just [initialValue]
?)
But, how does the parent object
that contains the child string
get updated when the child string
gets updated? Because I can see where the msg
that I passed in (per the documentation) that will replace the root of the tree in my model gets mapped to the new table you create at https://github.com/avh4/elm-debug-controls/blob/master/src/Debug/Control.elm#L605 and the Parent
's children are getting viewed here: https://github.com/avh4/elm-debug-controls/blob/master/src/Debug/Control.elm#L590. But I don't see how when the Child
's childValue
string is updated via the onInput
https://github.com/avh4/elm-debug-controls/blob/master/src/Debug/Control.elm#L165, how that gets propagated back up the tree so that the Parent
view value gets updated.
As I look at the code, it is also possible that I don't understand https://github.com/avh4/elm-debug-controls/blob/master/src/Debug/Control.elm#L448 where I'm guessing the magic is occuring in the Html.map
.
Also, it's possible that I don't understand what the a
and b
actually are in field
(a
is the current field being decoded, but b
is either the Parent
or the ... http://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/latest
It's similar to how json-decode-pipeline works: If you have type alias Record = { a : String, b : Bool }
, then the control
record Record
|> field "a" (string "")
|> field "b" (bool False)
will have a view for both the string and the bool. If the string control changes, it applies the new string to Record
and ends up with a Control (Bool -> Record)
, and it then applies the current value of the bool control to create the new Record. If instead the bool control changes, then it uses the current value of type Bool -> Record
and applies the new Bool
value to create the new `Record.
Great project!
https://avh4.github.io/elm-debug-controls/
demonstrates how to create, view, and interact with (click on and change) aUploadRequest
, but how would one create and view aList UploadRequest
? It would be great to be able to have a list of NUploadRequest
s all of which are interactive. That's what I am trying to do with my own model. I have a record like:It's not clear to me from the documentation and code how do to this. I saw that there is a "list" Control, but that doesn't do what I am trying to do, and I haven't been able to figure out exactly how to do it. I'm having a bit of a hard time wrapping my head around exactly how all of this works -- I'm a good developer with functional experience so a lot of it makes sense, but the overall approach is translucent to me currently (though, I can see that it is very cool).
Thank you!