Posty z tej serii:

Wspólną cechą omawianych do tej pory przykładów jest to, że dane wyświetlane są na jednej stronie. W rzeczywistości systemy webowe zawierają wiele stron. Adres aktualnie przeglądanej strony pokazywany jest w pasku przeglądarki internetowej. W Bolero taki efekt można uzyskać, wykorzystując mechanizm o nazwie Routing.

Bolero nie definiuje pojęcia strony. Mamy do dyspozycji widok, który sami musimy zaprojektować pod wymagane potrzeby. Weźmy dla przykładu aplikację, która zawiera trzy przyciski:

  • Counter
  • EnterValues
  • ViewComponents

Po kliknięciu odpowiedniego przycisku, widok powinien pokazać jedną z funkcjonalności omawianych w poprzednich artykułach. Taki efekt możemy uzyskać, modelując kliknięty przycisk, a następnie na podstawie wartości modelu, wyświetlić prawidłowy HTML template. W ostateczności widok będzie się zmieniał, ale adres w pasku przeglądarki pozostanie ten sam. Dla uproszczenia poniższy przykład wyświetla prosty napis załadowanej funkcjonalności:

Picture1
Picture2

Picture3
Picture4

Picture5
Picture6

Jeśli chcemy, aby każdy z wyświetlanych widoków posiadał swój unikalny adres, wyświetlany w pasku przeglądarki, musimy skorzystać z mechanizmu Routing’u.

Zaprojektujmy oraz zaprogramujmy taki efekt. Zamienimy jedną rzecz w stosunku do powyższego przykładu - zamiast wyświetlać przyciski, wyświetlimy linki. Dzięki temu zobaczymy, w jaki sposób możemy skorzystać z jednej z właściwości Routing’u pozwalającej na pobranie adresu URL dla wybranej strony.

Kod opisanych przykładów znajdziesz na GitHub’e.

Końcowy wynik będzie wyglądał tak:

Picture7
Picture8

Picture9
Picture10

Picture11
Picture12

Picture13
Picture14

W pierwszym kroku tworzymy plik main.html z poniższym kodem i umieszczamy w katalogu wwwroot:

<div>Choose:</div>
<a href="${MainLink}">Main</a>&nbsp;&nbsp;
<a href="${CounterLink}">Counter</a>&nbsp;&nbsp;
<a href="${EnterValuesLink}">EnterValues</a>&nbsp;&nbsp;
<a href="${ViewComponentsLink}">ViewComponents</a>&nbsp;&nbsp;
<br />
${Body}

Holes zdefiniowane w atrybutach href reprezentują linki do poszczególnych stron. $Body reprezentuje zawartość aktualnie wyświetlanej strony.

Definiujemy proste Templates dla poszczególnych stron:

  • Counter - plik wwwroot/counter.html
<span>Counter</span>
  • EnterValues - plik wwwroot/entervalues.html
<span>EnterValues</span>
  • ViewComponents - plik wwwroot/viewcomponents.html
<span>ViewComponents</span>

Modelujemy dostępne strony oraz ustawiamy stronę startową:

type Page =
    | [<EndPoint "/">] Main
    | [<EndPoint "/counter">] Counter
    | [<EndPoint "/entervalues">] EnterValues
    | [<EndPoint "/viewcomponents">] ViewComponents

type Model =
    {
        page: Page
    }

let initModel =
    {
        page = Main
    }

Union Type Page reprezentuje strony. Atrybut EntryPoint pozwala konstruować wzorce dla ścieżki URL. Przykładowo definicja [<EndPoint "/counter/{id}">] wymagałby podania w URL id licznika. W naszym przykładzie nie są wymagane żadne parametry. W takim przypadku można pominąć atrybut EntryPoint, ale zostawiamy go dla lepszej czytelności.

Główną stroną jest strona, która nie wyświetla nic (patrz screen powyżej).

Definiujemy message, który będzie ustawiał stronę:

type Message =
    | SetPage of Page

Definiujemy logikę funkcji update, która zaktualizuje stan o wybraną stronę:

let update message model =
    match message with
    | SetPage p -> { model with page = p }

Definiujemy Type Templates dla stron:

type MainTemplate = Template<"wwwroot/main.html">
type CounterTemplate = Template<"wwwroot/counter.html">
type EnterValuesTemplate = Template<"wwwroot/entervalues.html">
type ViewComponentsTemplate = Template<"wwwroot/viewcomponents.html">

Definiujemy funkcję view, która wyświetli widok w zależności od stanu modelu:

let view model dispatch =
    let main =
        MainTemplate()
            .MainLink(router.Link Main)
            .CounterLink(router.Link Counter)
            .EnterValuesLink(router.Link EnterValues)
            .ViewComponentsLink(router.Link ViewComponents)

    match model.page with
    | Main ->
        main.Body("").Elt()
    | Counter ->
        main.Body(CounterTemplate().Elt()).Elt()
    | EnterValues ->
        main.Body(EnterValuesTemplate().Elt()).Elt()
    | ViewComponents ->
        main.Body(ViewComponentsTemplate().Elt()).Elt()

Pomocnicza funkcja main zwraca reprezentację strony głównej. W miejsce Body wstawiamy zawartość, w zależności od tego, jaki link został kliknięty lub jaki adres został podany w przeglądarce.

W przykładzie widzimy, że link do strony generowany jest przez member router.Link przyjmujący w parametrze wartość modelu Page. Definicja value router wygląda tak:

let router = Router.infer SetPage (fun m -> m.page)

Funkcja infer z modułu Router, należącego do Bolero, zwraca konfigurację Routing’u bazując na message, który ustawia stronę oraz na modelu reprezentującym stronę.

To samo value wykorzystujemy, aby poinstruować całą aplikację o zdefiniowanym Routing’u, wołając Program.withRouter router:

type MyApp() =
    inherit ProgramComponent<Model, Message>()

    override this.Program =
        Program.mkSimple (fun _ -> initModel) update view
        |> Program.withRouter router

W taki sposób można wykorzystać Bolero Routing do symulowania funkcjonalności przechodzenia pomiędzy stronami systemu webowego.

Mechanizm Routing’u zawiera właściwość Page Models, która pozwala zamodelować stan dla konkretnej strony. Zajmiemy się tym tematem w następnym artykule.

=

Komentarze

Anonymous

Great post. Thank you :)

2021-05-06 00:20 UTC


mikedevbo

Thank you very much :)

2021-05-06 03:54 UTC