Data binding Grails命令对象数据绑定

Data binding Grails命令对象数据绑定,data-binding,grails,groovy,command-objects,Data Binding,Grails,Groovy,Command Objects,Grails具有将请求参数绑定到域对象及其关联的功能。这在很大程度上依赖于检测以.id结尾的请求参数,并自动从数据库加载这些参数 但是,不清楚如何填充命令对象的关联。以以下为例: class ProductCommand { String name Collection<AttributeTypeCommand> attributeTypes ProductTypeCommand productType } 我使用此界面填充GSP中的产品和属性类型选择列

Grails具有将请求参数绑定到域对象及其关联的功能。这在很大程度上依赖于检测以
.id
结尾的请求参数,并自动从数据库加载这些参数

但是,不清楚如何填充命令对象的关联。以以下为例:

class ProductCommand {

    String name
    Collection<AttributeTypeCommand> attributeTypes 
    ProductTypeCommand productType
}
我使用此界面填充GSP中的产品和属性类型选择列表。我还将此接口注入命令对象,并使用它“模拟”命令对象上的
attributeType
productType
属性

class ProductCommand {

    ProductAdminService productAdminService

    String name   

    List<Integer> attributeTypeIds = []
    Integer productTypeId

    void setProductType(ProductTypeCommand productType) {
        this.productTypeId = productType.id
    }

    ProductTypeCommand getProductType() {
        productAdminService.productTypes.find {it.id == productTypeId}        
    }

    Collection<AttributeTypeCommand> getAttributeTypes() {

        attributeTypeIds.collect {id ->
            productAdminService.getAttributeType(id)
        }
    }

    void setAttributeTypes(Collection<AttributeTypeCommand> attributeTypes) {
        this.attributeTypeIds = attributeTypes.collect {it.id}
    }
}
class-ProductCommand{
ProductAdminService ProductAdminService
字符串名
列表AttributeTypeId=[]
整数productTypeId
void setProductType(ProductTypeCommand productType){
this.productTypeId=productType.id
}
ProductTypeCommand getProductType(){
productAdminService.productTypes.find{it.id==productTypeId}
}
集合getAttributeType(){
attributeTypeIds.collect{id->
productAdminService.getAttributeType(id)
}
}
void setAttributeType(集合AttributeType){
this.attributeTypeIds=attributeTypes.collect{it.id}
}
}

实际情况是,
attributeType ID
productTypeId
属性绑定到相关的请求参数,getter/setter“模拟”
productType
attributeType
属性。有没有更简单的方法来填充命令对象的关联

我在一些项目中看到的是使用Apache Commons集合中的Lazy*集合类。它使用如下代码惰性地初始化命令关联:

class ProductCommand {

  String name
  String type

  List<AttributeTypeCommand> attributes = org.apache.commons.collections.list.LazyList.decorate(new ArrayList(), new org.apache.commons.collections.functors.InstantiateFactory(AttributeTypeCommand.class))
}

class AttributeTypeCommand {
  // ...
}
class-ProductCommand{
字符串名
字符串类型
名单

更新:

Groovy 2.0(它还不是Grails发行版的一部分)将嵌入对惰性和急切列表的支持

更新:

随着Grails2.2.0的发布,Groovy2.0成为发行版的一部分


您确实需要为AttributeType和productType属性设置子命令吗?您不使用PropertyEdit或支持绑定的任何原因?例如:

public class ProductTypeEditor extends PropertyEditorSupport
{
    ProductAdminService productAdminService // inject somewhow
    void setAsText(String s)
    {
        if (s) value = productAdminService.productTypes.find { it.id == s.toLong() }
    }

    public String getAsText()
    {
        value?.id        
    }
}
(和attributeType对象类似),并在编辑器注册器中注册:

import java.beans.PropertyEditorSupport
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(ProductType, new ProductTypeEditor())
        reg.registerCustomEditor(AttributeType, new AttributeTypeEditor())
    }
}
并在您的资源中注册。groovy:

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}
然后在Cmd中,您只需:

class ProductCommand {
    String name
    List<AttributeType> attributeTypes = []
    ProductType productType
}

希望我没有误解您试图实现的目标:)

我在嵌套命令对象方面也遇到了同样的问题,因此我采取了以下解决方法:

  • 我明确地将其他域对象作为参数添加到我的 控制器动作
  • 另外,为嵌套的命令对象显式调用bindData() (通常,包装其他命令对象的命令对象 将成功绑定其数据,而无需绑定它 显然,这取决于您的视图命名约定)
  • 然后我对这些命令对象调用了.Validate()
  • 使用这些对象检查.hasErrors()的错误
  • 要保存域对象,请同时显式指定每个嵌套对象 属性及其对应的命令对象

  • 下面是一个示例伪代码:

    class CommandObjectBig{
    
        String name
        CommandObjectSmall details
    
        static constraints = {
          name (blank: false)
        }
    
    }
    
    
    class CommandObjectSmall{
    
        String address
    
        static constraints = {
          address (blank: false)
        }
    
    }
    
    在控制器中:

    .
    .
    .
    
    def save = { CommandObjectBig cob, CommandObjectSmall cos ->
    
    //assuming cob is bounded successfully by grails, and we only need to handle cos
    
    bindData(cos, params.details)
    cos.validate()
    
    //then do you code logic depending on if cos or cob has errors
    
    if(cob.hasErrors() || cos.hasErrors())
    render(view: "create", model: [bigInstance: cob, smallInstance: cos])
    }
    else
    {
     //create the Domain object using your wrapper command object, and assign its details
     //property it's value using cos command object instance, and call the save on you
     //command object and every thing should go smoothly from there
       .
       .
       .
    
    }
    .
    .
    .
    

    • 希望grails的未来版本能够解决这个问题,并允许开发人员添加一个可选的params或params作用域来分配给每个命令对象,这可能会很有用:)

    Grails中的命令对象

    @Validateable
    class UserProfileInfoCO {
        String name
        String addressLine1
        String addressLine2
        String city
        String state
        String zip
        String contactNo
    
        static constraints = {
            name(nullable: false, blank: false)
            addressLine1(nullable: true, blank: true)
            addressLine2(nullable: true, blank: true)
            city(nullable: true, blank: true)
            state(nullable: true, blank: true)
            zip(nullable: true, blank: true, maxSize: 6, matches: "[0-9]+")
            contactNo(blank: true, nullable: true)
        }
    }
    
    在Grails中,命令对象类似于域类,但不持久化数据。在Grails中使用命令对象是一种在不需要创建域对象的情况下执行数据绑定和验证的简单方法

    您需要做的第一件事是描述您的命令对象。可以在包含将使用它的控制器的同一文件中进行描述。如果命令对象将由多个控制器使用,请在groovy源目录中描述它

    声明命令对象

    @Validateable
    class UserProfileInfoCO {
        String name
        String addressLine1
        String addressLine2
        String city
        String state
        String zip
        String contactNo
    
        static constraints = {
            name(nullable: false, blank: false)
            addressLine1(nullable: true, blank: true)
            addressLine2(nullable: true, blank: true)
            city(nullable: true, blank: true)
            state(nullable: true, blank: true)
            zip(nullable: true, blank: true, maxSize: 6, matches: "[0-9]+")
            contactNo(blank: true, nullable: true)
        }
    }
    
    使用命令对象

    @Validateable
    class UserProfileInfoCO {
        String name
        String addressLine1
        String addressLine2
        String city
        String state
        String zip
        String contactNo
    
        static constraints = {
            name(nullable: false, blank: false)
            addressLine1(nullable: true, blank: true)
            addressLine2(nullable: true, blank: true)
            city(nullable: true, blank: true)
            state(nullable: true, blank: true)
            zip(nullable: true, blank: true, maxSize: 6, matches: "[0-9]+")
            contactNo(blank: true, nullable: true)
        }
    }
    
    接下来您可能要做的是将控制器中的操作接收的数据绑定到命令对象,并对其进行验证

     def updateUserProfile(UserProfileInfoCO userProfileInfo) {     
      // data binding and validation
       if (!userProfileInfo.hasErrors()) {
          //do something
       } 
    
    }
    

    我不知道你的问题的答案,但就我个人而言,我认为这是一个很好的插件材料,甚至是未来Grails发行版中的一个特性。“对LazyList的每次get(索引)调用都会评估该列表是否已经在该位置上有一个元素”当然,您想要的是,每当向列表中添加项时,列表都会自动增长,因为当请求参数绑定到命令对象时会发生这种情况?例如,如果“attriutes[0]”被引用,但属性列表为空,InstanceFactory将使用给定类的默认构造函数并创建一个新实例并将其添加到索引0。此实例用于将请求参数与“attributes[0].xxx”绑定但在我看来,Grails将尝试做的第一件事是在列表中设置一个项目,而
    LazyList
    的文档清楚地指出,只有当从列表中检索到一个项目时,列表才会增长Grails必须首先执行“attributes[0]”,这将导致一个List.get(index)调用,在LazyList/InstanceFactory的情况下,该调用将创建指定类的新实例。如果使用对象初始化列表,则此机制不适用。是的,gsp中会有一个与属性相对应的字段。例如,使用隐藏输入:对于单个关联,您会有多个在我的子命令版本中,输入类似于数组符号将其映射到惰性列表中的位置(我认为您不需要这样做)。
     def updateUserProfile(UserProfileInfoCO userProfileInfo) {     
      // data binding and validation
       if (!userProfileInfo.hasErrors()) {
          //do something
       } 
    
    }