xiaochong / zkui

Grails ZK UI Plugin
http://grails.org/plugin/zkui
GNU Lesser General Public License v3.0
24 stars 8 forks source link

in MMVM view how do you pass an id into g:createLink (...) #54

Open woodmawa opened 12 years ago

woodmawa commented 12 years ago

Nearly there but i cant figure out how to this - i've had a go at converting your generated MVC (composer ) output into hand cranked MVVM equiv to make sure i can get it all to hang together

i've recoded the list view - which works ok except for the link in the grid

i use <z:template to render first column as objects id but as a link -

<html>
<head>
    <meta name="layout" content="main" />
    <g:set var="entityName" value="${message(code: 'book.label', default: 'Book')}" />
    <title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
    <z:window style="padding:5px" apply="org.zkoss.bind.BindComposer"
              viewModel="@id('vm') @init ('com.softwood.BookMVVMController')" >
        <z:hlayout>
            <z:toolbarbutton href="${createLink(action:'createMVVM')}" image="/images/skin/database_add.png" label="${message(code:'default.new.label',args:[entityName])}"/>
            <z:space/>
            <z:label value="${message(code:'book.id',default:'Id')}"/>
            <z:textbox id="idsearchtextbox" value="@bind (vm.searchFilter)"/>
            <z:space/>
            <z:button id="searchButton" 
                      label="${message(code:'search')}" 
                      onClick="@command ('search')"/>
        </z:hlayout>
        <g:if test="${flash.message}">
            <z:window mode="popup" border="normal">
                <z:hlayout>
                    <z:image src="/images/skin/information.png"/>
                    <z:div>
                        ${flash.message}
                    </z:div>
                </z:hlayout>
            </z:window>
        </g:if>
        <z:grid id="grid" emptyMessage="${message(code:'emptyMessage',default:'No Record')}"
                model="@bind (vm.bookList)">
            <z:columns sizable="true" >
                <z:column label="${message(code: 'book.id.label', default: 'Id')}" />
                <z:column label="${message(code: 'book.ISBN.label', default: 'ISBN')}"/>
                <z:column label="${message(code: 'book.title.label', default: 'Title')}"/>
                <z:column label="${message(code: 'book.dateCreated.label', default: 'Date Created')}"/>
                <z:column width="150px"/>
            </z:columns>
            <z:template name="model" var ="book">
                <row>
                    <a label="@load (book.id)" href="${g.createLink(controller:'bookMVVM', action:'editMVVM', id:'book.id')}" />
                    <label value="@load (book.ISBN)" />
                    <label value="@load (book.title)" />
                    <label value="@load (book.dateCreated)" />
                </row>
            </z:template>
        </z:grid>
        <z:paging autohide="true" id="paging" pageSize="15"/>
    </z:window>
</body>
</html>

but its that <a tag thats the difculty -

i want to format the link using grails createLink and set the id in the params map

                    <a label="@load (book.id)" href="${g.createLink(controller:'bookMVVM', action:'editMVVM', id:'book.id')}" />

but you say id:book.id - it says thats null, and your cant use id:@load (book.id) as throws parse errors on the page

how take that id in the template and call the grails createLink?

xiaochong commented 12 years ago

You should use mvvm way to implement it,please refer:

CRUD page by MVVM pattern

woodmawa commented 12 years ago

didnt really answer the question i'm having a diffiuclty with

i'm using a template to render the rows in the grid. i'm trying to setup the first column as a <a href link that would trigger the edit form - provided elsewhere

i'm trying to build that link dynamically using grails g:createLink and need to pass the id: param of current record to the link.

as there is a var="book" set on the template i'd have expected to be able to use that reference in the grails tag but it erros

        <z:template name="model" var ="book" >
                <row >
                    <a label="@load (book.id)" href="${g.createLink(controller:'bookMVVM', action:'editMVVM', id:'book.id')}" />
                    <label value="@load (book.ISBN)" />
                    <label value="@load (book.title)" />
                    <label value="@load (book.dateCreated)" />
                </row>
            </z:template>

your crud page by MVVM doesnt address the discussion pointm which is how do i create the <a tag dynamically and pass that id across?

if i take the quotes of the 'book.id' and use id:book id - it all fails with null pointer exception

so

with no quotes this time - which id hoped would work and pass the id - fails with npe

Will

woodmawa commented 12 years ago

hi - i think im not not getting this and i have a problem

the CRUD example you pointed to doesnt help on a number of fronts - heres why and where i am stuck

i've been trying to reuse a grails controller as controller and as a view model at same time - and this causes issues when i do a redirect from @command action routine back to a std controller action (see issue 55 )

so i've split the grails controller from the View model this time, and put the view model in the grailsApp/vms directory - where you intend them to go . But there are issues now - the grailsApp/vms directory is ordinary groovy directory - so it has no access to g.methods, or redirect etc - as these are added by grails to controllers.

