| 

.NET C# Java Javascript Exception

g:checkbox - Frage

Dies ist das Archiv des ehemaligen Forums zum Thema Groovy, Grails, Griffon und Bean Scripting Framework, welches unter groovy-forum.de existierte. Die neue Adresse des Groovy-Forums ist: http://codekicker.de/fragen/themen/groovy.


g:checkbox - Frage

typo - 14.09.2010 12:41
Hallo zusammen!

Ich habe (noch) eine Frage zu g:checkboxes: In meiner App kann ich per g:checkbox ein Menü bestellen:

<g:each in="${meal.mealitems}">
                <g:checkBox name="menuitem_$it.id" 
                          onclick="${remoteFunction(
                          update: 'priceTotalOfMealId' + meal.id, 
                          action: 'mealitemSelected', id: it.id, 
                          params: ' 
                                    \'completed=\' + this.checked + 
                                    \'&mealId=\' + ' + meal.id + '  
                          ')}"/>
</g:each>

die dazugehörige Methode ist:

    def mealitemSelected = {

      def user = User.findByEmail("john@example.org")
      def item = Mealitem.get(params.id)
      def meal = Meal.findById(params.mealId)     
      def orders = Orders.findByUserAndMeal(user, meal)  
      
      if ( ! orders) {
        orders = new Orders(user : user, meal : meal, created: new Date()).save()      
      }
      
      // True = add new item
      if (params.completed == "true") {
        orders.addToMealitems(item)
      } else { 
        orders.removeFromMealitems(item)
      }
      
      render sumTotal(orders)
      
      if (orders.mealitems.size() == 0) {
        orders.delete()
      } 
  
    }

mit den Domain classes:

class Meal {
    static hasMany = [mealitems : Mealitem]
    Date date
}


class Mealitem {
    int id
    String text
    float price    
    static belongsTo = [meal : Meal]
    int sortId
}


class Orders {
    User user
    Meal meal
    Date created    
    static hasMany = [mealitems : Mealitem]
 }

Meine Frage: Wie bringe ich es hin, dass die Checkbox beim Laden angewählt ist, wenn eine Order auf dieses Mealitem und User gesetzt ist? Ich dachte mir, ich könnte eine Methode auf dem Controller schreiben, der ich User und Mealitem übergeben kann und die dann ein 1 oder 0 zurückliefert. Kann ich eine solche Methode irgendwie von der View aus aufrufen?

Ich danke euch für eure Hilfe!
Beste Grüsse
Silvan


Re: g:checkbox - Frage

typo - 14.09.2010 13:45
Hi Horst,

Zum render: Dachte ich zuerst auch, aber es läuft, der Eintrag wird gelöscht! Aber wahrscheinlich ist es eh schöner, wenn ich das vor das render packe.
Wenn ich dich recht verstanden habe, gebe ich eine Liste aller Mealitems Id's an meine View, das mache ich ja z.B. mit [mealitems : mealitems].

Bei der Checkbox überprüfe ich dann, ob das aktuelle Id-Element in der Liste steht? Finde ich super - kannst du mir noch rasch sagen, wie ich das am besten mache? In Java wäre es z.B. ja ein Map, wo ich via xx.find(key) das Element ziehen könnte, oder eine Liste, wo ich durchloopen müsste...

Und noch eine letzte Frage (entschuldige bitte, aber du hilfst mir extrem weiter!): Per sumTotal(order) zähle ich die Beträge zusammen. Das müsste ich bei OnLoad ja auch noch wissen - was wäre der milkyman-Weg?

Herzlichen Dank nochmals und beste Grüsse aus der Schweiz.


Re: g:checkbox - Frage

milkyman - 14.09.2010 14:25
Grüezi.

typo schrieb:
-------------------------------------------------------
> Hi Horst,
>
> Zum render: Dachte ich zuerst auch, aber es läuft,
> der Eintrag wird gelöscht! Aber wahrscheinlich ist
> es eh schöner, wenn ich das vor das render packe.

