Mark Karavan
Mark Karavan

Reputation: 2674

Rendering local files in Elm with FileReader

I am using simonh1000's file-reader to upload image files. I would like elm to display the contents of a file when they are drag-n-dropped.

I should be able to render an img with blob data directly in the src, but I am not sure how to get FileReader.elm to output a src value acceptably.

[ div [ class "panel" ] <|
    [ p [] [ text "User Avatar" ]
    , div dzClass
        [ label
            [ class "custom-file-upload"]
            [ input
                [ Html.Attributes.type_ "file"
                , FileReader.onFileChange OnDrop
                , multiple False
                ]
                []
            ]
        ]
    , case model.file of
        Just nf ->
            div []
                [ span [] [ text nf.name ]
                , button [ onClick (StartUpload UserAvatar) ] [ text "Upload" ]
                , div [] [ small [] [] ]
                , img [src FileReader.parseSelectedFiles] [] -- This is what I want to do
                ]

        Nothing ->
            text "" 

However, the compiler gives me this error:

The argument to function `src` is causing a mismatch.

488|                                 src FileReader.parseSelectedFiles
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Function `src` is expecting the argument to be:

    String

But it is:

    Json.Decode.Decoder (List NativeFile)

Detected errors in 1 module.

All of the methods in FileReader.elm seem to have return types that are incompatible with the src attribute. Is there a practical way to convert this?

Upvotes: 2

Views: 352

Answers (1)

Alex Lew
Alex Lew

Reputation: 2124

It looks like you're currently trying to handle all of this in your view function. The way the Elm Architecture works, you'll need to integrate the behaviors of your view and update functions. The pipeline you want is:

  • Display a file selection dialog (in the view, you already do)
  • User selects a file (causes an OnDrop message)
  • Parse the file content into a data URL (invoke the readAsDataUrl command)
  • Store the parsed data URL in the model, perhaps converted to a string using Json.Decode.decodeValue
  • In your view function, if available, use the parsed data URL as the argument to src.

Your update will need to handle the two cases of an initial drop (OnDrop) and successful/unsuccessful parsing of the data inside:

update msg model =
   case msg of
      OnDrop (nf :: _) -> 
         model ! [Task.attempt DataURL (FileReader.readAsDataUrl nf.blob)]
      DataURL (Ok dataUrl) ->
         { model | parsedDataUrl = Json.Decode.decodeValue Jason.Decode.string dataUrl |> Result.withDefault "error.png" }
      ...

In your view, pass the model.parsedDataUrl in as the image's source.

Upvotes: 5

Related Questions