So whats the consequence of that - in the view model you cant ask the binder to get the View todo a redirect (at least i cant see how to do that)

the CRUD example you link to avoids the issue - it keeps all the view and view model in one place - with the list, edit, and create actions concident in the same view at the same time (single view never leaving that page)

so in my example i try to get a button with onClick="@command ('edit')" from a create.gsp to call the view model - that works and saves my record. However the view model isnt a controller - so it cant redirect, and can only return to the view.

if it returns to the create.gsp view - i cant see how to get the view to to a redirect by the grails controller to another action automatically

heres snippit from the view model

@Command ("create") // triggered by binder 
def createNewInstance () {
    println "create new instance called on ${this}, with new book " + selectedBook //+  " and passed " + newBook

    def bookInstance = selectedBook  //binder writes attributes into selectedBook
    if (!bookInstance.save(flush: true) && bookInstance.hasErrors()) {
        log.error bookInstance.errors
        //self.renderErrors(bean: bookInstance)  how to do this in view?
    } else {
        println "created book ${bookInstance}"
        log.error "created book ${bookInstance}"
        //flash.message = g.message(code: 'default.created.message', args: [g.message(code: 'book.label', default: 'Book'), bookInstance.id])
        //flash.message = "book created id: " + bookInstance.id
        //redirect(controller: "BookMVVM2", action: "listMVVM")
    }
}

notice you cant use flash or redirect etc as its enhanced by grails to get these methods

here is my create gsp view

<z:window style="padding:5px" apply="org.zkoss.bind.BindComposer"
              viewModel="@id('vm') @init ('com.softwood.BookMVVM2ViewModel')" >
<z:grid>
    <z:columns sizable="true">
        <z:column label="${message(code:'name',default:'Name')}" width="100px"/>
        <z:column label="${message(code:'value',default:'Value')}"/>
    </z:columns>
    <z:rows>

        <z:row>
            <z:label value="${message(code:'book.ISBN.label',default:'ISBN')}"/>
            <z:textbox name="ISBN" value="@bind (vm.selectedBook.ISBN)" />
        </z:row>

        <z:row>
            <z:label value="${message(code:'book.title.label',default:'Title')}"/>
            <z:textbox name="title" value="@bind (vm.selectedBook.title)" />
        </z:row>

    </z:rows>
</z:grid>
<z:hlayout>
    <z:button id="saveButton" label="${message(code: 'default.button.create.label', default: 'Create')}"
              onClick="@command('create')" href="${g.createLink(action:'listMVVM')}"/>
    <z:button href="${g.createLink(action:'listMVVM')}" label="${message(code: 'default.list.label', args:[entityName])}"/>
</z:hlayout>
</z:window>
</body>
</html>

what i'd like to do is be able to get the view to redirect to another gsp - aka the list.gsp (i've even added href button to try and get it to go) that doesnt work

if a manually clik the second button - that does do a redirect and the list.gsp gets opened.

in this mode - my original issue which was that i couldnt pass an 'id:.id' into the g.createLink action cant be done anyhow as the View Model is just straight pojo with no grails controllers meta methods added .

so how to you drive the view from view model when you wan the view to move to next page ?

I hope my problem is clear to you - other than go back to MVC mode i'm not sure how get this to work under MVVM

xiaochong commented 12 years ago

In VM use org.zkoss.zk.ui.Executions for redirect

Executions.getCurrent().sendRedirect("/demo/index")

uris77 commented 12 years ago

Hi woodmawa,

This is what I did to solve the issue you had. First of all ,Executions.current.sendRedirect('/demo/edit/5') doesn't fit my need. Why? Because to pass params to the Composer, I need to use an ugly format: /demo/edit/id=5 I don't want that, I want to use clean URLs. To make this work, I have to use the normal grails controllers.

For example, if I have a controller Country, with the default actions, and I want to redirect a user to a page to edit a country they select from a list that is using MVVM, I do the following in my VM:

@NotifyChange public void setSelected(selectedCountry){ Executions.current.sendRedirect("/country/edit/${selectedCountry.id}") }

And in my CountryController:

def edit(){ Country instance = Country.get(params.id.toLong()) render(view: "edit", model: [countryInstance: instance]) }

peterbohm commented 12 years ago

You could also pass binding parameters e.g.

<button label="Edit" onClick="@command('edit', id=each.id)" /> 
@Command
public void edit(@BindingParam("id") Integer id) {
    Executions.current.sendRedirect("/contact/edit/${id}")
}

and do the Executions redirect in the wired edit method.

Here's more about the binding parameters http://books.zkoss.org/wiki/ZK%20Developer%27s%20Reference/MVVM/Advance/Parameters#A_Local_Command_Example