Schöner auf jeden Fall. Dann musst du beim sumTotal nur drauf achten, dass da null als Parameter kommen kann.
Im Übrigen ist mir nicht ganz klar, was sich hinter "orders" verbirgt - ein Auftrag oder eine Liste von Aufträgen. Weil der findBy kann doch theoretisch für User und Meal mehrere Aufträge finden?

> Wenn ich dich recht verstanden habe, gebe ich eine
> Liste aller Mealitems Id's an meine View,

Genau.

> Bei der Checkbox überprüfe ich dann, ob das
> aktuelle Id-Element in der Liste steht? Finde ich
> super - kannst du mir noch rasch sagen, wie ich
> das am besten mache? In Java wäre es z.B. ja ein
> Map, wo ich via xx.find(key) das Element ziehen
> könnte, oder eine Liste, wo ich durchloopen
> müsste...

Hui, du triffst meinen wunden Punkt. Mein Code ist wenig Groovy. Von daher kann ich immer nur auf pleac groovy verweisen.

Es könnte also in etwa so funktionieren:
(mealItems.find { it -> it == gesuchteId } != null)
=> true/false

> Und noch eine letzte Frage (entschuldige bitte,
> aber du hilfst mir extrem weiter!): Per
> sumTotal(order) zähle ich die Beträge zusammen.
> Das müsste ich bei OnLoad ja auch noch wissen -
> was wäre der milkyman-Weg?

milkyman-Weg - das hast du schön gesagt. *lach*
Es ist aber wieder die gleiche Antwort: einen zusätzlichen Parameter mit dem Gesamtbetrag an die View übergeben.

Bye,
Horst


Re: g:checkbox - Frage

typo - 14.09.2010 19:55
Ich hab' bald nur noch graue Haare, das will mir seit 2 Stunden nicht dämmern

      List alle = Orders.findAllByUser(user)   
      def test = Mealitem.findById(4)
      def sonstwas = alle.mealitems.find{test} != null

Im Controller ist sonstwas korrekterweise true.

Nun der Check im View:


<g:each in="${meal.mealitems}" var="currentMealitem">
   <g:if test="${allMealitems.mealitems.find{currentMealitem} != null}">
        <b>YUP!</b>
   </g:if>
</g:each>

Das Problem ist, es sind alle Positionen auf YUP - was nicht stimmt (nur ID 1 und 4)


Re: g:checkbox - Frage

beginner_ - 15.09.2010 08:52
Ev. hilft es, wenn du das problem ein wenig grossflächiger umschreibst? Was macht die Applikation, was ist das Ziel?

Mir leuchtet das Datenmodell noch nicht ein bzw, wieso hat ein order ein Meal und MealItems?

Die mealItems wären ja über meal schon definiert.

Wie wärs mit einer Methode in der Klasse MealItem?

pseudo code:

boolean isOrderedByUserAndMeal(User user){
   
	def orders = Orders.findByUserAndMeal(user, meal)  	
  
	if ( ! orders) {
		return false      
	}else{
		return true
	}
}

value der checkbox wäre dann "${mealItem.isOrderedByUser(user)}"

Woher der Wert von user kommt (loggedInUser?) ist mir aber nicht klar. Aber denke das Prinzip is verständlich.


Re: g:checkbox - Frage

milkyman - 15.09.2010 11:57
Hi.

Du hast mein Beispiel etwas zu sehr verkürzt.

def sonstwas = alle.mealitems.find{test} != null

Wenn du in den geschweiften Klammern nur test stehen hast, ist das immer true (sofern test != null) und du bekommst immer das erste Element zurück. Du willst aber ja das Element haben, das test entspricht.

Daher:
def sonstwas = alle.mealitems.find{it.id == test.id} != null

Jetzt ist sonstwas nur dann true, wenn sich in alle.mealitems ein Mealitem findet, dass die gleiche ID hat wie das Mealitem test. Ich denke das ist eher das was du möchtest.

Bye,
Horst


Re: g:checkbox - Frage

typo - 15.09.2010 19:20
Hi beginner_

Besten Dank für deinen Lösungsvorschlag. Ich denke, es gibt in meinen Überlegungen im Schema noch Ungereimtheiten.

