$ brew install openjdk@25
$ brew install git
- Guida all’installazione
- Prima applicazione
- Come funziona l’applicazione con il Taack Framework?
- Creare un nuovo plugin dell’applicazione
- Gestire i futuri aggiornamenti
- Costruire un menu per l’intera applicazione
- Creare le classi
- Creare una tabella
- Aggiungere pulsanti a un blocco tabella
- Creare un modulo e salvare oggetti
- Mostrare un oggetto
- Eliminare un oggetto
- Assegnare un nome alle intestazioni
- Filtro
- Aggiungere un secondo menu
- Collegare gli autori ai libri
- Risoluzione dei problemi ed errori comuni
Guida all’installazione
Requisiti
Per iniziare, assicurati che i seguenti strumenti siano installati sulla tua macchina:
-
Java (JDK 25 …)
-
Git
Installare i prerequisiti
macOS (Homebrew)
Windows (Winget)
winget install --id Microsoft.OpenJDK.25
winget install --id Git.Git
Clonare l’intranet pronta per lo sviluppo
$ git clone https://github.com/Taack/intranet.git
Questo repository include uno scheletro minimale di intranet (gestione utenti Crew + plugin Spring Security) pronto per lo sviluppo.
Avviare la tua intranet
$ cd intranet
$ ./gradlew server:bootRun
|
Il comando |
Grails application running at http://localhost:9442
TODO: Accedi all’intranet aprendo quell’indirizzo nel tuo browser. Utilizza le credenziali di accesso predefinite:
-
Nome utente:
admin -
Password:
ChangeIt
Per modificare la password di amministratore predefinita, modifica i seguenti file:
-
server/grails-app/conf/application.yml -
server/grails-app/conf/application-MACOSX.yml
Cerca la riga:
taack.admin.password: ChangeIt
Dopo aver effettuato la modifica, elimina il file intranetDb.mv.db per applicare la nuova password.
|
|
|
L’eliminazione del file |
Configurare un database persistente
Se desideri che i dati siano persistenti, modifica i file server/grails-app/conf/application.yml e
server/grails-app/conf/application-MACOSX.yml per utilizzare un database persistente.
environments:
development:
dataSource:
dbCreate: update (1)
url: jdbc:h2:./intranetDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE (2)
| 1 | Modalità di creazione (vedi la documentazione di Grails GORM) |
| 2 | intranetDb: radice del nome del file contenente i dati |
Produrre un file JAR per Tomcat
$ ./gradlew server:assemble
Il JAR assemblato (ad es., server-0.6.jar) si troverà in server/build/libs.
cd server/build/libs
java -jar server-[version].jar (1)
| 1 | Il numero di versione si trova in build.gradle. Il valore predefinito è 0.6 |
|
Assicurati che l’applicazione non sia già in esecuzione quando avvii una nuova istanza. |
Configurare il tuo IDE
Consigliamo vivamente di utilizzare l’ultima versione di IntelliJ Ultimate Edition per il suo supporto completo a Groovy e Grails.
IntelliJ Ultimate Edition
Consigliamo di installare l'IntelliJ Taack Plugin, che puoi installare dall’IntelliJ Marketplace TaackUiASTAutocomplete o direttamente dal repository del codice sorgente.
Apri il progetto, vai su File > Open… e seleziona intranet/settings.gradle.
Assicurati che Gradle stia utilizzando il JDK 25:

