F# Bolero - A cóż to takiego? - routing
Posty z tej serii:
- F# Bolero - A cóż to takiego? - wprowadzenie
- F# Bolero - A cóż to takiego? - HTML templates
- F# Bolero - A cóż to takiego? - view components
- F# Bolero - A cóż to takiego? - routing
- F# Bolero - A cóż to takiego? - routing - page models
- F# Bolero - A cóż to takiego? - remoting
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:
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:
W pierwszym kroku tworzymy plik main.html
z poniższym kodem i umieszczamy w katalogu wwwroot:
<div>Choose:</div>
<a href="${MainLink}">Main</a>
<a href="${CounterLink}">Counter</a>
<a href="${EnterValuesLink}">EnterValues</a>
<a href="${ViewComponentsLink}">ViewComponents</a>
<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.
=
Anonymous
Great post. Thank you :)
2021-05-06 00:20 UTC
Thank you very much :)
2021-05-06 03:54 UTC