Folgendes möchte ich abbilden:

Ein Meal hat ein Datum, wann es serviert wird und besteht aus n Mealitems. Bei einer Bestellung soll man gewünschte Mealitems selektieren können (man muss aber nicht alle Mealitems nehmen).

Mit deinem Vorschlag
def orders = Orders.findByUserAndMeal(user, meal) 
erhalte ich damit leider nicht das Menuitem - hab schon alles versucht à la Orders.findMealitemByUserAndMeal (aber so human ist Grails dann auch nicht :.)

Ich freue mich über euer Feedback.
Beste Grüsse
Silvan


Re: g:checkbox - Frage

beginner_ - 16.09.2010 08:52
typo schrieb:
-------------------------------------------------------
> Hi beginner_
>
> Besten Dank für deinen Lösungsvorschlag. Ich
> denke, es gibt in meinen Überlegungen im Schema
> noch Ungereimtheiten.
>
> Folgendes möchte ich abbilden:
>
> Ein Meal hat ein Datum, wann es serviert wird und
> besteht aus n Mealitems. Bei einer Bestellung soll
> man gewünschte Mealitems selektieren können (man
> muss aber nicht alle Mealitems nehmen).
>

Also ist eine Bestellung eigentlich nichts anderes als das erstellen eines neuen "Meals"?
Oder steckt da noch mehr dahinter? Die Klasse Order ist ev. überflüssig aber schwer zu sagen ohne genaue kentnisse, was du erreichen willst,
Überlege es dir einfach nochmals.

> Mit deinem Vorschlag def orders =
> Orders.findByUserAndMeal(user, meal) erhalte ich
> damit leider nicht das Menuitem - hab schon alles
> versucht à la Orders.findMealitemByUserAndMeal
> (aber so human ist Grails dann auch nicht :.)
>

Das Ziel ist auch nicht das menuitem zu kreieren. Hier deine Frage aus dem ersten Post:

< Meine Frage: Wie bringe ich es hin, dass die Checkbox beim Laden angewählt ist, wenn eine Order auf dieses Mealitem und User gesetzt ist?

Meine Antwort: Du musst "value" von checkbox auf true (Order vorhanden) oder false (kein Order vorhanden) setzen.

Language: HTML
<g:each in="${meal.mealitems}"> <g:checkBox name="menuitem_$it.id" value="${it.isOrderedByUser(user)}" /> </g:each>

Du musst jetzt noch die Variable "user" auf den entsprechenden User setzen.
Allerdings habe ich das nicht selbst ausprobiert, ob es auch funktioniert und was passiert, wenn du die Checkbox an bzw abwählst.

Das ganze sollte einfach ein Denkanstoss sein -> logik ins Model (oder service) und nicht in die Seite oder den Controller.

Aufjedenfall scheint das Datenmodel nicht zu stimmen, den wenn man so komische workarounds basteln muss, ist das oft ein Zeichen für ein Problem im Model. Das problem sehe ich in Orders. MealItem hat in Orders nichts verloren. MealItem gehört zu Meal. Und ein bestimmtes Meal ist logischerweise immer aus den gleichen MealItems aufgebaut. In deinem Model kann es sein das ein Order mealItem 1 und 2 beinhaltet und das meal im order item 3 und 4. Das macht ja absolut keinen Sinn.

Die korrekte Frage ist also immer "Ist ein Meal von User x bestellt oder nicht?".

Wenn ein User das menu ändern will, muss er entweder aus einer Liste an vorhandenen menüs auswählen oder ein neues erstellen mti den gewünschten Items.

Model wäre dann IMHO so "korrekt" mit meinem Wissen bezüglich der Anforderungen:

Language: Groovy
class Mealitem &#123; &nbsp; static belongsTo = &#91;meal : Meal&#93; String text float price int sortId &#125; &nbsp; class Meal &#123; &nbsp; static hasMany = &#91;mealitems : Mealitem, orders : Order&#93; Date date &#125; &nbsp; &nbsp; class Orders &#123; &nbsp; static belongsTo = &#91;User, Meal&#93; User user Meal meal Date created &#125; &nbsp; class User &#123; &nbsp; static hasMany = &#91;orders : Order&#93; String username &#125;


