MVC push vs pull - które podejście gdzie się stosuje?

0

Mamy do wyboru dwie opcje:

  1. View pobiera sobie dane z Controllera wywołując jakąś metode
  2. Controler wciska dane Viewowi poprzez argumenty

Zastanawiam się jako że zaczynam dopiero zabawę, które podejście gdzie się stosuje.
Jak np. chcę wyświetlić najnowsze wpisy, to sensownym wydaje się, żeby View sam wywołał metodę i operował na tych danych.
Natomiast co jeśli np chcę wyświetlić wyniki wyszukiwania? Czy Controller powinien przekazać np frazę do View a ten wywołuje metodę / metody na Controllerze i bierze stąd dane, czy może Controller powinien wcisnąć już gotowe dane które View tylko odczyta?
Czy jest sens w ogóle pozwalać Viewowi wywolywać metody kontrollera czy wszystkie dane powinny zostać mu dostarczone i on tylko na tych danych operuje?

0

Widok to szablon do wyświetlania danych. Kontroler decyduje o tym, jakie dane przekazać któremu widokowi do wyświetlenia.
Jak sobie wyobrażasz wywoływanie metod przez widok? Jedynie skrypt JS mógłby wywołać jakąś akcję kontrolera, ale w tym przypadku nadal aktywny będzie kontroler.

0

Nie nie.
W Playu, w którym robię, wygląda to tak. Jest sobie model, zaczynający przykładową deklaracją:
@(homeContent: List[String])(message: String)
No i mogę wywołując renderowanie widoku wepchnąć za pomocą argumentów dane.
Ale mogę też sobię wywołać w tym samym pliku widoku
@controllers.MessageController.jakiesdane
i uzyskam dane, bez tworzenia żadnych Routes, bo dalej to jest jeszcze server side.

1
case class User(firstName: String, lastName: String)

def someAction = Action {
  Ok(views.html.someView(User("Stefan", "Cos Tam"))
}

@(user: User)

Slowami:
Kontroler ma pobrac, przetworzy i przekazac dane do widoku. Tylko te dane, ktore sa potrzebne do wyswietlenia czegos tam.
Widok ma tylko wiedziec jak wyswietlic te dane. Nie ma nic wiedziec o tym, ze kontroler w ogole istnieje.
Same dane polecam przekazywac w formie viewmodeli, ktore sa case classami, case bo prosciej sie tworzy, wyglada, binduje z formularzami, etc. i ogolnie sa osom.

0

Jak pytałem o tę architekturę push/pull to powodem była chęć zmniejszenia ilości danych przesyłanych w parametrach. Znalazłem sobie taki post: http://stackoverflow.com/a/9632085
@n0name_l , najczęściej używa się chyba implicitsów?

I przy okazji pytanie troche poza tematem, czy jeśli chce zrobic wyszukiwanie, to czy zrobić deklarację w routes:
GET /search kontroler.search(imie: Option[String], nazwisko: Option[String], wiek: Option[String], zawod: Option[String])
czy może po prostu
GET /search kontroler.search
a potem przeczesać request.queryString w metodzie search? Bo REST-owa kompozycja chyba średnio pasuje
/:imie/:nazwisko
czy
/i=:imie/n=:nazwisko
Przynajmniej mnie jakoś gryzie w oczy

2

No okej, a nie mozesz po prostu zrobic tak, zeby framework sobie wyciagnal te parametry z query stringa za ciebie?

Bo generalnie zadne z tego co podales mi sie nie podoba, ale nie chodzi o sam fakt przekazywania/nie przekazywania parametrow.

Ja bym zrobil tak:

  def search(name: Option[String], lastName: Option[String], age: Option[String]) = Action {
    Ok(":D")
  }

GET /app/search controllers.Application.search(name: Option[String], lastName: Option[String], age: Option[String])

i uzycie:

6de9ec2da2.png

Albo jeszcze lepiej, uzyl typow:

  def search(name: Option[String], lastName: Option[String], age: Option[Int]) = Action {
    Ok(":D")
  }

GET /app/search controllers.Application.search(name: Option[String], lastName: Option[String], age: Option[Int])

da16b4a5e8.png

Generalnie nie widze powodu sie martwic iloscia parametrow, dopoki nie przekracza ona 5. Ale wtedy bym sie raczej zaczal martwic, czy akcja, ktora wywoluje nie stara sie po prostu robic za duzo, niz sztucznie zmniejszal sama ilosc argumentow jakimis przykrywkami.

najczęściej używa się chyba implicitsów?

Nie mam pojecia, czego sie uzywa. :P

1

W sumie to zaciekawilo mnie to, wiec chyba cos mam.

Najpierw polecimy z pokazem, jak wyglada akcja po 'modyfikacjach':

  def searchImproved(tokens: Tokens) = Action {
    Ok(tokens.name + " " + tokens.lastName + " " + tokens.age.toString)
  }

Klasa Tokens:

case class Tokens(name: String, lastName: String, age: Int)

conf/routes:

GET     /app/searchImproved         controllers.Application.searchImproved(tokens: models.Tokens)

Dodatkowo trzeba dodac w build.sbt:

routesImport += "Binders._"

Uzycie takie samo jak poprzednio:
b9b40a2516.png

I teraz cala magia, dzieki ktorej to dziala:

object Binders {
  implicit def queryStringBinder(implicit stringBinder: QueryStringBindable[String],
                                          intBinder: QueryStringBindable[Int]) =
    new QueryStringBindable[Tokens] {
      override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Tokens]] = {
        for {
          name <- stringBinder.bind("name", params)
          lastName <- stringBinder.bind("lastName", params)
          age <- intBinder.bind("age", params)
        } yield {
          (name, lastName, age) match {
            case (Right(name), Right(lastName), Right(age)) => Right(Tokens(name, lastName, age))
            case _ => Left("Unable to bind Tokens.")
          }
        }
      }

      override def unbind(key: String, tokens: Tokens): String = {
        stringBinder.unbind("name", tokens.name) + "&" +
        stringBinder.unbind("lastName", tokens.lastName) + "&" +
        intBinder.unbind("age", tokens.age)
      }
    }
}

Na razie nie obsluguje opcjonalnych parametrow, ale spokojnie mozna dodac.

Uwaga uzywanie tego dla kazdej bzdury to ostry overkill

PS. Sa tu jakies znaczniki dla Scali, czy jakich mam uzywac :(
PS2. Uzywanie Scali z Play na windowsie to jakis koszmar jest, albo ja nie umiem tego jakos ustawic, zeby dzialalo.

1 użytkowników online, w tym zalogowanych: 0, gości: 1