2012-03-30 17 views
13

Mam mapę reprezentującą spis treści, zawiera ona klucze Chapter i wartości List[Section]. Teraz próbuję pętli w moim szablonie tak:Graj! Framework 2.0 - Pętla przez mapę w szablonie scala?

<dl> 
@table_of_contents.foreach((e) => { 
    <dt> 
     @e._1.title 
    </dt> 
     for(section <- e._2){ 
     <dd> 
      @section.title 
     </dd> 
     } 
}) 
</dl> 

Obecnie dostać żadnego wyjścia w <dl> jednak.

dodałem println(table_of_contents) oświadczenie górze szablonu w celu zapewnienia, że ​​mapa rzeczywiście mają dane i wydrukować:

{[email protected]=BeanList size[4] hasMoreRows[false] list[[email protected], [email protected], [email protected], [email protected]], [email protected]=BeanList size[0] hasMoreRows[false] list[]}

może muszę użyć imperatyw styl?

UPDATE:

Wciąż nad tym pracujemy ... dostał tę odmianę do kompilacji, ale nie ma wyjścia.

<dl> 
@table_of_contents.foreach{case(a, b) => { 
    <dt> 
     @a.title 
    </dt> 
     @displaySections(b) 
}} 
</dl> 

... 

@displaySections(sections: List[Section]) = { 
    @for(a_section <- sections) { 
     <dd>@a_section.title</li> 
    } 
} 
+3

Nie mam dużego doświadczenia z Play, ale czy to dlatego, że typem zwrotu foreach jest 'Unit'? Czy próbowałeś użyć 'map' zamiast? – Gareth

+0

@Gareth Myślę, że gdzie masz rację, zmieniłem pętlę, aby użyć składni 'for()' zamiast wywoływania metody 'foreach()' i zadziałało – wbarksdale

Odpowiedz

17

tl; dr

Odpowiedzi udzielone do tej pory (o @wbarksdale, @PlexQ i @Daniel C. Sobral w komentarzu) są wystarczająco dobre, aby kierować problem opisany tutaj.

Ale brakuje im prawdziwego wyjaśnienia, dlaczego początkowy kod, używając foreach, nie działa.

To nie działa, ponieważ foreach zwraca Unit.

zabaw koncepcje

Podam krótki nuty/przypomnieć o tym, jak działają szablony.

System szablonów Scala dostarczany domyślnie w Play Framework 2 jest rzeczywiście oparty na koncepcjach FP i dlatego używa wielu niezmiennych struktur i tak dalej.

Co więcej, taki szablon Scala (powiedzmy myTemplate.scala.html) zostanie wkompilowany w zwykłą Scalę object, która nazywa się metoda apply. Ta ostatnia funkcja umożliwia nam wywołanie obiektu jako funkcji z niektórymi parametrami (zadeklarowanymi w pierwszym wierszu szablonu).

Ten object opiera się również na konstrukcji podobnej do BaseScalaTemplate, która jest zbudowana za pomocą formatera wyjściowego (HTML). Ten formatator będzie mógł pobierać elementy (takie jak String, Unit, Seq[Int],, ...) i renderować je do kodu HTML.

formatowanie nastąpi podczas stosując metodę _display_ z BaseScalaTemplate, która zwraca wystąpienie sformatowane wyjściu. Ta metoda wyświetlania zostanie wywołana w kodzie skompilowanym pliku .scala.html w treści obiektu obiektu apply.

więc organizm może skończyć tak:

def apply/*1.2*/(testMap:scala.collection.immutable.Map[String, Int]):play.api.templates.Html = 
    _display_ { 
    Seq[Any](
     _display_(
     Seq[Any](
      /*3.2*/testMap/*3.9*/.map/*3.13*/ { e => 
      _display_(Seq[Any](_display_(Seq[Any](/*5.3*/e)))) 
      } 
     ) 
    ) 
    ) 
    } 

See? Wywołania _display_ niczego nie mutują, ale są skomponowane w taki sposób, że samo zastosowanie zwróci instancję sformatowanego kodu (Html)!

