【问题标题】:Autogenerate Jquery Autocomplete in CRUD grails scaffolding (one-to-many relationships)在 CRUD grails 脚手架中自动生成 Jquery Autocomplete(一对多关系)
【发布时间】:2012-05-22 04:16:24
【问题描述】:

我已经更改了 grails 项目中的 src/templates/scaffolding/renderEditor.template 文件,以便在所有关系中插入必要的 html 字段框(和 javascript 代码)来配置和使用 jquery 自动完成“许多一对一”。 (代码如下所示)

自动生成的自动完成 (_form.gsp) 工作正常……但当用户使用脚手架编辑记录时,我需要在自动完成文本框中显示正确的值(代码和描述)。

为此,我需要识别域内的两个字段:一个用于代码,另一个用于描述。

为了解决这个问题,我尝试创建两个虚拟约束,使用插件“约束”,第一个用作代码,第二个用作描述。我不喜欢这个解决方案,因为约束可以在域中多次使用。

修改到 src/templates/scaffolding/renderEditor.template 文件的代码如下:(注意两个输入框和用于自动完成的 Javascript 代码):

private renderManyToOne(domainClass, property) {
    if (property.association) {


        /*  ORIGINAL CODE inside comments
            def sb = new StringBuilder()
        sb << '<g:select'
            // id is "x" and name is "x.id" as the label will have for="x" and "." in an id will confuse CSS
            sb << ' id="' << property.name << '"'
            sb << ' name="' << property.name << '.id"'
            sb << ' from="${' << property.type.name << '.list()}"'
            sb << ' optionKey="id"'
            if (isRequired()) sb << ' required=""'
            sb << ' value="${' << "${domainInstance}?.${property.name}" << '?.id}"'
            sb << ' class="many-to-one"'
            sb << renderNoSelection(property)
            sb << '/>'
            sb as String

            */


        def sb = new StringBuilder()

        // hidden field for domain.id

        sb << '<input type=\"hidden\" '
        sb << ' id="' << property.name << '.id"'
        sb << ' name="' << property.name  << '.id"'
        sb << ' value="${' << "${domainInstance}" << '?.id}" '
        sb << '/>\n'

        // Text field to show the description generated by autocomplete
        sb << '\t<input type=\"text\" '
        sb << ' id="' << property.name << '"'
        sb << ' name="' << property.name  << '"'
        if (isRequired()) sb << ' required="" '
        sb << 'style=\"width: 600px;\" '
        sb << ' value="${' << "${domainInstance}?.${property.name}" << '?.id}"'
        // sb << '${' << "${property.name}" << '"'
        sb << '/>'

        def js = new StringBuilder()
        js << '''
        <script type="text/javascript">

            /*
             * Remember include jquery and jquery-ui libraries into head section of edit.gsp file
             *   < g:javascript library="jquery"/>
             *   < g:javascript library="jquery-ui"/>
             */

            \$(document).ready(function() {
        '''
           js << '\t\$("#' << property.name << '").focus(function(){this.select(); });\n'

           js << '\t\t$("#' << property.name << '").autocomplete({\n'

           js << '''
                      source: function(request, response){
                          \$.ajax({
                              // Define Remote datasource into the controller
           '''
           js << '            \t\t url: "'
           js << '/' << grails.util.Metadata.current.'app.name' << '/' << property.name << '/' << 'autoCompleteList",'

           js << '''
                              data: request,
                              success: function(data){
                                  // Get the response (JSON format)
                                  response(data);
                              },
                              error: function(){
                                  // Handle server errors
                                  response("Error after search records. Try Again.")
                              }
                          });
                      },
                      // General options: Triggered only after minimum 2 characters have been entered and others
                      minLength: 2,
                      delay: 1,
                      autoFocus: true,
                      // Event handler when user selects a Loinc from the list.
                      select: function(event, ui) {
                          // update the hidden field.
           '''
           js <<  '\t\t\t\t  \$("#' << property.name << '\\.id").val(ui.item.id);'
           js << '''
                      }
                });
           });
        </script>
        '''
        sb << js
        sb as String


    }

使用虚拟约束(autoid 和 autodesc)的域:

class LOINC {

    static searchable = {
        only = ["code", "shortName", "longName", "property", "system", "scale", "method", "time"]
    }

    String code         // LOINC_NUM         * 0
    String shortName    // SHORTNAME         * 29
    String longName     // LONG_COMMON_NAME  * 35
    String name         // BASE_NAME         * 21
    String component    // COMPONENT         * 1
    String property     // PROPERTY          * 2
    String time         // TIME_ASPCT        * 3
    String system       // SYSTEM            * 4
    String scale        // SCALE_TYP         * 5
    String method       // METHOD_TYP        * 6

    static constraints = {
        code(nullable: false, unique: true, blank: false, maxSize: 100, autoid: true)
        shortName(nullable: false)
        longName(nullable: false, autodesc: true)
        name(nullable: false, maxSize: 100)
        component(nullable: false)
        property(nullable: false)
        time(nullable: false)
        system(nullable: false)
        scale(nullable: false)
        method(nullable: false)
    }

    String toString(){
        "${code} ${longName}"
    }
}

控制器内部的代码:

 def autoCompleteList = {
        def loincAutoCompleteService = new LOINCAutoCompleteService()
        render loincAutoCompleteService.loincList(params) as JSON
 }

服务:

class LOINCAutoCompleteService {

    def loincList(params) {

        // Creates a new query Object
        def query = {
            or {
                like("code", "${params.term}%") // term is the parameter send by jQuery autocomplete
                like("longName", "${params.term}%")
                like("shortName", "${params.term}%")
            }
            projections { // good to select only the required columns.
                property("id")
                property("code")
                property("longName")
            }
        }

        def loincSelectList = [] // aArray List to add each Loinc details
        def clist = LOINC.createCriteria().list(query)


        clist.each {
            // Add to map. jQuery autocomplete expects the JSON object to be with id/label/value
            def loincMap = [:]

            loincMap.put("id", it[0])

            // Label is text showed int he drop-down list
            loincMap.put("label", it[1] + " : " + it[2])

            // Values is the code to be returned when the user select an item from drop-down list
            loincMap.put("value", it[1] + " : " + it[2])

            // Add the row to the array list
            loincSelectList.add(loincMap)
        }


        return loincSelectList

    }
}

我想要在域类中类似的东西:

<code>
static autocompleteAble = {
        fields = ["code", "longName"]
}
</code>

然后从 src/templates/scaffolding/renderEditor.template 中访问该数组,以获取字段名称(代码和 longName)并在 _forms.gsp 中生成正确的 html 代码并修复问题。

其他解决方案?有什么想法吗?

在此先感谢您。 ...请原谅我的英语不好。

【问题讨论】:

    标签: grails jquery-autocomplete grails-plugin scaffolding


    【解决方案1】:

    阅读测试再测试……我找到了答案,按步骤操作:

    1. 将以下代码添加到 Domain 类中:

      类域类 { 字符串代码字段; 字符串描述字段; 静态 autoCompleteConfig = ["codeField", "descriptionField"] }

    2. 更改 src/templates/scaffolding/renderEditor.template(仅 renderManyToOne 方法):

      private renderManyToOne(domainClass, property) {

          def AUTOCOMPLETE_PROPERTY = "autoCompleteConfig"
          def className = property.type.name
      
          def autoCompleteProperty = org.codehaus.groovy.grails.commons.GrailsClassUtils.getStaticPropertyValue(property.referencedDomainClass.clazz, AUTOCOMPLETE_PROPERTY)
          def sb = new StringBuilder()
          // sb << "\n<!-- getFullName(): " <<   domainClass.getFullName() << " property.type.name: " << property.type.name << " property.referencedDomainClass.propertyName: " << property.referencedDomainClass.propertyName <<  "     property.referencedDomainClass: " << property.referencedDomainClass <<  " -->\n"
          if (autoCompleteProperty != null) {
      
              if (autoCompleteProperty[0] ) {
                  if (property.association) {
      
                      // hidden field for domain.id
      
                      sb << '<input type=\"hidden\" '
                      sb << ' id=  "' << property.name << '.id"'
                      sb << ' name="' << property.name << '.id"'
                      sb << ' value="${' << "${domainInstance}" << '?.id}" '
                      sb << '/>\n'
      
                      // Text field to show the description generated by autocomplete
                      sb << '\t<input type=\"text\" '
                      sb << ' id=  "' << property.name  << '_' << (autoCompleteProperty[1]? autoCompleteProperty[1]:'Description')  << '\" '
                      sb << ' name="' << property.name  << '_' << (autoCompleteProperty[1]? autoCompleteProperty[1]:'Description')  << '\" '
                      if (isRequired()) sb << ' required="" '
                      sb << 'style=\"width: 600px;\" '
      
                      sb << ' value="${'
                      sb << "${domainInstance}?.${property.name}" << '?.' << autoCompleteProperty[0] << '}' << (autoCompleteProperty[1]? '' : '"' )
      
                      if (autoCompleteProperty[1]) {
                          sb << ': ${' << "${domainInstance}?.${property.name}" << '?.' << autoCompleteProperty[1] << (autoCompleteProperty[2]? '}' : '}"' )
                      }
      
                      if (autoCompleteProperty[2]) {
                          sb << ': ${' << "${domainInstance}?.${property.name}" << '?.' << autoCompleteProperty[2] << '}"'
                      }
      
      
                      sb << ' />'
      
                      def js = new StringBuilder()
                      js << '''
                      <script type="text/javascript">
      
                          /*
                           * Remember include jquery and jquery-ui libraries into head section of edit.gsp file
                           *   < g:javascript library="jquery"/>
                           *   < g:javascript library="jquery-ui"/>
                           *
                           */
      
                          \$(document).ready(function() {
                      '''
                         js << '\t\$("#' << property.name << '").focus(function(){this.select(); });\n'
      
                         js << '\t\t\t\t\t\t' // Tabs to sort the output
                         js << '\$("#' << property.name  << '_' << (autoCompleteProperty[1]? autoCompleteProperty[1]:'Description')  << '").autocomplete({\n'
      
                         js << '''
                                    source: function(request, response){
                                        \$.ajax({
                                            // Define Remote datasource into the controller
                         '''
                         js << '            \t\t url: "'
                         js << '/' << grails.util.Metadata.current.'app.name' << '/' << property.name << '/' << 'autoCompleteList",'
      
                         js << '''
                                            data: request,
                                            success: function(data){
                                                // Get the response (JSON format)
                                                response(data);
                                            },
                                            error: function(){
                                                // Handle server errors
                                                response("Error after search records. Try Again.")
                                            }
                                        });
                                    },
                                    // General options: Triggered only after minimum 2 characters have been entered and others
                                    minLength: 2,
                                    delay: 1,
                                    autoFocus: true,
                                    // Event handler when user choose un item from the list.
                                    select: function(event, ui) {
                                        // update the hidden field.
                         '''
                         js <<  '\t\t\t\t  '
                         js << '\$("#' << property.name << '\\\\.id").val(ui.item.id);'
      
                         js << '''
      
                                    }
                              });
                         });
                      </script>
                      '''
                      sb << js
                      sb as String
      
      
                  }
              }
          } else {
      
      
              sb << '<g:select'
              // id is "x" and name is "x.id" as the label will have for="x" and "." in an id will confuse CSS
              sb << ' id="' << property.name << '"'
              sb << ' name="' << property.name << '.id"'
              sb << ' from="${' << property.type.name << '.list()}"'
              sb << ' optionKey="id"'
              if (isRequired()) sb << ' required=""'
              sb << ' value="${' << "${domainInstance}?.${property.name}" << '?.id}"'
              sb << ' class="many-to-one"'
              sb << renderNoSelection(property)
              sb << '/>'
              sb as String
      
      
          }
      
      }
      
    3. 将 jquery 库添加到 src/templates/scaffolding/edit.gsp。记得安装jquery插件:

    4. 在域控制器中编写您自己的 autoCompleteRoutine,类似于:

      def autoCompleteList = { def domainAutoCompleteService = new DomainAutoCompleteService() 将 domainAutoCompleteService.domainList(params) 渲染为 JSON }

    5. 编写自己的 domainAutoCompleteService,类似:

      包包名

      // Change the words "Domain" and "domain" with your own Domain class name
      class DomainAutoCompleteService {
      
          def domainList(params) {
      
              // Creates a new query Object
              def query = {
                  or {
                      // term is the parameter send by jQuery autocomplete
                      like("codeField", "${params.term}%") 
                      like("descriptionField", "${params.term}%")
                      like("otherField", "${params.term}%")
                  }
                  projections { // good to select only the required columns.
                      property("id")
                      property("codeField")
                      property("descriptionField")
                  }
              }
      
              def domainSelectList = [] 
              // Replace the word "Domain" by your own domain Name
              def clist = Domain.createCriteria().list(query)
      
      
              clist.each {
                  // Add to map. jQuery autocomplete expects the JSON object to be with id/label/value
                  def map = [:]
      
                  map.put("id", it[0])
      
                  // Label is text showed int he drop-down list
                  map.put("label", it[1] + " : " + it[2])
      
                  // Values is the code to be returned when the user select an item from drop-down list
                  map.put("value", it[1] + " : " + it[2])
      
                  // Add the row to the array list
                  domainSelectList.add(map)
              }
      
      
              return domainSelectList
      
          }
      }
      
    6. 生成视图 .... 瞧!一切正常。

    有什么意见吗?我觉得可以更优雅不过是第一步……

    【讨论】:

    • Jaime - 我认为这是一个非常酷的解决方案。如果您有时间并且可以弄清楚如何将它们包装在一起,那将是一个很好的插件。
    猜你喜欢
    • 2010-10-04
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多