| 1 | Apri le impostazioni di Gradle |
| 2 | Verifica che la versione della JVM di Gradle sia 25 (il fornitore può essere qualsiasi) |
Visual Studio Code
Se preferisci Visual Studio Code, consigliamo le seguenti estensioni per utilizzare questo framework:
|
La maggior parte delle funzionalità di Grails e del Taack Framework non verrà riconosciuta da VS Code. Le importazioni, la navigazione e l’assistenza al codice potrebbero essere limitate. IntelliJ rimane l’IDE preferito per un supporto completo. |
Prima applicazione
Come funziona l’applicazione con il Taack Framework?
Il tuo progetto intranet è diviso in due parti:
-
I plugin dell’applicazione, situati nella cartella
app, sono micro-progetti, ciascuno con le proprie dipendenze, la propria build, ecc. (ad es., Crew). Per una spiegazione più approfondita su come vengono dichiarati i plugin, consulta Informazioni sui plugin. -
Il server principale utilizza tutti i plugin dell’applicazione implementati nel suo file
build.gradlee li visualizza nella pagina principale dell’intranet.
Creare un nuovo plugin dell’applicazione
Per creare un nuovo plugin dell’applicazione (un modulo Gradle), assicurati di scaricare l’ultima versione della tua intranet dal nostro repository GitHub (vedi installazione).
$ ./gradlew -DmodName=myBooks server:generateTaackAppTask
Questo crea una nuova cartella app/myBooks.
|
Sotto |
Per far riconoscere al tuo IDE il modulo appena creato, ricarica Gradle facendo clic sull’icona mostrata di seguito:
Gestire i futuri aggiornamenti
Nella maggior parte dei casi, l’aggiornamento della tua intranet comporterà l’aggiornamento della cartella buildSrc, del file gradle.properties e, meno frequentemente, dei file build.gradle delle applicazioni.
|
Infatti, questo processo è comune nello sviluppo software a lungo termine e non dovrebbe intimorire i nuovi arrivati. |
Costruire un menu per l’intera applicazione
TODO: Costruiremo due menu, uno per i libri e uno per gli autori. In ciascun menu potrai aggiungere nuovi elementi, filtrarli e visualizzare ogni voce in una tabella.
Concentriamoci sulla navigazione per la tua nuova applicazione, myBooks.
Il passo iniziale consiste nel definire come verrà costruito il menu. Vai su grails-app/services/my/books/MyBooksUiService.
static UiMenuSpecifier buildMenu(String q = null) { (1)
new UiMenuSpecifier().ui {
menu MyBooksController.&index as MC
}
}
| 1 | Aggiungi static se non è già presente. |
Creiamo un oggetto UiMenuSpecifier per definire la struttura del menu, quindi specifichiamo il contenuto da visualizzare.
menu MyBooksController.&index as MC
In questo caso, menu crea un semplice link nella barra di navigazione. index è il nome mostrato per il link e MyBooksController.&index (utilizzando MethodClosure) definisce l’azione a cui il link reindirizzerà.
Creare le classi
Crea una classe Groovy chiamata Author in grails-app/domain/my/books.
import grails.compiler.GrailsCompileStatic
import taack.ast.annotation.TaackFieldEnum
@GrailsCompileStatic
@TaackFieldEnum
class Author {
String firstName
String lastName
Date dateOfBirth
}
Crea un’altra classe Groovy chiamata Book in grails-app/domain/my/books.
import grails.compiler.GrailsCompileStatic
import taack.ast.annotation.TaackFieldEnum
@GrailsCompileStatic
@TaackFieldEnum
class Book {
String title
String authorName
int numberOfPages
}
|
Usa sempre |
Creare una tabella
Ora visualizziamo una tabella nella nostra pagina listBook. Vai su grails-app/services/my/books/MyBooksUiService.
buildBookTable:UiTableSpecifier buildBookTable() {
Book book = new Book()
UiTableSpecifier bookTableSpecifier = new UiTableSpecifier()
bookTableSpecifier.ui {
//Add table content inside the closure here
}
}
Qui stiamo definendo una nuova tabella che elencherà le istanze di Book. Usa import taack.ui.dsl.UiTableSpecifier.
bookTableSpecifier.ui {
// -- Header --
header {
column {
sortableFieldHeader book.title_ (1)
}
column {
label book.authorName_
}
column {
label book.numberOfPages_
}
column {
label "Delete book"
}
}
}
| 1 | Nota che qui usiamo sortableFieldHeader, questo rende la colonna ordinabile; puoi ordinare i libri in ordine alfabetico facendo clic sull’intestazione book.title.
Ora popoleremo la nostra tabella iterando sulle istanze di Book nel database utilizzando il metodo DSL di tabella iterate. |
bookTableSpecifier.ui {
//table headers...
iterate(taackFilterService.getBuilder(Book) (1)
.setMaxNumberOfLine(8) (2)
.setSortOrder(TaackFilter.Order.DESC, book.title_) (3)
.build()) { Book bookIterator ->
rowColumn {
rowField bookIterator.title_ (4)
}
rowColumn {
rowField bookIterator.authorName_
}
rowColumn {
rowField bookIterator.numberOfPages_
}
rowColumn {
rowAction ActionIcon.DELETE * IconStyle.SCALE_DOWN, (5)
MyBooksController.&deleteBook as MethodClosure, bookIterator.id
}
}
}
| 1 | Usa import taack.domain.TaackFilterService. |
| 2 | Verranno visualizzati solo i primi otto libri. |
| 3 | Imposta l’ordine di visualizzazione in base al titolo dei libri in ordine decrescente (usa import taack.domain.TaackFilter). |
| 4 | Il trattino basso (underscore) è necessario qui. |
| 5 | Usa import taack.ui.dsl.common.ActionIcon
import taack.ui.dsl.common.IconStyle. |
|
Dobbiamo creare l’azione |
Vai su grails-app/controllers/my/books/MyBooksController.groovy.
index:def deleteBook () {}
Per ogni libro nella nostra lista, creiamo una nuova riga con il titolo del libro nella prima colonna, seguito dall’autore, dal numero di pagine e da un pulsante Delete nella quarta colonna.
La tua tabella è ora completa; dobbiamo solo renderizzarla nella pagina.
Per renderizzare gli UiSpecifier precedentemente costruiti, dobbiamo usare taackUiService, che dovrebbe essere già importato nel controller creato dal comando create-taack-app.
Lo faremo in un’azione chiamata listBook: all’interno del metodo index, sostituisci il suo contenuto con un reindirizzamento all’azione listBook.
listBook:def index() {
redirect action: 'listBook'
}
listBook e aggiungi il seguente codice:UiTableSpecifier tableBookSpecifier = myBooksUiService.buildBookTable() (1)
taackUiService.show(new UiBlockSpecifier().ui {
table tableBookSpecifier
}, MyBooksUiService.buildMenu())
| 1 | Ottiene il tableSpecifier che abbiamo creato da MyBooksUiService. |
Usa import taack.ui.dsl.UiTableSpecifier.
taackUiService.show(UiBlockSpecifier block, UiMenuSpecifier menu) si occuperà di renderizzare la specifica che gli passiamo.
Utilizziamo il metodo statico buildMenu() creato in precedenza da MyBooksUiService come secondo argomento di show() per renderizzare il menu insieme alla pagina.
Ora puoi avviare il server e navigare verso la tua nuova applicazione (myBooks.app).
La tabella dovrebbe essere funzionante, ma al momento vedrai solo le intestazioni della tabella poiché non ci sono libri nel tuo database.
Procediamo quindi con la creazione di un modulo e il salvataggio degli oggetti nel database.
Aggiungere pulsanti a un blocco tabella
Aggiungiamo un pulsante alla tabella dei libri che apra una finestra modale tramite AJAX per creare un nuovo libro. Per farlo, dobbiamo aggiungere una closure nella tabella.
taackUiService.show(new UiBlockSpecifier().ui {
table tableBookSpecifier, {
//Added Closure here
menuIcon ActionIcon.CREATE, this.&bookForm as MethodClosure (1)
}
}, MyBooksUiService.buildMenu())
| 1 | Dovrai definire l’azione bookForm, proprio come abbiamo fatto con deleteBook. |
Non dimenticare di usare import taack.ui.dsl.common.ActionIcon.
Ora avvia il server e naviga di nuovo verso la tua nuova applicazione; vedrai un pulsante Create in alto a destra nella tabella.
Il metodo menuIcon è composto dai seguenti parametri:
-
L’icona utilizzata dal pulsante deve essere un valore dell’enum
ActionIcon. -
L’azione a cui il pulsante reindirizzerà.
Creare un modulo e salvare oggetti
Ora realizzeremo il modulo che verrà utilizzato per creare e aggiornare i libri.
Per gestire entrambi i casi, in MyBooksUiService inizializzeremo un nuovo Book oppure lo leggeremo se è stato passato un ID Book nei parametri della richiesta.
buildBookForm:UiFormSpecifier buildBookForm(Book book) {
book ?= new Book(params) (1)
}
| 1 | Ciò significa che se book è null, gli viene assegnata una nuova istanza utilizzando i parametri della richiesta. |
Ora definiamo il suo modulo e il suo contenuto.
buildBookForm (importa le classi necessarie):UiFormSpecifier bookFormSpecifier = new UiFormSpecifier()
bookFormSpecifier.ui book, {
//Section of fields
section "Book details", {
field book.title_
field book.authorName_
field book.numberOfPages_
}
//Save button
formAction MyBooksController.&saveBook as MethodClosure (1)
}
| 1 | Dovrai definire l’azione saveBook (come per deleteBook) in MyBooksController. |
Usa import taack.ui.dsl.UiFormSpecifier.
Una volta definito il modulo, puoi visualizzarlo utilizzando il metodo taackUiService.show().
bookForm in MyBooksController:def bookForm(Book book) {
UiFormSpecifier tableFormSpecifier = myBooksUiService.buildBookForm book
taackUiService.show new UiBlockSpecifier().ui {
modal {
form tableFormSpecifier
}
}
}
Usa import taack.ui.dsl.UiFormSpecifier.
Questa volta non specifichiamo buildMenu nella chiamata a show, poiché non vogliamo che il menu venga renderizzato all’interno della finestra modale.
saveBook (importa e inizializza le classi necessarie):@Transactional
def saveBook() {
taackSaveService.saveThenReloadOrRenderErrors(Book)
}
Usa import taack.render.TaackSaveService e import grails.gorm.transactions.Transactional.
|
Vedi Chiudere la modale e ricaricare la pagina per scoprire come evitare l’uso di |
Avvia nuovamente il server; ora dovresti essere in grado di fare clic sul pulsante e compilare il modulo.
|
Nell’angolo in alto a sinistra, puoi passare da un gruppo di libri all’altro utilizzando i numeri di pagina (1 o 2). Questa navigazione appare perché l’elenco è limitato a 8 libri per pagina. |
Mostrare un oggetto
Ora che possiamo creare libri e vederne un elenco in una tabella, visualizziamo ogni libro più nel dettaglio utilizzando una finestra modale.
Ancora una volta definiamo lo specifier e lo renderizziamo all’interno di un blocco modale utilizzando taackUiService.show().
showBook (importa la classe necessaria):def showBook(Book book) {
// Define the show displayed fields
UiShowSpecifier showSpec = new UiShowSpecifier().ui({
fieldLabeled book.title_
fieldLabeled book.authorName_
})
taackUiService.show(new UiBlockSpecifier().ui {
modal {
show showSpec
}
})
}
Usa import taack.ui.dsl.UiShowSpecifier.
Dobbiamo anche visualizzare un link a questa finestra modale nella tabella. Per farlo, aggiungi la seguente riga nella stessa rowColumn (sotto il campo del titolo del libro) in cui desideri che appaia il pulsante in MyBooksUiService:
buildBookTable:UiTableSpecifier buildBookTable() {
//Section of code
rowColumn {
//Add these 2 lines
rowAction ActionIcon.SHOW * IconStyle.SCALE_DOWN, (1)
MyBooksController.&showBook as MethodClosure, bookIterator.id
rowField bookIterator.title_
}
//Section of code
}
| 1 | Qui riduciamo la dimensione dell’icona con l’operatore di moltiplicazione. |
Questo creerà un piccolo pulsante icona nella cella della tabella che aprirà una finestra modale con le informazioni del libro corrispondente.
Nota che ActionIcon è stato moltiplicato per un IconStyle per modificarne la dimensione in questo caso.
Fai clic sull’icona dell’occhio per visualizzare il libro.
Eliminare un oggetto
Ricordi il pulsante Delete che abbiamo aggiunto alla nostra tabella?
Rendiamo funzionante il pulsante Delete definendo l’azione deleteBook che richiama.
@Transactional
def deleteBook(Book book) {
book.delete()
redirect action: 'index'
}
|
In alcuni casi, è preferibile aggiungere un campo |
Utilizziamo il metodo delete di Grails per eliminare il libro dal database e quindi reindirizziamo all’azione index dove si trova la tabella dei libri.
Nota che i nomi dei campi non sono etichette dirette, ma chiavi i18n come book.numberOfPages.label,default.numberOfPages.label (che fanno riferimento alle chiavi i18n in messages.properties). Modifichiamolo!
Assegnare un nome alle intestazioni
Per impostare il nome delle intestazioni, vai su app/myBooks/grails-app/i18n/messages.properties.
default.numberOfPages.label e le altre etichette con i nomi che preferisci:default.authorName.label=Author Name
default.title.label=Title
default.numberOfPages.label=Number of Pages
myBooks.index.label=Books
myBooks.app=Book
myBooks.desc=Book storage
Avvia il server per visualizzare i nomi delle etichette aggiornati.
Filtro
Se aggiungiamo troppi libri potremmo perdere tempo a cercare un libro specifico; filtrare i libri può rendere la ricerca più efficiente. Implementiamolo in MyBooksUiService.
buildBookFilter:UiFilterSpecifier buildBookFilter(Book book) {
UiFilterSpecifier bookFilterSpecifier = new UiFilterSpecifier() (1)
bookFilterSpecifier.ui Book, { (2)
section "Book Filter", { (3)
filterField book.title_ (4)
}
}
}
| 1 | Crea il filtro. Usa import taack.ui.dsl.UiFilterSpecifier. |
| 2 | Passa la classe dell’oggetto e la closure contenente le specifiche del filtro tramite il metodo ui. |
| 3 | Crea una sezione con etichetta "Book Filter". |
| 4 | Aggiungi il campo al filtro, nota il trattino basso alla fine del nome del campo. |
Il passo successivo consiste nel visualizzare questo filtro appena creato.
taackUiService.show in listBook (situato in MyBooksController):def listBook() {
Book book = new Book()
UiTableSpecifier tableBookSpecifier = myBooksUiService.buildBookTable()
UiFilterSpecifier filterBookSpecifier = myBooksUiService.buildBookFilter book
taackUiService.show(new UiBlockSpecifier().ui {
tableFilter filterBookSpecifier, tableBookSpecifier, {
menuIcon ActionIcon.CREATE, this.&bookForm as MethodClosure
}
}, MyBooksUiService.buildMenu())
}
Usa import taack.ui.dsl.UiFilterSpecifier.
Per informazioni più dettagliate sui filtri, fai riferimento alla documentazione di filtri e tabelle.
Aggiungere un secondo menu
Ricordi la classe Author che abbiamo creato all’inizio? È arrivato il momento di usarla! Creeremo un secondo menu in cui potremo aggiungere autori e filtrarli.
buildMenu in MyBooksUiService:menu MyBooksController.&listAuthor as MC
Questo crea un secondo menu nell’angolo in alto a sinistra per accedere alla pagina degli autori.
|
Puoi anche creare sottomenu annidati all’interno del menu principale.
Questo crea un sottomenu con etichetta "Menu" contenente un link all’azione |
MyBooksUiService:UiTableSpecifier buildAuthorTable(Boolean isSelect = false ) {
Author author = new Author()
UiTableSpecifier authorTableSpecifier = new UiTableSpecifier()
authorTableSpecifier.ui {
header {
column {
sortableFieldHeader author.firstName_
}
column {
sortableFieldHeader author.lastName_
}
column {
label author.dateOfBirth_
}
column {
label "Delete Author"
}
}
iterate(taackFilterService.getBuilder(Author)
.setMaxNumberOfLine(8)
.setSortOrder(TaackFilter.Order.DESC, author.lastName_)
.build()) { Author authorIterator ->
rowColumn {
rowField authorIterator.firstName_
rowAction ActionIcon.SHOW * IconStyle.SCALE_DOWN,
MyBooksController.&showAuthor as MethodClosure, authorIterator.id
}
rowColumn {
rowField authorIterator.lastName_
}
rowColumn {
rowField authorIterator.dateOfBirth_
}
rowColumn {
rowAction ActionIcon.DELETE * IconStyle.SCALE_DOWN,
MyBooksController.&deleteAuthor as MC, authorIterator.id
}
}
}
}
UiFormSpecifier buildAuthorForm(Author author) {
author ?= new Author(params)
UiFormSpecifier authorFormSpecifier = new UiFormSpecifier()
authorFormSpecifier.ui author, {
section "Author details", {
field author.firstName_
field author.lastName_
field author.dateOfBirth_
}
formAction MyBooksController.&saveAuthor as MC
}
}
UiFilterSpecifier buildAuthorFilter() {
Author author = new Author()
UiFilterSpecifier authorFilterSpecifier = new UiFilterSpecifier()
authorFilterSpecifier.ui Author, {
section "Author Filter", {
filterField author.lastName_
}
}
}
Nota che isSelect in buildAuthorTable verrà utilizzato più avanti.
listAuthor in MyBooksController:def listAuthor() {
UiTableSpecifier tableAuthorSpecifier = myBooksUiService.buildAuthorTable()
UiFilterSpecifier filterAuthorSpecifier = myBooksUiService.buildAuthorFilter()
taackUiService.show(new UiBlockSpecifier().ui {
tableFilter filterAuthorSpecifier, tableAuthorSpecifier, {
menuIcon ActionIcon.CREATE, this.&authorForm as MethodClosure
}
}, MyBooksUiService.buildMenu())
}
deleteAuthor, saveAuthor, authorForm e showAuthor:def authorForm(Author author) {
UiFormSpecifier tableFormSpecifier = myBooksUiService.buildAuthorForm author
taackUiService.show new UiBlockSpecifier().ui {
modal {
form tableFormSpecifier
}
}
}
@Transactional
def deleteAuthor(Author author) {
author.delete()
redirect action: 'index'
}
@Transactional
def saveAuthor() {
taackSaveService.saveThenReloadOrRenderErrors(Author)
}
def showAuthor(Author author) {
UiShowSpecifier showSpec = new UiShowSpecifier().ui(author, {
fieldLabeled author.firstName_
fieldLabeled author.lastName_
})
taackUiService.show(new UiBlockSpecifier().ui {
modal {
show showSpec
}
})
}
default.firstName.label=First Name
default.lastName.label=Last Name
default.dateOfBirth.label=Date of Birth
myBooks.listAuthor.label= Authors
Ora hai menu separati per Libri e Autori, ma al momento operano in modo indipendente. E se potessi selezionare un autore direttamente dall’elenco degli autori registrati quando crei un libro? Implementiamo questa funzionalità.
Collegare gli autori ai libri
Author:String toString() {
return firstName + ' ' + lastName
}
Book:Author author
|
Modifica tutte le occorrenze di |
|
Modificare il nome o il campo di una classe è fortemente sconsigliato, poiché richiederà di cancellare i file contenenti i dati aggiunti in precedenza.
Devi eliminare |
messages.properties:default.author.label=Author Name
buildAuthorTable situato in MyBooksUiService:rowColumn {
rowField authorIterator.firstName_
rowAction ActionIcon.SHOW * IconStyle.SCALE_DOWN,
MyBooksController.&showAuthor as MethodClosure, authorIterator.id
// Add the following if statement
if (isSelect)
rowAction tr('default.role.label'), ActionIcon.SELECT * IconStyle.SCALE_DOWN, authorIterator.id, authorIterator.toString()
}
Questo aggiungerà un pulsante di selezione per scegliere l’autore durante la compilazione di bookForm. Usa import static taack.render.TaackUiService.tr.
buildBookForm situato in MyBooksUiService, sostituendo field book.author_:ajaxField book.author_, MyBooksController.&selectAuthor as MC
Questa riga chiamerà selectAuthor quando si seleziona un autore in bookForm.
Implementiamo selectAuthor.
selectAuthor in MyBooksController:def selectAuthor() {
UiTableSpecifier t = myBooksUiService.buildAuthorTable true (1)
UiFilterSpecifier f = myBooksUiService.buildAuthorFilter()
taackUiService.show new UiBlockSpecifier().ui {
modal {
tableFilter f, t
}
}
}
| 1 | Nota che isSelect è true.
Questo renderizzerà una versione compatta della tabella Author e del relativo filtro durante la selezione di un autore per il modulo del libro. |
Ora disponi di un’interfaccia CRUD completamente funzionante per le tue entità Book e Author utilizzando il framework DSL Taack, senza dover scrivere manualmente viste HTML o GSP.
Ora sei pronto per esplorare le funzionalità più avanzate del Taack UI Framework.
Benvenuto!
Risoluzione dei problemi ed errori comuni
ERROR: Cannot resolve symbol 'MyBooksUiService'
Hai dimenticato di ricaricare Gradle dopo aver creato il plugin. Fai clic sul pulsante di ricarica di Gradle nel tuo IDE.
ERROR: Field 'authorName_' not found
Hai rinominato il campo authorName in author (un riferimento ad Author). Assicurati di aver modificato tutti gli authorName_ in author_ sia nel modulo che nella tabella.
ERROR: No matching method for action...
Assicurati di aver dichiarato il metodo corrispondente in MyBooksController e di aver utilizzato correttamente la sintassi MethodClosure: MyBooksController.&myMethod as MethodClosure.
Potrebbe mancare l’annotazione @Transactional nel metodo saveBook oppure hai dimenticato di importare taackSaveService.
Se hai modificato i campi nelle classi di dominio, devi eliminare i file del database:
intranet/server/intranetDb.mv.db
intranet/server/intranetDb.trace.db. Fai attenzione, poiché questo cancellerà tutti i dati salvati in precedenza.
Potresti utilizzare l’annotazione @Transactional sbagliata. Assicurati di utilizzare quella del package GORM e non quella di Javax.
Potresti voler controllare l’integrità dei dati e i DDL generati dal database H2 integrato.
Per farlo devi modificare le impostazioni di Spring Security.
Naviga verso Root → /server → /grails-app → /conf
Troverai un file application.groovy.
Aggiungi
[pattern: '/h2-console/**', access: ['permitAll']],
a questo blocco di configurazione:
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
[pattern: '/', access: ['permitAll']],
[pattern: '/error', access: ['permitAll']],
[pattern: '/index', access: ['permitAll']],
[pattern: '/index.gsp', access: ['permitAll']],
[pattern: '/shutdown', access: ['permitAll']],
[pattern: '/assets/**', access: ['permitAll']],
[pattern: '/**/js/**', access: ['permitAll']],
[pattern: '/**/css/**', access: ['permitAll']],
[pattern: '/**/images/**', access: ['permitAll']],
[pattern: '/**/favicon.ico', access: ['permitAll']]
]