简体   繁体   English

CRUD中的自动生成Jquery自动完成功能会削弱脚手架(一对多关系)

[英]Autogenerate Jquery Autocomplete in CRUD grails scaffolding (one-to-many relationships)

I have altered the src/templates/scaffolding/renderEditor.template file in a grails project, in order to insert, the necessary html field boxes (and javascript code) to configure and use jquery autocomplete, in all the relationships "many-to-one". 我已在grails项目中更改了src / templates / scaffolding / renderEditor.template文件,以便在所有关系“多对多”中插入必要的html字段框(和javascript代码)以配置和使用jquery自动完成功能一”。 (The code is shown below) (代码如下所示)

The autogenerated autocomplete (_form.gsp) works correctly ... but I need to show the correct values (code and description) into the autocomplete textbox when a user edit a record using the scaffolding. 自动生成的自动完成(_form.gsp)可以正常工作...但是当用户使用脚手架编辑记录时,我需要在自动完成文本框中显示正确的值(代码和描述)。

To do that, I need to identify two fields inside the domain: one for code and the other for the description. 为此,我需要在域内标识两个字段:一个用于代码,另一个用于描述。

To face this problem, I have tried to create two dummies constraints, using the plugin "constraints", the first one used like code, the second one used like a description. 为了解决这个问题,我尝试使用插件“ constraints”创建两个虚拟约束,第一个使用类似代码,第二个使用类似描述。 I don't like this solution, because the contrainsts could be used several times into the domain. 我不喜欢这种解决方案,因为约束可能会在域中多次使用。

The code altered into the src/templates/scaffolding/renderEditor.template file is the following: (Note the two input boxes and Javascript code used for autocomplete): 更改为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


    }

The domain using the dummy contraints (autoid and autodesc): 使用伪约束(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}"
    }
}

The code inside the controler: 控制器内的代码:

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

The service: 服务:

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

    }
}

I want something like that inside the domain class: 我想要在域类中这样的东西:

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

Then access this array from the src/templates/scaffolding/renderEditor.template in order to get the field names (code and longName) and generate the correct html code in _forms.gsp and fix the problem. 然后从src / templates / scaffolding / renderEditor.template访问此数组,以获取字段名(代码和longName)并在_forms.gsp中生成正确的html代码并解决问题。

Other solution? 其他解决方案? Any Ideas? 有任何想法吗?

Many Thanks in Advance. 提前谢谢了。 ... and excuse my bad English. ...并原谅我的英语不好。

Reading and testing and testing again ... I have found the answer, follow the steps: 再次阅读,测试和测试...我找到了答案,请按照以下步骤操作:

  1. Add the following code to the Domain class: 将以下代码添加到Domain类中:

    class DomainClass { String codeField; class DomainClass {字符串codeField; String descriptionField; 字符串descriptionField; static autoCompleteConfig = ["codeField", "descriptionField"] } 静态autoCompleteConfig = [“ codeField”,“ descriptionField”]}

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

    private renderManyToOne(domainClass, property) { 私人renderManyToOne(domainClass,属性){

      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. Add the jquery libraries to src/templates/scaffolding/edit.gsp. 将jquery库添加到src / templates / scaffolding / edit.gsp。 Remember to install the jquery plugin: 记住要安装jquery插件:

  4. Write your own autoCompleteRoutine inside the Domain controller, something like: 在Domain控制器内编写自己的autoCompleteRoutine,例如:

    def autoCompleteList = { def domainAutoCompleteService = new DomainAutoCompleteService() render domainAutoCompleteService.domainList(params) as JSON } def autoCompleteList = {def domainAutoCompleteService = new DomainAutoCompleteService()将domainAutoCompleteService.domainList(params)呈现为JSON}

  5. Write your own domainAutoCompleteService, something like: 编写自己的domainAutoCompleteService,类似:

    package packageName 包packageName

     // 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. Generate the views .... and voila! 产生意见....,瞧! All is working. 一切正常。

Any Comments? 任何意见? I think can be more elegant but is the first step ... 我认为可以更加优雅,但这是第一步...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM