Grails itself provides a feature called scaffolding that allows to build extremely easily a simple working application skeleton from constraints defined on the domain classes.
Doing the same with Flex is a bit more complex because there are two runtimes involved: the Flash client and the Java server. There are thus three main possible options:
After this short introduction, let's see how this works with the latest release of gdsflex (0.7.1).
First we create a new Grails application, once again a book manager (maybe next time I'll find some more original example). Assuming you have grails 1.1.1 installed, type from the command line :
grails create-app gdsexample2
cd gdsexample2
grails install-plugin gdsflex
As before, the main part is to define the domain model. Let's start this time by a simple Author class.
grails create-domain-class com.myapp.Author
Author.groovy
package com.myapp
class Author {
static constraints = {
name(size:0..50, blank:false)
birthDate(format:'DD/MM/YYYY')
picture(nullable:true, widget:'image')
}
String uid
String name
Date birthDate
java.sql.Blob picture
}
This is a relatively simple domain class, you can notice the constraints that drive Grails scaffolding. So let's add a simple controller :
grails create-controller com.myapp.Author
AuthorController.groovy
package com.myapp
@org.granite.tide.annotations.TideEnabled
class AuthorController {
def index = { redirect(action:list, params:params) }
def scaffold = Author
}
Then run :
grails generate-flex-app
grails mxmlc
grails run-app
And go to http://localhost:8080/gdsexample2/gdsexample2.swf.
You should have a basic working Flex application that allows to create and edit authors. So what happened here :
This is a simple example, but we can see how the constraints have been used : the Grails constraints in Author.groovy have been translated to the following block in Author.as :
public static const meta_constraints:Array = [
{ property: "name",
blank: "false", size: "0..50"
},
{ property: "birthDate",
format: "DD/MM/YYYY"
},
{ property: "picture",
widget: "image"
}
]
Even if you don't want to use the GraniteDS UI builder framework, you could still use these as3 constraints from any custom framework.
You can check that the blank and size constraints have been mapped to Flex client-side validators, and that the date format has been used in the author table. The 'image' widget is simply a component that appears in edit mode and allows to upload an image from Flex.
Now that we have seen a basic domain class, let's see how it works with associations.
grails create-domain-class com.myapp.Book
grails create-domain-class com.myapp.Chapter
grails create-controller com.myapp.Book
Book.groovy
package com.myapp
class Book {
static constraints = {
title(size:0..100, blank:false)
category(inList:["Fiction", "Non-fiction", "Biography"])
author()
description(size:0..2000, widget:"textArea")
chapters()
}
String uid
String title
String category
Author author
Setchapters
String description
static hasMany = [ chapters:Chapter ]
static mapping = {
author fetch:"join"
chapters cascade:"all-delete-orphan"
}
}
Chapter.groovy
package com.myapp
class Chapter {
static constraints = {
title(size:0..50, blank:false)
}
String uid
Book book
String title
String summary
static mapping = {
book fetch:"join"
}
}
BookController.groovy
package com.myapp
@org.granite.tide.annotations.TideEnabled
class BookController {
def index = { redirect(action:list, params:params) }
def scaffold = Book
}
An important thing is that ManyToOne associations must be marked fetch:"join" or lazy:false. GraniteDS does not support transparent lazy loading for single ended associations, and most likely never will (it would generate unacceptable network and database traffic, one http request and one SQL query for each uninitialized entity). Lazy collections are supported, so OneToMany and ManyToMany associations do not have to be marked lazy:false.
Let's regenerate the Flex application to add the Book menu to the main mxml.
grails generate-flex-app
grails mxmlc
You can notice that the main mxml has been overwritten. This is quite annoying if you made some changes to the generated file but the previous file is backuped and in general you would have added the entity manually in the mxml. If you have a look to the mxml, only two lines have been added for the new entity :
<mx:LinkButton label="Books" width="100%" textAlign="left"
click="mainStack.selectedChild = bookUI" />
<ui:EntityUI id="bookUI"
entityClass="{Book}"
width="100%" height="100%"/>
The EntityUI is a Flex component and can just be used anywhere in a Flex application (provided the Tide framework initialization has been done correctly).
We can now create and edit books, and see how many-to-one and one-to-many associations are displayed in the generated application.
As you eventually tried to add chapters, you will see that it does not work. There are indeed two minor changes to do manually in the AS3 classes to help the UI builder make the link between the associated oneToMany entities: initialize the Chapters collection in the Book.as class, and add a constructor in Chapter.as :
public function Book():void {
chapters = new ArrayCollection();
}
public function Chapter(book:Book = null):void {
this.book = book;
}
Once these changes are made, just refresh the browser and check that you can add chapters to your books.
Now we're almost finished, we'll just add a final improvement to this simple app:
grails html-wrapper
As the command name indicates, this will generate a html wrapper and allow the application to use Flex deep linking, and to use Grails url mapping on the flex application now located at flexindex.gsp.
Now you can go to http://localhost:8080/gdsexample2/flexindex.gsp#book/list to access the book list directly.
The documentation on the plugin page gives more information on what constraints are supported by the builder and how to override the behaviour of the generated application. Anyway the sources of the UI builder are also present in the grails-app/views/flex folder and can be used as a template to build your customized UI.
It should be easy to add security and data push to the generated application by following the previous blog entry here.
Hopefully this new feature of the gdsflex Grails plugin is a useful tool to get started quickly with Flex and GraniteDS and to quickly build prototype applications.
As always, feedback and comments are welcome.
20 commentaires:
Well it looks good but when I try to add a chapter a popup shows with no message. Is there a way to enable debug??
Thanks
You have to install a debug version of the flash player. Did you add the two constructors in Chapter.as and Book.as ?
I'm trying to override the UIBuilder for one of my domain entities, namely org.epseelon.confplan.Session. So I created a SessionUIBuilder class in the same package as DefaultUIBuilder, and I annotated it like so: [Name("org.epseelon.confplan.Session.tideUIBuilder")]. Yet, when I execute my application, DefaultUIBuilder seems to be used instead. What did I miss?
It should be [Name("org.epseelon.confplan.session.tideUIBuilder")] (lowercase on session).
Hi,
I was trying as mentioned in this article and I am getting the below error while doing "grails generate-flex-app"
Could you help me resolve this issue? Please email me: beeyarkay at yahoo dot com
Environment set to development
[mkdir] Created dir: E:\grails_projects\gflex\web-app\plugins\gdsflex-0.7.1
[copy] Copying 28 files to E:\grails_projects\gflex\web-app\plugins\gdsflex-0.7.1
[groovyc] Compiling 20 source files to C:\Documents and Settings\ramesh\.grails\1.1.1\projects\gflex\classes
[groovyc] org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, Compile error during compilation with javac.
[groovyc] C:\Documents and Settings\ramesh\.grails\1.1.1\projects\gflex\plugins\gdsflex-0.7.1\src\java\org\granite\grails\integration\GrailsBinaryConverter.java:29: method does not override a method from its superclass
[groovyc] @Override
[groovyc] ^
[groovyc] C:\Documents and Settings\ramesh\.grails\1.1.1\projects\gflex\plugins\gdsflex-0.7.1\src\java\org\granite\grails\integration\GrailsBinaryConverter.java:35: method does not override a method from its superclass
[groovyc] @Override
[groovyc] ^
[groovyc] 2 errors
[groovyc] 1 error
Seems you are using a JDK 5, could you try with a JDK 6. The plugin does not seem to compile on a JDK 5, I'll try to fix this in a next update.
Thank you. That fixed it. In the meantime, I commented out the @Override annotations to make it work in java1.5. That was all I did.
-Ramesh
Is possible the integration of webflow for the example of grails?
Thanks
When I execute the command "grails mxmlc", it throws me the following error:
Environment set to development
Error executing script Mxmlc: tried to access class org.granite.web.util.WebComp
ilerWrapper$_compile_closure1 from class org.granite.web.util.WebCompilerWrapper
java.lang.IllegalAccessError: tried to access class org.granite.web.util.WebComp
ilerWrapper$_compile_closure1 from class org.granite.web.util.WebCompilerWrapper
at org.granite.web.util.WebCompilerWrapper.compile(WebCompilerWrapper.gr
oovy:26)
at org.granite.web.util.WebCompilerWrapper$compile.call(Unknown Source)
at Mxmlc$_run_closure1.doCall(Mxmlc:25)
at Mxmlc$_run_closure1.doCall(Mxmlc:23)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)
You should run
> grails html-wrapper
before running
> grails mxmlc
I hope it helps.
When i try to upload image file in Author panel i get:
Error #2044: Nieobsługiwane IOErrorEvent:. text=Error #2038: Błąd dostępu do pliku.
at org.granite.tide.uibuilder.editors::ImageEditor/uploadImage()[/Users/kkaczmarek/development/grails/GDSExample/grails-app/views/flex/org/granite/tide/uibuilder/editors/ImageEditor.mxml:38]
at org.granite.tide.uibuilder.editors::ImageEditor/__upload_click()[/Users/kkaczmarek/development/grails/GDSExample/grails-app/views/flex/org/granite/tide/uibuilder/editors/ImageEditor.mxml:93]
ok, this is generic bug in osx flash player (!)
Following the example, when delete an Author pops up a confimation dialog. If you click yes, the Author's List gets empty but the edit button keeps enable and if you click it, it's like you haven't delete the Author at all. The information keeps there to be deleted again. How to fix this? I try doing myself other examples and all of them have this same issue.
It is possible to deploy it as war file each time i run grails > war i get : Error executing script War: : org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, D:\Documents\.grails\1.2.1\projects\aiolos\gspcompile\gsp_aiolos_webapp_WEB_INF_classes_org_granite_grails_templatetideDomainClassBase_gsp.groovy: 18: Unknown type: IMPORT at line: 18 column: 1. File: D:\Documents\.grails\1.2.1\projects\aiolos\gspcompile\gsp_aiolos_webapp_WEB_INF_classes_org_granite_grails_templatetideDomainClassBase_gsp.groovy @ line 18, column 1.
import org.granite.generator.as3.reflect.JavaProperty;
^
plus more rubbish :)
thx
Hello everybody,
Following http://www.grails.org/plugin/gdsflex I've created a sample.
Here is the error outputed after I have executed "grails mxmlc":
Welcome to Grails 1.1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: C:\grails\grails-1.1.1
Base Directory: G:\grails-projects\mygdsflex3
Running script C:\Documents and Settings\Administrator\.grails\1.1.1\projects\mygdsflex3\plugins\gdsflex-0.7.2\scripts\Mxmlc.groovy
Environment set to development
compiling file mygdsflex3.mxml
Loading configuration file G:\grails-projects\mygdsflex3\web-app\WEB-INF\flex\flex-config.xml
error during compilation Line number support for XML tag attributes is not available. It is possible that compile-time MXML error reporting and component debugging may not give correct line numbers. Please make sure that xercesPatch.jar is in the classpath.
mygdsflex3.mxml compilation ended at: Tue Mar 16 19:57:31 CET 2010.
Thank you to help me.
Kwame
wonderful work! It works fine in development mode. however, when I jar it and moves to tomcat, I kept receiving "send failed" message. could you tell me why?
thanks!
Thanks a lot for this tutorial - the first part worked great thanks although I had to do 'grails html-wrapper' before running 'grails mxmlc' as someone helpfully suggested in the comments.
However the last part didn't work. After I added the Chapter.as and Book.as constructors nothing changed - I still got the same behaviour as before (blank message box etc).
Great Post! you have done a great job. Thanks for sharing it with us. Well done and keep posting Grails Application.
Enregistrer un commentaire