Re: g:checkbox - Frage

typo - 16.09.2010 09:03
Vielen Dank für deinen Input! Ich sehe deinen Punkt, gerne möchte ich das Projekt kurz beschreiben:

Es gibt jeden Tag Angebote, so z.B. Sandwich, Suppe, Salat und Dessert. Jedes dieser Menüs bzw. Konstellationen gibt es nur an einem bestimmten Datum (deshalb kam ich auf die komische Idee mit dem Meal - das einzig und alleine nur das Attribut datum hat).

Nun ist es so, dass ich z.B. nur eine Suppe oder ein Dessert und Salat - also variabel auswählen kann.

Ich denke mittlerweile, dass es Meal gar nicht mehr braucht - sondern die Order direkt mehrere Mealitems besitzt - was einer Bestellposition gleich kommt (da die Menge immer 1 ist, brauche ich keine Verbindungstabelle mit eigenen Attributen).

Der Vorteil von Meal wäre einfach gewesen, dass ich hätte sagen können: def meals = Meal.findAll() und dann bequem im View durchloopen könnte und mit einem Subloop die Items hätte anzeigen lassen können.

Nehmen wir dann mal an, das sieht so aus:

ORDERS hat n MEALITEMS, datum
MEALITEMS hat datum, preis, beschreibung

Wenn ich dann durchloope und sage def meals = Mealitems.findAll() erhalte ich ja eine Auflistung, die so aussieht:

Mealitem A -> Datum 16.9.2010
Mealitem B -> Datum 16.9.2010
Mealitem C -> Datum 22.9.2010

Wie könnte ich die Darstellung dann ändern und quasi gruppieren?:

16.9.2010
-------------
Mealitem A
Mealitem B

22.9.2010
--------------
Mealitem C


Ich hoffe, ich habe es einigermassen klar ausgedrückt - freue mich auf euer Feedback!


Re: g:checkbox - Frage

koeberle - 16.09.2010 09:44
Ich finde das Konzept von dem du gestartet bist eigentlich in Ordnung, vielleicht ist die Nomenklatur nicht so gekonnt. Aber wenn man das im Ganzen betrachtet musst du ja für jeden Tag ein Angebot von Speisen haben. Jeder Kunde kann sich eigentlich beliebig viele von den Speisen bestellen (Nachschlag, oder vielleicht kommt ja mal die Freundin oder der Freund mit essen und hat kein Geld dabei). Somit muss das Tagesangebot alle Speisen kennen Speise <-> Tagesangebot 1:n (ich nehme mal an das der Koch nicht unendlich viele Rezepte hat) Damit kannst du schon mal den Speiseplan für jeden Tag anzeigen. Der Kunde kann dann bestellen (damit gibt es die Bestellung). Diese kann wiederum beliebig viele Speise enthalten Bestellung <-> Speise n:m (eine Speise kann in unterschiedlichen Bestellungen auftauchen).
Eventuell solltest du das Attribut meal aus der Bestellung durch das Datum ersetzen, einen Informationsverlust wir dadurch nicht entstehen. Du kannst ja später über das Datum die Bestellungen und Tagesangebote wieder verknüpfen (man weiß nie was einem später an so einem Datenwust alles mal interessiert).

Tagesangebot <1:n> Speise <n:m>Bestellung

Da bin ich doch mal gespannt was es bei mir heute zum Mittag gibt,

Christian


Re: g:checkbox - Frage

typo - 16.09.2010 10:14
Hi Christian

Wenn die App laufen würde, könntest du dir was aussuchen :-)
Vielen Dank für deine Ausführungen, genau so mache ich es.

Wenn du mir nun noch sagen könntest, wie ich die Liste sortieren kann (s. mein vorheriger Post), kann ich mich wieder voller Elan ins Projekt stürzen!

Beste Grüsse aus der Schweiz
Silvan


Re: g:checkbox - Frage

