This question is kind of silly but i didn't find a straight forward solution.
Assuming I have a model that resembles this: - at least this big.
initModel =
{ selectedCategory = "Vacantion"
, context = "root/Work"
, abstractSyntaxTree =
[ { categoryName = "Work"
, categoryContent =
[]
}
, { categoryName = "Vacation"
, categoryContent =
[ { folderName = "Hawaii"
, folderContent =
FolderContent
( [ { folderName = "Booking"
, folderContent = FolderContent ( [], [] )
}
]
, [ "flightTicket.jpg" ]
)
}
]
}
]
}
Question: How can i display it in the browser so that it looks good? - nothing fancy - just to see what's happening like a quick debugger..
What I've tried so far:
view =
div []
[ pre [style [("width", "300") ] ] [ text (toString model)]
]
Works great on smaller models but on this case, I get this long -single line - formatted json like structure:
I think this is the Problem: The prettify extension i installed in google chrome doesn't know how to deal with strings that do not contain \n
in them. To check for this, i manually added a \n
- and that string was split on the second row, as expected.
The output form text (toSting model)
- it's a string that has no \n
in it - so that's why everything is displayed on a single line in the browser - regardless of the 300 px limit on width.
Splitting up the string - by adding \n
by myself - works except i don't know where exactly to add the \n
. To make it dynamic, this requires a full-flagged parser for the model. A way to know where the expression starts, where is the next matching bracket .. etc. I'm not good enough to build this parser. I feel i'm over complicating this stuff. Must be a better solution..
How do you folks do it?
Elm does not allow you to enumerate over the items in a record. For type-safety no doubt. So there is no "clean" way to do display a record nicely.
UPDATE: The solution below will not work anymore as of Elm 0.19.
It relies on a (deprecated) function toString
that would convert any type into a string.
Since Elm 0.17 (when I made this), Elm had already released a great debugger in 0.18, which already has a feature to follow model and messages in a separate window.
For debugging, you can do some trickery with toString
to print it more neatly.
Below is example code, which you can copy/ paste to elm-lang.org/try.
Simply pass any record to the viewModel
function, and it displays it in the browser.
It is quite crude, and probably not very performant with large records, but it will do the job..
import Html exposing (Html, text, div, p, pre)
import Html.Attributes exposing (style)
import String
quote = "\""
indentChars = "[{("
outdentChars = "}])"
newLineChars = ","
uniqueHead = "##FORMAT##"
incr = 20
model =
{ name = "Abe"
, age = 49
, someTuple = (18,49)
, relatives = [ "Claire", "Bill" ]
, comments = "any special characters like []{}, will not be parsed"
, cars = [ { brand = "BMW", model = "535i" } ]
}
viewModel : a -> Html msg
viewModel model =
let
lines =
model
|> toString
|> formatString False 0
|> String.split uniqueHead
in
pre [] <| List.map viewLine lines
viewLine : String -> Html msg
viewLine lineStr =
let
(indent, lineTxt) = splitLine lineStr
in
p [ style
[ ("paddingLeft", px (indent))
, ("marginTop", "0px")
, ("marginBottom", "0px")
]
]
[ text lineTxt ]
px : Int -> String
px int =
toString int
++ "px"
formatString : Bool -> Int -> String -> String
formatString isInQuotes indent str =
case String.left 1 str of
"" -> ""
firstChar ->
if isInQuotes then
if firstChar == quote then
firstChar
++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
else
firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
else
if String.contains firstChar newLineChars then
uniqueHead ++ pad indent ++ firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
else if String.contains firstChar indentChars then
uniqueHead ++ pad (indent + incr) ++ firstChar
++ formatString isInQuotes (indent + incr) (String.dropLeft 1 str)
else if String.contains firstChar outdentChars then
firstChar ++ uniqueHead ++ pad (indent - incr)
++ formatString isInQuotes (indent - incr) (String.dropLeft 1 str)
else if firstChar == quote then
firstChar
++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
else
firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
pad : Int -> String
pad indent =
String.padLeft 5 '0' <| toString indent
splitLine : String -> (Int, String)
splitLine line =
let
indent =
String.left 5 line
|> String.toInt
|> Result.withDefault 0
newLine =
String.dropLeft 5 line
in
(indent, newLine)
main =
viewModel model
By now we have a nice package for 0.19.
elm install ThinkAlexandria/elm-pretty-print-json
And run
json = """{"name": "Arnold", "age": 70, "isStrong": true,"knownWeakness": null,"nicknames": ["Terminator", "The Governator"],"extra": {"foo": "bar","zap": {"cat": 1,"dog": 2},"transport": [[ "ford", "chevy" ],[ "TGV", "bullet train", "steam" ]]}}"""
{-| Formating configuration.
`indent` is the number of spaces in an indent.
`columns` is the desired column width of the formatted string. The formatter
will try to fit it as best as possible to the column width, but can still
exceed this limit. The maximum column width of the formatted string is
unbounded.
-}
config = {indent = 4, columns = 80}
-- run prettifier
Result.withDefault "" (Json.Print.prettyString config json)
-- result
{-
{
"extra": {
"transport": [
[
"ford",
"chevy"
],
[
"TGV",
"bullet train",
"steam"
]
],
"zap": {
"dog": 2,
"cat": 1
},
"foo": "bar"
},
"nicknames": [
"Terminator",
"The Governator"
],
"knownWeakness": null,
"isStrong": true,
"age": 70,
"name": "Arnold"
}
-}
I found this in slack : elm-debug-transformer
Not rely what i asked for, because for example im gonna need to make it work with node.js also, but still seems like good solution for now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With