To daje nam wskazówkę ...

tak bla bla ... teraz, dlaczego?

Po tych błyskawicach, które zostały podane o urządzeniach Play, możemy teraz zmierzyć się z prawdziwym pytaniem: dlaczego, do cholery, jest idealny kod Scala podany w pytaniu, nie działa ... czytaj, nic nie wyświetla na wszystko.

Jest to dość proste, przy użyciu foreach na Map, jesteś rzeczywiście zapętlenie nad elementami i dostosowania je do HTML. Jednak te obliczenia nie będą możliwe do zastosowania przez system szablonów, ponieważ są one zawarte w pętli foreach. To jest foreach musi być użyte, gdy efekty uboczne są wymagane dla każdego elementu w sekwencji ... I wróć Unit kiedy to się stanie.

Ponieważ system szablonów spróbuje _display_ wynikiem foreach na danym Map będzie to po prostu renderowania/format Unit a tym samym pustym String!

Podsumowując, wystarczy użyć map która będzie zwracać nową sekwencję utrzymującą dostosowany przedmioty, instancja Html.

Hmmm a co z numerem for?

Tak, masz rację ... Na podstawie tego, co zostało powiedziane, dlaczego są odpowiedzi, że proponowane użył pracy for pętli, ponieważ bez uzyskując wartość, o for jest równoważna foreach! (Wprowadzenie yield zakończy się map -Jak zachowań)

Odpowiedź jest w kodzie ... Kompilator Szablon będzie poprzedzić yield słowa kluczowego do ciała for „s - to sprawdzić here.:-D

Et voilà, że działa zbyt, ponieważ wygenerowane spożywczych w organizmie for „s zostanie dołączony do zwracanego sekwencji po jej zakończeniu.

+0

Link w tej odpowiedzi to 404 i nadal jestem tak zagubiony, że używam frameworku For, Foreach lub Map in Play, to przerażenie dla wra p pętli w szablonach, aby wypluć potrzebną strukturę. –

7

Gra w Scali bardzo dobrze wykorzystuje funkcjonalną naturę Scali. Zmień to na mapę, która zwraca elementy, i powinno działać.

<dl> 
@table_of_contents.map(case(k,v) => { 
    <dt> 
     @k.title 
    </dt> 
    @v.map { section => 
     <dd> 
      @section.title 
     </dd> 
    } 
}) 
</dl> 

Zgodnie z sugestią powyżej, w przypadku, zamienia ją w częściową funkcję, która robi to, co chcemy ładnie!

+0

Dzięki @PlexQ, pojawia się błąd 'oczekiwany początek definicji 'w linii '@e._2.map {section =>'. Naprawdę nie wpadłem na Scalę zbyt wiele, ale zacząłem się uczyć od momentu opublikowania tego, i jestem zdumiony tym, jak bardzo jest ekspresyjny. naprawdę fajne rzeczy! – wbarksdale

+0

pozwól mi sprawdzić, czy mogę to naprawić! – PlexQ

+0

w porządku - zaktualizowano, spróbuj tego – PlexQ

17

Rozwiązanie, które wymyśliłem, wyglądało tak. Zasadniczo unika się używania programowania funkcjonalnego, na którym na razie jestem w porządku, ale nadal chciałbym zobaczyć działające rozwiązanie wykorzystujące funkcjonalny styl scala.

<dl> 
@for((key, value) <- table_of_contents) { 
    <dt> 
     @key.getTitle 
    </dt> 
     @displaySections(value) 
} 
</dl> 

@displaySections(sections: List[Section]) = { 
    @for(a_section <- sections) { 
     <dd>@a_section.getTitle</li> 
    } 
} 
+4

Wypróbuj '@table_of_contents.map {case (key, value) => '. –

+1

Nie przypominam sobie, żeby widziałem jak się tak rozbrajałem, z częściową funkcją, to jest fajne. Ciągle jestem zdumiony tym, co potrafi Scala do. – PlexQ

Powiązane problemy