koeberle - 16.09.2010 10:50
Also wen ich das richtig verstehe möchtest du eine Liste generieren die ausgibt was es wann zu essen gab / gibt. Was ist den der Zweck dieser Anzeige? Soll da nur eine Liste mit Datum und Speisen des Tages stehen. Soll vielleicht auch noch die Preise in der Liste auftauchen, soll man vielleicht gleich auswählen (bestellen) können. Oder willst du anzeigen was an welchen Tagen bestellt wurde?

In jedem Fall kannst du so starten:
Controller:
def tagesAngebote = Meal.findAll(params)
//params ist immer gut denn nach zwei Wochen hat dein Liste schon 10 Element (Mo-Fr)
//das wird dann ganz schnell sehr lang
//paginatem kann dir dann helfen 
return [mealList: tagesAngebote]
im View :
<ul>
<g:each in="${mealList}" var="meal">
<li><g:formatDate date="${meal?.date}" format="dd.MM.yyyy"/></li>
    <g:each in="${meal.mealitems}" var="mealitem">
        <li>${fieldValue(bean: mealitem, field: "text")}</li>
   </g:each>
</g:each>
<ul>

Wenn du mehr als nur den text von Mealitem anzeigen willst, solltest du auf eine Tabelle umsteigen.
Wenn du auch noch auf die Bestellungen zugreifen willst, solltest du eine bidirektionale Beziehung zwischen Mealitem und Oder haben. Sprich Mealitem sollte noch hasMany = [orderList:Order] haben. Dann musst du zwar immer noch über Meal filtern, ist aber eleganter als ein separates Select abzusetzen.
//Bestellungen zählen
//Methode von Mealitem
def countOrders(meal){
    orderList.inject(0){count, order -> if(order.meal == meal) count++}
}

Bevor ich aber noch weiter rate was du machen willst, schicke ich das mal ab.

Christian


Re: g:checkbox - Frage

milkyman - 16.09.2010 10:54
Wenn du erstmal die Liste der mealItems hast, kannst du die per sort() (siehe wieder Sorting a List by Computabl Field) sortieren.

Beispiel:
meatItems.sort { a,b -> a.datum <=> b.datum }

Dann sind die zumindest schonmal nach Datum sortiert und du kannst per each drüber iterieren.

Für die Ausgabe mit Datum als Überschrift musst du dann m.E. Logik innerhalb von each haben, so dass immer wenn sich das Datum ändert, die Überschrift ausgegeben wird.

Bye,
Horst


Re: g:checkbox - Frage

typo - 16.09.2010 11:29
Hi Christian

So mache ich es bei den Meals - das funktioniert bestens. Was mache ich aber, wenn ich die Orders einer Person habe. Die Orders-Klasse hat ja ein Datum drauf - und ich im UI dann nach Datum gruppieren möchte? (Also einfach alle Orders von mir anzeigen möchte)

Ich sollte ja dafür nicht das Datum in eine Domain-Klasse auslagern, oder :-) (Wenn ich es so machen würde, könnte ich ja wieder gleich wie von dir beschrieben über alle Datumsobjekte iterieren)

Gibt es eventuell noch einen schöneren Weg, als sich im Loop das Datum zu merken und wenn es anders ist als der Wert des Vorgängers es anzuzeigen? In Java würde man wahrscheinlich eine Map<Datum, List<Orders>> erstellen - gibt's da was ähnliches?

Besten Dank!
Grüsse
Silvan


Stelle deine Groovy-Frage jetzt!


Diese Seite zeigt den Thread "g:checkbox - Frage" der ehemaligen Webseite groovy-forum.de, welche durch einen Serverunfall zerstört wurde. codekicker.de hat viele Konversationen über die beliebte Programmiersprache Groovy und zugehörige Frameworks wie das Grails-Framework retten können.

Hast Du eine Frage zum Thema Groovy, Grails oder allgemein Java? Viele ehemalige groovy-forum.de Mitglieder beantworten dir auf codekicker.de deine Frage! Stelle jetzt eine Frage!

Viele weitere Diskussionen zu Grails und Groovy befinden sich auf der Threadübersicht des alten groovy-forum.de.