I'm rehashing it here for my own edification; it took me a while to read between the lines wrt/missing import statements & the implicit directory structure being used in the original posting. As I only toy with these technologies occasionally, I tend to forget the details...
I'm working with:
Caveat: I needed to disable AVG's Resident Shield to get Grails' grails run-app command to work in my environment. I'll need to recreate that scenario to tell you the exact (obscure) error I was getting.
Prep you environment by either running these commands from a command shell you're going to use or modify your System Control panel's Advanced/Environment Variables/User variables panel:
- SET GRAILS_HOME=somewhere\grails-1.3.5
- SET GWT_HOME=somewhere else\gwt-2.0.4
(I'll have to post a little batchfile I found on the net that switches these settings on the fly; e.g. gr8[.cmd] 1.0.4 will flip your current shell to Grails v1.0.4. I use this for Grails & GWT now)
Create a new Grails project via the usual means i.e.
- Open a command shell
- cd to your favourite project directory
- grails create-app projectName
- {a myriad of files are created}
- cd projectName
Create a new directory under projectName\lib i.e.
- cd .\lib
- md gwt
Put smartgwt.jar into this new directory i.e.
- copy path_to_smartgwt_zip_exploded\smartgwt.jar .\gwt\
And copy it into the lib directory too:
- copy .\gwt\smartgwt.jar .
Install the GWT plugin for Grails i.e.
- cd ..\
- grails install-plugin gwt
- {a myriad of files are created}
Create a domain object type e.g. net.myorg.domain.thing
- grails create-domain-class net.myorg.domain.thing
- {a myriad of files are created, incl. projectName\grails-app\domain\net\myorg\domain\Thing.groovy}
Edit the domain class
-
- package net.myorg.domain
-
- class Thing {
-
- String name
- String title
- String description
- Boolean isPublic = Boolean.TRUE
-
- static constraints = {
- name(unique: true, blank: false)
- title(blank: false)
- description(nullable: true)
- }
- }
Create a controller (aka servlet) for the domain object(s) e.g. net.myorg.controller.thing
- grails create-controller net.myorg.controller.thing
- {a myriad of files are created, incl. projectName\grails-app\controller\net\myorg\domain\ThingController.groovy}
Edit the controller
-
-
- package net.myorg.controller
-
- import groovy.xml.MarkupBuilder
- import com.smartgwt.client.data.RestDataSource
-
- class ThingController {
- def thingService
-
- def list = {
- log.info "ThingController.list( ${params} )"
- def things = thingService.findThings();
- def xml = new MarkupBuilder(response.writer)
- xml.response() {
- status(0)
- data {
- things.each { theThing ->
- flushThing xml, theThing
- }
- }
- }
- }
-
- def save = {
- log.info "ThingController.add( ${params} )"
- def theThing = thingService.save(params)
- def xml = new MarkupBuilder(response.writer)
- xml.response() {
- status(0)
- data {
- flushThing xml, theThing
- }
- }
- }
-
- def remove = {
- log.info "ThingController.remove( ${params} )"
- thingService.remove(params)
- def xml = new MarkupBuilder(response.writer)
- xml.response() {
- data {
- status(0)
- record {
- id(params.id)
- }
- }
- }
- }
-
- private def flushThing = { xml, thing ->
- xml.record(
- id: thing.id,
- name: thing.name,
- title: thing.title,
- description: thing.description,
- isPublic: thing.isPublic
- )
- }
- }
- grails create-service net.myorg.service.thing
- {a myriad of files are created, incl. projectName\grails-app\services\net\myorg\service\ThingService.groovy}
Edit the service
-
- package net.myorg.service
-
-
- import net.myorg.domain.Thing
-
- class ThingService {
-
- static transactional = true
-
- Thing findThing(Long id) {
- Thing.get(id)
- }
-
- def Thing[] findThings() {
- Thing.list()
- }
-
- def Thing save(Map
parameters) { - log.info "save( ${parameters} )"
- def theThing =
-
- Thing .get(parameters.id)
- if (!theThing) {
-
- theThing
- = new Thing()
- }
- theThing.properties = parameters;
- theThing.isPublic =
- "true".equals(parameters.isPublic) ? true : false
- theThing.save()
- }
-
- def remove(Map
parameters) { - log.info "remove( ${parameters} )"
- Thing.get(parameters.id)?.delete()
- }
- }
-
Compile the Groovy bits
- grails compile
Create a GWT module
- grails create-gwt-module net.myorg.app
- {2 files are produced: [a] projectName\src\gwt\net\myorg\App.gwt.xml and [b] projectName\src\gwt\net\myorg\client\App.java}
Edit the GWT module file (ie App.gwt.xml)
- Add the line:
<
inherits
name
=
"com.smartgwt.SmartGwt"
/>
- between:
<
inherits
name
=
"com.google.gwt.user.User"
/>
and:
<
entry-point
class
=
"net.myorg.client.App"
/>
Edit the GWT module's Java source file
-
- package net.myorg.client;
-
- import com.google.gwt.core.client.EntryPoint;
- import net.myorg.client.ds.ThingDs;
-
- import net.myorg.client.view.ThingView;
-
- public class app implements EntryPoint {
-
- public void onModuleLoad() {
- ThingDs thingDs = ThingDs.getInstance();
- ThingView thingView= new ThingView(thingDs);
- thingView.draw();
- }
- }
-
- touch src\gwt\net\myorg\client\ds\ThingDs.java
-
- package net.myorg.client.ds;
-
- import com.smartgwt.client.data.RestDataSource;
- import com.smartgwt.client.data.fields.DataSourceIntegerField;
- import com.smartgwt.client.data.fields.DataSourceTextField;
- import com.smartgwt.client.widgets.form.fields.TextItem;
- import com.smartgwt.client.widgets.form.fields.TextAreaItem;
- import com.smartgwt.client.data.fields.DataSourceBooleanField;
- import com.smartgwt.client.data.OperationBinding;
- import com.smartgwt.client.types.DSOperationType;
- import com.smartgwt.client.types.DSProtocol;
-
- public class ThingDs
- extends RestDataSource
- {
- private static ThingDs instance;
-
- public static ThingDs getInstance()
- {
- if (instance == null)
- {
- instance = new ThingDs();
- }
- return instance;
- }
-
- private ThingDs()
- {
- //set id + general stuff
- setID("thingDs");
- setClientOnly(false);
-
- //setup fields
- DataSourceIntegerField idField = new DataSourceIntegerField("id", "ID");
- idField.setCanEdit(false);
- idField.setPrimaryKey(true);
-
- DataSourceTextField nameField = new DataSourceTextField("name", "Name", 250, true);
- TextItem nameItem = new TextItem();
- nameItem.setWidth("100%");
- nameField.setEditorType(nameItem);
-
- DataSourceTextField titleField = new DataSourceTextField("title", "Title", 250, true);
- TextItem titleItem = new TextItem();
- titleItem.setWidth("100%");
- titleField.setEditorType(titleItem);
-
- DataSourceTextField descField = new DataSourceTextField("description", "Description", 500, false);
- TextAreaItem areaItem = new TextAreaItem();
- areaItem.setLength(500);
- areaItem.setWidth("100%");
- descField.setEditorType(areaItem);
-
- DataSourceBooleanField isPublicField = new DataSourceBooleanField("isPublic", "Public", 0, false);
-
- setFields(idField, nameField, titleField, descField, isPublicField);
-
- //setup operations
- //1. fetch
- OperationBinding fetch = new OperationBinding(DSOperationType.FETCH, "/projectName/thing/list");
- fetch.setDataProtocol(DSProtocol.POSTPARAMS);
-
- //2. update
- OperationBinding update = new OperationBinding(DSOperationType.UPDATE, "/projectName/thing/save");
- update.setDataProtocol(DSProtocol.POSTPARAMS);
-
- //3. add
- OperationBinding add = new OperationBinding(DSOperationType.ADD, "/projectName/thing/save");
- add.setDataProtocol(DSProtocol.POSTPARAMS);
-
- //4. remove
- OperationBinding remove = new OperationBinding(DSOperationType.REMOVE, "/projectName/thing/remove");
- remove.setDataProtocol(DSProtocol.POSTPARAMS);
- setOperationBindings(fetch, update, add, remove);
- }
- }
Create a Java View file ie net.myorg.client.view.ThingView.java
- touch src\gwt\net\myorg\client\view\ThingView.java
-
-
- package net.myorg.client.view;
-
- import com.smartgwt.client.widgets.layout.Layout;
- import com.smartgwt.client.widgets.layout.HLayout;
- import com.smartgwt.client.widgets.layout.VLayout;
- import com.smartgwt.client.widgets.events.ClickHandler;
- import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
- import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
- import com.smartgwt.client.widgets.events.ClickEvent;
- import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
- import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
- import com.smartgwt.client.widgets.grid.ListGrid;
- import com.smartgwt.client.widgets.viewer.DetailViewer;
- import com.smartgwt.client.widgets.Window;
- import com.smartgwt.client.widgets.form.DynamicForm;
- import com.smartgwt.client.widgets.IButton;
- import com.smartgwt.client.widgets.HeaderControl;
- import com.smartgwt.client.types.HeaderControls;
- import com.smartgwt.client.types.Alignment;
- import net.myorg.client.ds.ThingDs;
-
- public class ThingView
- extends HLayout
- implements ClickHandler
- {
- private final ThingDs thingDs;
- private final ListGrid table = new ListGrid();
- private final DetailViewer detail = new DetailViewer();
- private final Window formWindow = new Window();
- private final DynamicForm form = new DynamicForm();
- private final IButton addButton = new IButton();
- private final IButton saveButton = new IButton();
- private final IButton removeButton = new IButton();
- private final IButton cancelButton = new IButton();
-
- public ThingView(ThingDs thingDs)
- {
- this.thingDs = thingDs;
- initUi();
- }
-
- private void initUi()
- {
- //init myself
- initMySelf();
- //init listgrid
- initGrid();
- //a detail viewer
- Layout detailsComp = initDetailViewer();
- //form for editing
- initEditForm();
- //button layout
- addMember(table);
- addMember(detailsComp);
- }
-
- private void initMySelf()
- {
- setWidth100();
- setHeight100();
- setMembersMargin(5);
- setMargin(5);
- setPadding(10);
- }
-
- private void initGrid()
- {
- table.setDataSource(thingDs);
- table.setDataPageSize(20);
- table.setAutoFetchData(true);
- table.setAlign(Alignment.CENTER);
- table.setWidth("70%");
- table.setShowAllColumns(false);
- table.setWrapCells(true);
- table.setEmptyCellValue("---");
- table.addRecordClickHandler(new RecordClickHandler()
- {
- public void onRecordClick(RecordClickEvent recordClickEvent)
- {
- detail.viewSelectedData(table);
- }
- });
- table.addRecordDoubleClickHandler(new RecordDoubleClickHandler()
- {
- public void onRecordDoubleClick(RecordDoubleClickEvent recordDoubleClickEvent)
- {
- form.editSelectedData(table);
- formWindow.show();
- }
- });
- }
-
- private Layout initDetailViewer()
- {
- detail.setGroupTitle("Thing Details");
- detail.setDataSource(thingDs);
- detail.setShowEmptyMessage(true);
- //add button
- addButton.setTitle("ADD");
- addButton.setTooltip("Create a new Thing");
- addButton.addClickHandler(new ClickHandler()
- {
- public void onClick(ClickEvent clickEvent)
- {
- form.editNewRecord();
- formWindow.show();
- }
- });
- removeButton.setTitle("REMOVE");
- removeButton.setTooltip("Delete this Thing");
- removeButton.addClickHandler(new ClickHandler()
- {
- public void onClick(ClickEvent clickEvent)
- {
- table.removeSelectedData();
- table.fetchData();
- }
- });
- HLayout buttonPane = new HLayout();
- buttonPane.setAlign(Alignment.CENTER);
- buttonPane.addMember(addButton);
- buttonPane.addMember(removeButton);
- VLayout layout = new VLayout(10);
- layout.addMember(detail);
- layout.addMember(buttonPane);
- return layout;
- }
-
- private void initEditForm() {
- //the form
- form.setIsGroup(false);
- form.setDataSource(thingDs);
- form.setCellPadding(5);
- form.setWidth("100%");
- saveButton.setTitle("SAVE");
- saveButton.setTooltip("Save this Thing instance");
- saveButton.addClickHandler(new ClickHandler()
- {
- public void onClick(ClickEvent clickEvent)
- {
- form.saveData();
- formWindow.hide();
- }
- });
- cancelButton.setTitle("CANCEL");
- cancelButton.setTooltip("Cancel");
- cancelButton.addClickHandler(this);
- HLayout buttons = new HLayout(10);
- buttons.setAlign(Alignment.CENTER);
- buttons.addMember(cancelButton);
- buttons.addMember(saveButton);
- VLayout dialog = new VLayout(10);
- dialog.setPadding(10);
- dialog.addMember(form);
- dialog.addMember(buttons);
- //form dialog
- formWindow.setShowShadow(true);
- formWindow.setShowTitle(false);
- formWindow.setIsModal(true);
- formWindow.setPadding(20);
- formWindow.setWidth(500);
- formWindow.setHeight(350);
- formWindow.setShowMinimizeButton(false);
- formWindow.setShowCloseButton(true);
- formWindow.setShowModalMask(true);
- formWindow.centerInPage();
- HeaderControl closeControl = new HeaderControl(HeaderControl.CLOSE, this);
- formWindow.setHeaderControls(HeaderControls.HEADER_LABEL, closeControl);
- formWindow.addItem(dialog);
- }
-
- public void onClick(ClickEvent clickEvent)
- {
- formWindow.hide();
- }
- }
Create a (GSP) page to host the module
- grails create-gwt-page smart.gsp net.myorg.app
Edit the hosting page
Add the line:
<
script
>var isomorphicDir = "gwt/net.myorg.App/sc/"(sorry, closing script tag is not showing on blog)
just after the TITLE HTML tag
Move the hosting page
- move web-app\smart.gsp grails-app\views\
Edit the main layout page
- remove the reference to main.css
Compile the module
- grails compile-gwt-modules
Run the server
- grails run-app
View the page
- Browse to http://localhost:8080/projectName/smart.gsp
- Start editing Things