В fable-elmish, как вызвать команду после того, как представление было отображено? - PullRequest
0 голосов
/ 05 октября 2018

Я создал приложение, которое загружает некоторые данные и строит их с помощью Google Charts на div с определенным Id.

. Это прекрасно работает на статической странице.Однако при использовании меню для переключения между несколькими представлениями я изо всех сил пытаюсь перерисовать диаграмму при переключении обратно на соответствующий вид.

Если я использую команду для перерисовки диаграммы при обновлении модели, запросне удается, потому что конкретный div еще не обработан.

Есть ли способ вызвать команду, когда этап View в Model-View-Update завершен?Или лучший способ перерисовать диаграмму?

Пример кода приведен ниже (не запускается, но должен быть простым в использовании).Щелчок по кнопке «Получить данные» работает нормально, но если вы затем переключитесь на другую страницу и вернетесь назад, я не смогу найти способ снова нарисовать диаграмму.

module Client

open Elmish
open Elmish.React

open Fable.Helpers.React
open Fable.Helpers.React.Props

open Fulma

type View = ShowChart | NoChart
type Model = { View: View; Values: float[] option }

type Msg =
    | RequestValues
    | ReceiveValues of float[]
    | PageWithChart
    | PageWithoutChart
    | DrawChart

let init () : Model * Cmd<Msg> =
    { View = ShowChart; Values = None }, []

let update (msg : Msg) (currentModel : Model) : Model * Cmd<Msg> =
    match msg with
    | RequestValues -> currentModel, Cmd.ofMsg (ReceiveValues [| 1.0; 3.0; 2.0 |])  // normally this would be a long-running process
    | ReceiveValues values -> { currentModel with Values = Some values }, Cmd.ofMsg DrawChart
    | DrawChart ->
        match currentModel.Values with
        | Some values ->
            let series = TheGamma.Series.series<_, _>.values values
            let chart = TheGamma.GoogleCharts.chart.line series
            TheGamma.GoogleCharts.chart.show chart "Chart1"
        | None -> ()
        currentModel, []
    | PageWithChart ->
        match currentModel.Values with
        | Some _ -> { currentModel with View = ShowChart }, Cmd.ofMsg DrawChart // FAILS as the div with Id="Chart1" does not exist yet
        | None -> { currentModel with View = ShowChart }, []
    | PageWithoutChart -> { currentModel with View = NoChart }, []

let button txt onClick =
    Button.button
        [ Button.OnClick onClick ]
        [ str txt ]

let view (model : Model) (dispatch : Msg -> unit) =
    match model.View with
    | NoChart ->
        div []
          [ div [] [ str "Page without chart" ]
            button "Show page with chart" (fun _ -> dispatch PageWithChart) ]
    | ShowChart ->
        div []
          [ div [] [ str "Page with chart" ]
            button "Get data" (fun _ -> dispatch RequestValues)
            button "Show page without chart" (fun _ -> dispatch PageWithoutChart )
            div [ Id "Chart1" ] [] ]

#if DEBUG
open Elmish.Debug
open Elmish.HMR
#endif

Program.mkProgram init update view
#if DEBUG
|> Program.withConsoleTrace
|> Program.withHMR
#endif
|> Program.withReact "elmish-app"
|> Program.run

HTML-код просто:

<!doctype html>
<html>
<head>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <title>SAFE Template</title>
    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">

    <link rel="shortcut icon" type="image/png" href="/Images/safe_favicon.png"/>
</head>
<body>
    <div id="elmish-app"></div>
    <script src="./js/bundle.js"></script>
</body>
</html>

Ответы [ 2 ]

0 голосов
/ 06 октября 2018

Вы можете определить, когда элемент реагирования без сохранения состояния был смонтирован, используя атрибуты Ref.

Например, вот что я использую в своем производственном приложении:

div [ Id "Chart1"
      Ref (fun element ->
          // Ref is trigger with null once for stateless element so we need to wait for the second trigger
          if not (isNull element) then
              // The div has been mounted check if this is the first time
              if model.IsJustLoaded then
                  // This is the first time, we can trigger a draw
                  dispatch DrawChart 
          )
 ]
    [ ]

Другой путь - преобразовать div [ Id "Chart1" ] [ ] в компонент реагирования с состоянием и вызвать ничью, когда был вызван componentDidMount.

0 голосов
/ 06 октября 2018

Немного сложно давать конкретные предложения, не видя ваш код, но вот общее представление о том, как вы можете с этим справиться.На вашем месте, вероятно, я бы использовал реализацию Promise из Fable Powerpack и настроил бы ее так:

  • Код рисования диаграммы не рисует диаграммутем не менее, он просто возвращает обещание, которое, после разрешения, нарисует диаграмму.
  • Сразу после div в функции представления используйте this hack , чтобы вызвать некоторый код Javascript, который разрешитОбещание, запуск события draw ().(Или, как предложено в другом ответе на этот вопрос , поместите этот код в элемент script в нижней части вашего представления, чтобы его выполнение не блокировало ничего от рендеринга, а затем внутри сценария.это то место, где вы могли бы выполнить это обещание).

Извините, это так расплывчато;если бы я увидел ваш код, я мог бы сделать более конкретные предложения.В частности, я не знаю, как лучше всего справиться с передачей этого обещания: хранить его в модели кажется плохой идеей (хранить что-то изменяемое в неизменяемой модели данных ... yikes), но я не придумаллучшая идея еще.Но что-то связанное с обещаниями, похоже, будет лучшим способом справиться с этой ситуацией.

...