首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

精通 Grails:AJAX 實現多對多關聯與優化使用者介面

     編輯:Java 相關知識

多對多關聯在 Web 應用中常見且棘手。本文以行程規劃器為例,示範 Grails 如何透過 GORM 優雅建模多對多關係,並以 AJAX 提升大量選項下的使用者體驗。

關聯建模演進:從 1:N 到 M:N

一對多:航空公司與行程

class Airline {
    static hasMany = [flights: Flight]
    String name, iata, frequentFlier
}
class Flight {
    static belongsTo = [trip: Trip, airline: Airline]
    String flightNumber
    Date departureDate, arrivalDate
    Airport departureAirport, arrivalAirport
}

belongsTo 為 Map 形式可建立多重單向關聯;iata 儲存 IATA 代碼(如 UAL、DAL)。

機場取代城市:更精確的地點模型

class Airport {
    static hasMany = [flights: Flight]
    String name, iata, city, state, country
}

Flight 以 departureAirport/arrivalAirport 明確關聯,**不使用 belongsTo**,故刪除機場不連帶刪除航班,體現鬆耦合設計。

資料庫層面的連接表

MySQL 實際產出 4 張表:

mysql> show tables;
+----------------+
| Tables_in_trip |
+----------------+
| airline        |
| airport        |
| flight         |  -- 連接表
| trip           |
+----------------+

flight 表含 trip_id、airline_id、departure_airport_id、arrival_airport_id 等外鍵,完美對應領域模型。

航班建立表單(腳手架預設)

介面優化:下拉選單 vs AJAX 自動完成

預設 <g:select> 生成

<g:select optionKey="id"
          from="${Airline.list()}"
          name="airline.id"
          value="${flight?.airline?.id}" />

渲染為標準 <select>,選項值為 airline.id,顯示文字為 toString() 結果。

機場數量大時的痛點

全球機場數千,下拉選單不可用。IATA 代碼(如 DEN、ORD)為業界通用識別碼,適合作為輸入介面。

AJAX 方案:即時驗證 IATA 代碼

Controller 端:回傳 JSON

import grails.converters.*
class AirportController {
    def scaffold = Airport
    def getJson = {
        def airport = Airport.findByIata(params.iata)
        if (!airport) {
            airport = new Airport(iata: params.iata, name: 'Not found')
        }
        render airport as JSON
    }
}

測試:https://localhost:9090/trip/airport/getJson?iata=den

{"id":1,"class":"Airport","city":"Denver","country":"US","iata":"DEN","name":"Denver International Airport","state":"CO"}

View 端:Prototype 驅動 AJAX

<g:javascript library="prototype" />
<script>
function get(airportField) {
    var baseUrl = "${createLink(controller: 'airport', action: 'getJson')}"
    var url = baseUrl + "?iata=" + $F(airportField + "Iata")
    new Ajax.Request(url, {
        method: 'get',
        asynchronous: true,
        onSuccess: function(req) { update(req.responseText, airportField) }
    })
}
function update(json, airportField) {
    var airport = eval("(" + json + ")")
    var output = $(airportField + "Text")
    output.innerHTML = airport.iata + " - " + airport.name
    var hiddenField = $(airportField + ".id")
    airport.id == null ? hiddenField.value = -1 : hiddenField.value = airport.id
}
</script>

表單改造

<div id="departureAirportText">[Type an Airport IATA Code]</div>
<input type="hidden" name="departureAirport.id" value="-1" id="departureAirport.id"/>
<input type="text" name="departureAirportIata" id="departureAirportIata"/>
<input type="button" value="Find" onClick="get('departureAirport')"/>

改良後表單

AJAX 填充後表單

客戶端驗證:防無效送出

<g:form action="save" method="post" onsubmit="return validate()">
function validate() {
    if ($F("departureAirport.id") == -1) {
        alert("Please supply a valid Departure Airport")
        return false
    }
    if ($F("arrivalAirport.id") == -1) {
        alert("Please supply a valid Arrival Airport")
        return false
    }
    return true
}

關鍵洞見

  • 多對多本質上是兩個一對多,**第三個實體(如 Flight、Contract)擁有獨立屬性與生命週期**
  • 級聯刪除與否由 belongsTo 決定,視業務語義靈活配置
  • 選項少用 <select>/Radio/Checkbox;選項多用文字輸入 + AJAX 驗證
  • 腳手架非終點,而是起點——專注於調優關鍵介面

結語

掌握多對多建模與 AJAX 介面優化,下一篇將整合 Google Maps 視覺化航班路線,並介紹 Grails Service 層架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved