Проблема с конструктором Websharper/Web.Control

Желая настроить Google DataVisualization, я хотел параметризовать конструктор элемента управления, но по какой-то причине он не работает, когда я передаю параметр inputDataTableas.

Вот данные по умолчанию, которые я использую:

module Example = 
  let headers = [|"Country"; "Population (mil)"; "Area (km2)" |]
  let inputDataTable = [|
    [|box "CN"; box 1234000; box 9640821|]
    [|box "IN"; box 1133000; box 3287263|]
    [|box "US"; box 304000; box 9629091|]
    [|box "ID"; box 232000; box 1904569|]
    [|box "BR"; box 187000; box 8514877|]
  |]

Код для создания диаграммы на веб-странице.

let GeoChart headers inputDataTable =
    Div []
    |>! OnAfterRender (fun container ->
          let dataFormatter = DefaultOptions.NumberFormatter ""
          let options = DefaultOptions.GeoChart
          let visualization = new GeoChart(container.Dom)
          let data = Data.GeoMapData headers inputDataTable
          dataFormatter.format(data, 1)
          visualization.draw(data, options)
        )

«Ожидаемый» контроль.

type GoogleGeoChartViewer(headers, inputDataTable) =
  inherit Web.Control()

  [<JavaScript>]
  override this.Body = 
    Google.Charts.GeoChart headers inputDataTable :> _

И тест с использованием базового шаблона.

let HomePage =
  let headers = Google.Example.headers 
  let inputDataTable = Google.Example.inputDataTable
  Skin.WithTemplate "HomePage" <| fun ctx ->
      [
          Div [Text "HOME"]
          Div [new Controls.EntryPoint()]
          Div [new Controls.GoogleGeoChartViewer(headers, inputDataTable)]
          Links ctx
      ]

Однако, когда я использую этот код: географическая диаграмма не отображается.

Но если я удалю inputDataTable из списка параметров и использую:

let GeoChart headers =
    Div []
    |>! OnAfterRender (fun container ->
          let dataFormatter = DefaultOptions.NumberFormatter ""
          let options = DefaultOptions.GeoChart
          let visualization = new GeoChart(container.Dom)
          let inputDataTable = Example.inputDataTable
          let data = Data.GeoMapData headers inputDataTable
          dataFormatter.format(data, 1)
          visualization.draw(data, options)
        )

Оно работает...

Однако массив headers не вызывает никаких проблем.

Кто-нибудь знает, что происходит?

Спасибо за ваши идеи.


person user1251614    schedule 05.11.2014    source источник


Ответы (1)


Что происходит, так это то, что значения, переданные конструктору Web.Control, являются значениями на стороне сервера, которые сериализуются в JSON во время построения страницы на стороне сервера. Однако для типа obj нет сериализатора, поэтому сериализация вашего inputDataTable (типа obj[][]) завершается ошибкой. Напротив, в работающей версии inputDataTable создается на клиенте, поэтому сериализация не требуется.

Если inputDataTable действительно нужно поступать со стороны сервера, вам нужно использовать сериализуемый тип. Я не знаю точно, как вы заполняете свой объект данных в функции GeoMapData, но решение, вероятно, состоит в том, чтобы определить inputDataTable следующим образом:

let inputDataTable = [|
    ("CN", 1234000, 9640821)
    ("IN", 1133000, 3287263)
    ("US", 304000, 9629091)
    ("ID", 232000, 1904569)
    ("BR", 187000, 8514877)
  |]

а затем на стороне клиента сделайте что-то вроде:

let data = new Base.DataTable()
data.addRows(inputDataTable) |> ignore

Хитрость в том, что WebSharper представляет кортежи, используя массивы JavaScript на стороне клиента. И метод addRows, который обычно принимает obj[][], также имеет перегрузку, которая принимает 'T[]. Это позволяет вам написать правильно типизированный код F# выше, и он компилируется в точно такой же код JavaScript, как если бы вы передавали obj[][]. Этот трюк используется в этом примере (строка 155).

person Tarmil    schedule 06.11.2014
comment
Спасибо за объяснение. Я хотел иметь общую функцию, чтобы я мог добавлять на карту столько рядов, сколько захочу (не специально для географической диаграммы, но и для других, таких как диаграмма интенсивности), поэтому я использовал obj[ ][] вместо более простого (string * double ...)[]. Я просто удостоверюсь, что не использую obj, чтобы обойти это. - person user1251614; 06.11.2014