Versione 0.7.10 e 0.7.11

  • Nuovi temi (cliccare su config e selezionare un tema)

  • Riprogettazione del salvataggio per evitare bind

Versione 0.7.9

Example 1. Nuovo filtro filterFieldExpressionReverse
Filtro filterFieldExpressionReverse
def parentParts() {
    UiFilterSpecifier f = new UiFilterSpecifier().sec PlmFreeCadPart, {         (1)
        PlmFreeCadLink link = new PlmFreeCadLink()
        filterFieldExpressionReverse(link.part_,                                (2)
                true,                                                           (3)
                new FilterExpression(null as Object, Operator.NE, link.part_))  (4)
    }
    taackUiService.show(new UiBlockSpecifier().ui {
        tableFilter(plmFreeCadUiService.buildPartFilter(), plmFreeCadUiService.buildPartTable(null, f), {
            menuIcon ActionIcon.GRAPH, this.&model as MC
        })
    }, buildMenu())
}
1 Stiamo costruendo un filtro per PlmFreeCadPart
2 Vogliamo elencare gli assiemi padre, ovvero quelli per cui il campo PlmFreeCadLink part non punta al PlmFreeCadPart elencato
3 true sta per notIn
4 Letteralmente: vogliamo tutti i PlmFreeCadPart che non sono figli
HQL generato
select
    distinct sc  ,sc.dateCreated
from PlmFreeCadPart sc
where
    sc.id NOT IN (select auo.part.id from PlmFreeCadLink auo where auo.part IS NOT NULL)    (1)
    and sc.nextVersion IS NULL
order by sc.dateCreated DESC
1 Clausola where aggiunta dall’espressione filterFieldExpressionReverse

Versione 0.7.8

  • Versione di manutenzione

Versione 0.7.7

Example 2. Nuovo componente calendario
Visualizzazione a tabella
def calendarTable(TaackCalendarParams calendarParams) {
    UiTableSpecifier t = taackCalendarUiService.calendarUiTable(calendarEvents.collect {    (1)
        new TaackCalendarEventAdapter(it.key, it.value)
    }.iterator() as Iterator<TaackCalendarEvent>, calendarParams)
    taackUiService.show new UiBlockSpecifier().ui {
        table t
    }, RootController.buildMenu()
}
1 Crea una tabella da un iteratore TaackCalendarEvent. Utilizza il pattern Adapter per convertire qualsiasi oggetto in TaackCalendarEvent.
news calendar table view
Visualizzazione standard
def calendarEntries(TaackCalendarParams calendarParams) {
    taackUiService.show taackCalendarUiService.calendarBlock(CalendarController.&fromCustom as MC, calendarEvents.collect {
        new TaackCalendarEventAdapter(it.key, it.value)
    }.iterator() as Iterator<TaackCalendarEvent>, calendarParams), RootController.buildMenu()}
news calendar regular view

Versione 0.7.6

  • Miglioramento dell’editor personalizzato

  • Grails 7.2.0

Versione 0.7.5

show grid layout

Example 3. Layout a griglia per Show (Show Grid Layout)
Disporre uno Show su una griglia responsiva tramite ShowLayout.GRID
new UiShowSpecifier().ui ShowLayout.GRID, {                      (1)
    fieldLabeled item.ref_
    fieldLabeled item.name_
    fieldLabeled item.itemStatus_
    fieldLabeled item.gtin_
    fieldLabeled Style.SPAN_TWO, item.labeling_                  (2)
    fieldLabeled Style.BOLD + Style.SPAN_FULL, item.description_ (3)
}
1 Passare ShowLayout.GRID per disporre i campi su una griglia ad adattamento automatico invece che verticalmente in una colonna. Il valore predefinito è ShowLayout.LIST.
2 Ogni campo occupa per impostazione predefinita una cella della griglia. Style.SPAN_TWO / Style.SPAN_THREE possono essere utilizzati per far sì che le celle si estendano su più colonne.
3 Style.SPAN_FULL fa sì che un campo occupi l’intera riga. Gli span si combinano con qualsiasi altro stile tramite + (qui con Style.BOLD).

Si noti che la griglia adatta automaticamente tante colonne quante ne consente la larghezza del contenitore, ciascuna larga almeno 220px (grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)) sulla classe taackShowGrid; il valore minimo può essere sovrascritto nel proprio CSS).

Example 4. Nuova sostituzione del contenuto della finestra modale
Sostituire qualsiasi contenuto di una finestra modale superiore
@Transactional
def doComplexStuff() {
    boolean isDone = false

    // doComplexStuff

    taackUiService.show(new UiBlockSpecifier().ui {
        replaceModal {
            custom("<h1> ${isDone ? 'Done.' : 'Not Done.'} </h1>")
        }
    })
}
Example 5. Nuovo salvataggio o chiusura della modale con aggiornamento
Codice
@Transactional
@Secured(['ROLE_ADMIN', 'ROLE_CONSOLATOR_ADMIN'])
def saveLlmRag() {
    taackSaveService.saveOrCloseModalAndRefresh(LlmRag)     (1)
}
1 Se più finestre modali sono impilate, è ora possibile aggiornare la modale sottostante all’ultima, che verrà chiusa.

Versione 0.7.4

Example 6. Colonne fisse nelle tabelle (Table Sticky Columns)
Bloccare le colonne più a sinistra tramite TableOption
TableOption tableOption = new TableOption.TableOptionBuilder()
        .stickyColumns(2).build() (1)

new UiTableSpecifier().ui tableOption, {
    header {
        sortableFieldHeader p.ref_
        sortableFieldHeader p.name_
        // ... any number of columns ...
    }
}
1 Numero di colonne più a sinistra da bloccare. Rimangono visibili mentre la tabella scorre orizzontalmente.

Si noti che le tabelle che utilizzano colonne fisse non avranno vincoli di larghezza e utilizzeranno invece la classe CSS taack-sticky-table, che limita la larghezza massima di ciascuna colonna a 12rem.

Versione 0.7.2

Example 7. Kan-ban con Drag & Drop
Specifica Kanban senza filtro
new UiKanbanSpecifier().ui {
    ReminderStatus.values().each { ReminderStatus status ->             (1)
        column(this.&changeStatus as MC, [newStatus: status.name()]) {  (2)
            header status.name()                                        (3)
            reminders.grep { Reminder t -> t.status == status }
                     .sort { -it.lastUpdated.getTime() }
                     .each { Reminder rIt ->
                card(rIt.selfObject_) {                                 (4)
                    cardField rIt.title_, null, Style.BOLD + Style.BLUE
                    cardFieldRaw this.getReminderDoersHtml(rIt),
                        new Style('', 'position: absolute; right: 3px; top: 12px;')
                    cardAction ActionIcon.SHOW * IconStyle.SCALE_DOWN,
                        ReminderController.&showReminder as MethodClosure, rIt.id
                    cardField rIt.priority_
                }
            }
        }
    }
}
1 Iterare sulle colonne
2 Definire una colonna
3 Nome dell’intestazione di questa colonna
4 Creare la scheda con cardField, cardFieldRaw e cardAction
screenshot kanban
Figure 1. Kan-Ban
Example 8. Modificare oggetti nella tabella
Dichiarare il blocco rowQuickEdit
rowQuickEdit(CrmController.&applyLeadLine as MC, ll.id) {           (1)
    rowColumn {
        rowFieldEdit ll.salePrice_                                  (2)
        rowFieldEdit ll.qty_                                        (2)
        rowField nf.format(tot), style                              (3)
    }
}
1 rowQuickEdit dichiara quale azione chiamare in caso di modifica in linea
2 rowFieldEdit modifica il valore in un campo di input OPPURE visualizza il contenuto se non consentito
3 Gli altri campi vengono ricalcolati durante le modifiche
Definizione di applyLeadLine
@Transactional
def applyLeadLine() {
    LeadLine ll = new LeadLine()
    taackSaveService.saveThenReloadOrRenderErrors
                    LeadLine, ll.qty_, ll.salePrice_            (1)
}
1 Durante il salvataggio, elencare i campi che possono essere aggiornati solo nel blocco rowQuickEdit
screenshot inline edit
Figure 2. Modifica in linea con feedback di errore localizzato
Example 9. Nuova serializzazione dei filtri
Dichiarare il filtro
UiFilterSpecifier createReminderFilter(FieldInfo... leftField) {
    Reminder r = new Reminder()
    User u = new User()
    new UiFilterSpecifier(leftField).ui Reminder, {                 (1)
        section Reminder, {                                         (2)
            filterField r.assignee_, u.username_
            filterField r.status_
            filterField r.priority_
            filterField r.title_
        }
    }
}
1 leftField elenca i campi che puntano a un promemoria nell’oggetto contenitore
2 Nuova section con argomento classe per calcolare automaticamente l’i18n
Unione di filtri (Joining Filter)
UiFilterSpecifier createReminderLinkFilter(FieldInfo... leftField) {
    ReminderLink rl = new ReminderLink()
    new UiFilterSpecifier(leftField).ui(ReminderLink, {
        section tr('reminderLink.label'), {
            filterField rl.kind_
        }
    }).join(createReminderFilter(rl.reminder_))                     (1)
}
1 Il campo reminder è un Reminder; riutilizziamo il filtro della classe Reminder.
screenshot filter join
Figure 3. Filtri uniti
Example 10. Nuova opzione di layout rowCol
        taackUiService.show createModalForm(reminder, {
            section {
                field reminder.title_
                rowCols 3, {                                        (1)
                        field reminder.dueDate_
                        field reminder.status_
                        field reminder.priority_
                }
                ajaxField reminder.assignee_, CrewController.&selectUserM2O as MC
                field reminder.body_
            }
1 Evita di usare row, seguito da col, col, col …​
screenshot form rowcol
Figure 4. rowCol nel modulo
Example 11. Nuova istruzione di layout poke per l’aggiornamento dei blocchi
taackUiService.show(new UiBlockSpecifier().ui {
    table statsService.buildTable()                                 (1)

    row {
        poke p1, {                                                  (2)
            col BlockSpec.Width.HALF, {
                diagram(statsService.&buildChart1 as MethodClosure)
            }
        }
        poke p2, {                                                  (2)
            col BlockSpec.Width.HALF, {
                diagram(statsService::buildChart2 as MethodClosure)
            }
        }
    }
}
1 Cliccando su una tabella è possibile aggiornare altri blocchi
2 poke p1 e p2 sono booleani. Se true, i blocchi all’interno del blocco poke verranno aggiornati
Example 12. Fisarmonica (Accordion)
accordion {
    accordionItem('Filter') {
        form CrmDashboardUiService.filterForm(filter)
    }
}
Example 13. scrollPanel
scrollPanel('512px') {
    UiTableSpecifier t
    boolean seeSalesmen = params.boolean('seeSalesmen')
    boolean seeTop = params.boolean('seeTop')
    if (seeTop) {
        t = crmDashboardUiService.topSalesmen(leads)
    } else if (seeSalesmen) {
        t = this.crmDashboardUiService.topCountries(leads)
    } else {
        t = crmDashboardUiService.topCustomer(leads)
    }
    table(t) {
        menu 'Salesmen', this.&group as MC, [seeTop: true, seeSalesmen: false]
        menu 'Customers', this.&group as MC, [seeTop: false, seeSalesmen: false]
        menu 'Countries', this.&group as MC, [seeTop: false, seeSalesmen: true]
    }
}
screenshot scrollpanel

Versione 0.7.1

  • Correzione dell’i18n con il task bootRun quando l’hot-reload è abilitato

Novità nella versione 0.7.1

Versione 0.7.0

Esempio di codice per il menu contestuale
TaackUiService.registerContextualMenuClosure(                           (1)
    new Lead().leadStatus_,
    new UiMenuSpecifier().ui { Long id ->                               (2)
        label 'Change Status'
        Lead.read(id)?.leadStatus?.transitionsTo()?.each {
            menu it.toString(),                                         (3)
                CrmController.&changeLeadStatus as MC,
                [id: id, leadStatus: it.toString()]
    }
})
1 Registrare un menu contestuale per un campo
2 Utilizzare la normale closure MenuSpecifier (come per qualsiasi menu)
news contextual menu on field
Figure 5. Risultato di un clic destro sullo stato di un Lead
Creare un filtro che contrassegna le voci non lette
TaackFilter.FilterBuilder tf = taackFilterService.getBuilder(Item)
        .setMaxNumberOfLine(10)
        .setSortOrder(TaackFilter.Order.DESC, i.dateCreated_)

tf.setLastReadingDate(lastAccessDateFromUser)   (1)
1 Gli oggetti più recenti saranno in grassetto, vedere Righe non lette in cui lastUpdated dell’oggetto > lastAccessDateFromUser
news notif
Figure 6. Righe non lette in cui lastUpdated dell’oggetto > lastAccessDateFromUser
Registrare come verrà mostrato un oggetto
TaackGormClassRegisterService.register(
        new TaackGormClass(ItemRange.class).builder
                .setShowMethod(Bp2Controller.&showRange as MethodClosure)   (1)
                .build(),
        new TaackGormClass(Item.class).builder
                .setShowMethod(Bp2Controller.&showItem as MethodClosure)    (1)
                .setShowLabel({ Long id ->                                  (2)
                    def i = Item.read(id)
                    return "${i.name} ${i.ref ?: ''} ($id)"
                })
                .setTypeLabel({ Long id ->                                  (3)
                    return TaackUiService.tr('default.item.label')
                }).build()
)
1 Come mostrare l’oggetto
2 Etichetta personalizzata
3 Tipo per il faceting
Builder di diagrammi
new UiDiagramSpecifier().ui {
    bar {
        labels 'xLabel1', 'xLabel2', 'xLabel3', 'xLabel4'
        dataset 'dataset1', 1.0, 2.0, 3.0, 4.0
        dataset 'dataset2', 6.0, 1.0, 2.0, 4.0

        option DiagramOption.builder
            .setClickAction(Rma2Controller.&editClaim as MethodClosure)
            // "diagramAction" was moved to here
            .showDataCount()
            // The numbers are hidden by default. Use this option if need to show
            .hideLegend()
            // The legends are shown by default. Use this option if need to hide
            .setResolution(DiagramOption.DiagramResolution.DEFAULT_4K)
            // Default resolution is [SVG: 960x480, PNG: 2160x1080].
            // Allow to set to 540p, 720p, 1080p, 2K, 1440p, 4K
            .setKeyColors(Color.RED, Color.BLUE)
            // Allow to self-define the colors for each dataset
            // (If not set, there are by default 7 colors)
            .setTitle("Test Chart") // Diagram title
            .build()
    }
}
Editor Asciidoc
//...
EditorOption.EditorOptionBuilder editor = EditorOption.getBuilder()
editor.addSpanRegexes(TaackBaseAsciidocSpans.spans)                 (1)
editor.addSpanRegexes(TaackAsciidocTable.spans)                     (1)
editor.addSpanRegexes(TaackAsciidocPlantUML.spans)                  (1)
editor.onDrop(this.&dropEditor as MC, cmsPage.id)                   (2)
fieldEditor cmsPage.bodyContent_, editor.build()                    (3)
innerFormAction this.&previewBody as MC                             (4)
//...

@Transactional
def dropEditor() {                                                  (2)
    CmsPage page = CmsPage.get(params.long('cmsPage'))
    params.remove('cmsPage')
    String past = params.get('onpaste')
    if (past) {                                                     (5)
        render taackAsciidocService.convertFromHtml(past)
    } else {                                                        (6)
        final List<MultipartFile> mfl = (request as MultipartHttpServletRequest).getFiles('filePath')
        final mf = mfl.first()

        if ([AttachmentContentType.SHEET_ODS.mimeType,
             AttachmentContentType.LO_TEXT.mimeType
            ].contains(mf.contentType)) {                           (7)
            render taackAsciidocService.convert(page, mf.inputStream)
        } else {                                                    (8)
            CmsImage cmsImage = taackSaveService.save(CmsImage)
            if (cmsImage && page) {
                cmsImage.cmsPage = page
            }
            render "image::${cmsImage.originalName}[]"
        }
    }
}
1 Aggiunge estensioni per gli span di sintassi (sostanzialmente un’espressione regolare e una classe per colorare il codice)
2 Callback quando viene rilasciato un file o viene incollato del contenuto da un’altra pagina o applicazione
3 Il campo nel modulo
4 Azione di anteprima opzionale
5 Incolla di contenuto HTML. Il contenuto verrà convertito in Asciidoc
6 Parte relativa al rilascio di file
7 Se il file è un documento ODS o Writer, verrà convertito in Asciidoc e le immagini verranno importate
8 Se il file è un’immagine, verrà salvata nel CMS.
taackAsciidocService utilizza il servizio TaackEditorService di taack-ui-asciidoc, vedere DSL dei moduli
TaackUiConfiguration
@CompileStatic
class TaackUiConfiguration {
    static String defaultTitle = 'Taack'
    static String logoFileName = 'logo-taack-web.svg'
    static int logoWidth = 70
    static int logoHeight = 60

    static boolean fixedTop = false
    static boolean hasMenuLogin = true
    static boolean outlineContainer = false
    static String bgColor = '#05294c'
    static String fgColor = '#eeeeee'
    static String bodyBgColor = '#fff'

    static String home = System.getProperty('user.home')
    static String root = home + '/intranetFiles'
    static String taack = home + '/taack'
    static String resources = taack + '/resources'
    static String javaPath = '/usr/bin/java'
    static String plantUmlPath = home + '/plantuml-1.2022.7.jar'
    static String solrUrl = 'http://localhost:8983/solr/taack'
    static Boolean disableSecurity = false
}
Application.groovy
@CompileStatic
class Application extends GrailsAutoConfiguration {
    static void main(String[] args) {
        TaackUiConfiguration.disableSecurity = true
        TaackUiConfiguration.hasMenuLogin = false
        GrailsApp.run(Application, args)
    }
}
Rilascio su una tabella (Drop on Table)
        TableOption tableOption = new TableOption.TableOptionBuilder()
                .onDropAction(this.&testDrop as MC).build()             (1)

        new UiTableSpecifier().ui tableOption, {
            header {
                sortableFieldHeader cb.position_
                sortableFieldHeader cb.subsidiary_
                label 'Page OR Menu Entry'
            }
1 Azione chiamata in caso di rilascio in un punto qualsiasi della tabella.
Rilascio sulle celle (Drop on Cells), la colonna della riga è un’area di rilascio
cellDropAction(CmsController.&testCellDrop as MC)                       (1)
rowColumn {
    rowField cp.title_
    rowField 'test drop'
}
1 Azione chiamata in caso di rilascio in un punto qualsiasi della cella.

Versione 0.6.1

Utilizzo delle azioni sul diagramma
new UiDiagramSpecifier().ui {
    bar {
        labels 'date1', 'date2', 'date3'
        dataset 'Stuff1', 3.0, 4.0, 5.0

        diagramAction this.&clickDiagram as MC, id, (1)
        [optionalParam: 'value']                    (2)
    }
}

def clickDiagram() {
    println(params)
    // [id: 123456, dataset: "Stuff1",
    // optionalParam: "value"                       (3)
    // x: "date1", y: "3.0"]
}
1 Azione sul diagramma
2 È possibile passare una mappa
3 I parametri dell’azione contengono label, value, dataset name e map
Utilizzo degli helper statici
import org.codehaus.groovy.runtime.MethodClosure as MC

import static taack.ui.TaackUi.createMenu           (1)

@Override
UiMenuSpecifier editorCreate() {
    createMenu {                                    (2)
        menu this.&createFromTemplate as MC
        menuIcon
            CollaboraIcons.WRITER,
            this.&createFromTemplate as MC,
            [collaboraApp: CollaboraApp.WRITER]
    }
}
1 Importazione statica
2 Chiamata statica createMenu, scorciatoia per new UiMenuSpecifier().ui; altre scorciatoie includono createModal, che restituisce UiBlockSpecifier

Versione 0.6

  • Animazione di caricamento

  • Zoom / scorrimento dei diagrammi

  • Tooltip dei diagrammi

  • Selezione multipla nelle tabelle (vedere doc/DSLs/filter-table-dsl.html)

  • Caricamento differito (lazy loading) delle schede

  • Miglioramento della paginazione

  • Ottimizzazione del disegno, evitando ridisegni non necessari

  • Evita il caso in cui i blocchi venivano disegnati due volte

  • Miglioramento del layout di ricerca

  • Supporto per BigDecimal nelle tabelle, mostrato secondo le impostazioni internazionali dell’utente

  • WiP: Semplice editor Asciidoc WYSIWYG

Versione 0.5.7

  • Pulizia del codice DSL show, depreca il passaggio del parametro oggetto a UiShowSpecifier().ui

  • Editor WYSIWYG Asciidoctor iniziale

    • Supporto per il trascinamento e rilascio (Drag and Drop) di immagini e file

  • Ulteriori DSL per i diagrammi

    • timeSeries

    • areaChart

    • bubbleChart

  • Sicurezza

    • Sanificazione predefinita delle informazioni visualizzate (usare fieldRaw per evitare la sanificazione)

    • Controllo degli accessi su tutti i punti di ingresso

  • Correzioni di bug e aggiornamento delle dipendenze

Sostituire field <html code> con fieldRaw <html code>

Versione 0.5.6 (corregge bug della 0.5.5)

screenshot news sorting
Figure 7. Le intestazioni di colonna mostrano le direzioni di ordinamento
Come mantenere il contesto quando si clicca su una tabella
def showPart(PlmFreeCadPart part, Long partVersion, Boolean isHistory) {(1)
    taackUiService.show(
            plmFreeCadUiService.buildFreeCadPartBlockShow(
                    part, partVersion, false, isHistory),               (2)
            buildMenu(),
            "isHistory")                                                (3)
}
1 isHistory è un parametro dell’azione
2 isHistory viene utilizzato durante il disegno del blocco; dobbiamo ritrasmetterlo per disegnare esattamente lo stesso layout del blocco, mantenendo il contesto
3 La chiave isHistory viene passata come ultimo argomento di taackUiService.show. È possibile specificare più chiavi da mantenere.

Versione 0.5.4

Versione 0.5.3

  • Correzione delle caselle di controllo (checkbox) nei moduli

  • Consente gli alias in TQL per le colonne formula

  • Pulizia del codice e incremento delle versioni delle dipendenze

Versione 0.5.2

  • Il client JDBC è ora anche un’estensione AsciidoctorJ

  • Aggiunti getter ai campi di dominio accessibili tramite JDBC

  • Aggiunto il DSL TQL e TDL (Taack Display Language) per descrivere come visualizzare i dati interrogati (come tabella o grafico a barre)

  • Ripristinata l’etichettatura manuale sui menu

  • Ulteriori miglioramenti al DSL dei diagrammi (grazie a Chong e ZhenQing)

  • Migliore personalizzazione

TQL e TDL (Taack Display Language)
select
    u.rawImg,
    u.username,
    u.manager.username
from User u
where u.dateCreated > '2024-01-01' and u.manager.username = 'admin';
--
table rawImg as "Pic", username as "Name", manager as "Manager"
news table
Figure 8. Risultati

Versione 0.5.1

Sostituzione di TaackPlugin
@PostConstruct
void init() {
    TaackUiEnablerService.securityClosure(
        this.&securityClosure,
        CrewController.&editUser as MC,
        CrewController.&saveUser as MC)
    TaackAppRegisterService.register(
        new TaackApp(
            CrewController.&index as MC,                    (1)
            new String(
                this.class
                    .getResourceAsStream("/crew/crew.svg")  (2)
                    .readAllBytes()
            )
        )
    )
}
1 Punto di ingresso
2 Icona
Sostituzione di Charts: Diagrammi
private static UiDiagramSpecifier d1() {
    new UiDiagramSpecifier().ui {
        bar(["T1", "T2", "T3", "T4"] as List<String>, false, {
            dataset 'Truc1', [1.0, 2.0, 1.0, 4.0]
            dataset 'Truc2', [2.0, 0.1, 1.0, 0.0]
            dataset 'Truc3', [2.0, 0.1, 1.0, 1.0]
        }, DiagramTypeSpec.HeightWidthRadio.ONE)
    }
}
PDF contenente diagrammi
printableBody {
    diagram(d1(), BlockSpec.Width.HALF)
    diagram(d2(), BlockSpec.Width.HALF)
}
news diagram
Figure 9. Diagramma a barre impilate

Versione 0.5.0

Basato su Bootstrap

  • Supporto per i temi

    • Schema di colori dal tuo desktop

    • Dimensione dei componenti regolabile

  • Più facile da estendere

Tempo di caricamento migliorato

  • Meno risorse (asset)

  • CSS e JS ridotti

Principali modifiche al codice

Nuovo DSL di layout

  • Stesso layout per forms e blocks

    • col e row

    • tabs e tab

With solo per col

Menu DSL per i blocchi

  • Il DSL di menu sostituisce le azioni

  • Non è più necessario usare icone

  • I menu vengono tradotti automaticamente

Codice più semplice

Porta a:

  • Manutenzione più semplice

  • Maggiore facilità nel contribuire

Versione 0.4.2

In arrivo…​ questa versione dovrebbe portare alcune interessanti novità (rompendo talvolta del vecchio codice)

  • Migliorare la gerarchia DSL

    • campi nascosti solo in alto per migliorare la leggibilità

    • nessun passaggio ridondante di parametri nel modulo

    • nessun passaggio ridondante di parametri nel filtro

    • filterField esclusivamente sotto section

    • campo di primo livello del modulo solo nell’intestazione

  • hook per i campi del modulo per visualizzare correttamente le relazioni M2M

  • hook per registrare un filtro oggetto tipico

  • Migliorare il ripristino dello stato

  • Correggere il raggruppamento delle tabelle / alberi con paginazione

  • Da definire (TBD)

Versione 0.4.1

screenshot news menu 0.4.1
Figure 10. Layout dei menu aggiornato
Codice del layout dei menu
private UiMenuSpecifier buildMenu(String q = null) {
    new UiMenuSpecifier().ui {
        menu CrewController.&index as MC
        menu CrewController.&listRoles as MC
        menu CrewController.&hierarchy as MC
        menuIcon ActionIcon.CONFIG_USER, this.&editUser as MC
        menuIcon ActionIcon.EXPORT_PDF, this.&downloadBinPdf as MC
        menuSearch this.&search as MethodClosure, q
        menuOptions(SupportedLanguage.fromContext())            (1)
    }
}
1 La scelta della lingua si trova a destra della barra di ricerca ed è possibile aggiungere altre enumerazioni
Guida al debug di Kotlin JS
$ cd infra/browser/client                             (1)
$ ./gradlew browserDevelopmentRun                     (2)
$ vi intranet/server/grails-app/conf/application.yml  (3)
# Uncomment line bellow
# client.js.path: 'http://localhost:8080/client.js'

# Then your browser should show Kotlin code !
1 Spostarsi nella cartella client dove viene generato il codice JS
2 Avviare un server che espone client.js e client.js.map …​
3 Modificare il file application.yml della propria intranet
Nuova semplificazione del DSL Solr (non è più necessaria l’etichettatura)
@PostConstruct
private void init() {
    taackSearchService.registerSolrSpecifier(this,
            new SolrSpecifier(User,
                CrewController.&showUserFromSearch as MethodClosure,
                this.&labeling as MethodClosure, { User u ->
        u ?= new User()
        indexField SolrFieldType.TXT_NO_ACCENT, u.username_
        indexField SolrFieldType.TXT_GENERAL, u.username_
        indexField SolrFieldType.TXT_NO_ACCENT, u.firstName_
        indexField SolrFieldType.TXT_NO_ACCENT, u.lastName_
        indexField SolrFieldType.POINT_STRING, "mainSubsidiary", true, u.subsidiary?.toString()
        indexField SolrFieldType.POINT_STRING, "businessUnit", true, u.businessUnit?.toString()
        indexField SolrFieldType.DATE, 0.5f, true, u.dateCreated_
        indexField SolrFieldType.POINT_STRING, "userCreated", 0.5f, true, u.userCreated?.username
    }))
}

Versione 0.4.0

  • Niente più paginate nelle tabelle. Vedere Nuovo utilizzo di iterate

  • Niente list, ma un iterate che accetta una closure come parametro, con un approccio basato sul pattern builder per passare gli argomenti

  • I menu vengono ora etichettati automaticamente (usare lang=test nell’URL per tradurre i menu). Vedere Nuovo codice per menu

  • Niente più parametro isAjax nelle tabelle …​ Vedere Nuovo codice per rowAction

  • Sostituzione di rowLink con rowAction [i18n_isAjax]

  • Nessuna etichetta necessaria su rowAction nelle tabelle. Vedere Nuovo codice per rowAction

  • Non è più richiesto ajaxBlock per tabelle, moduli e tableFilters

  • formAction non ha più il parametro isAjax

  • formAction non ha più il parametro i18n obbligatorio

  • form non ha più il parametro i18n obbligatorio, l’i18n si basa sul nome dell’azione corrente

  • L’azione di blocco non ha più il parametro i18n obbligatorio, l’i18n si basa sull’azione di destinazione

  • L’azione di blocco non ha più il parametro isAjax obbligatorio

Nuovo utilizzo di iterate
iterate(taackFilterService.getBuilder(Role)                     (1)
        .setMaxNumberOfLine(20)                                 (2)
        .setSortOrder(TaackFilter.Order.DESC, u.authority_)     (3)
        .build()) { Role r, Long counter ->
            row {
                rowColumn {
                    rowField r.authority
                    if (hasSelect)
                        rowAction
                            ActionIcon.SELECT * IconStyle.SCALE_DOWN,
                            CrewController.&selectRole as MC
                            r.id                                (4)
                }
            }
        }
1 iterare
2 Specificare max è sufficiente per attivare la paginazione se ci sono più righe
3 Sostituisce il vecchio pattern inefficiente per descrivere l’ordinamento e la direzione iniziali
4 Niente più parametri i18n e isAjax
Nuovo codice per menu
private UiMenuSpecifier buildMenu(String q = null) {
    UiMenuSpecifier m = new UiMenuSpecifier()
    m.ui {
        menu CrewController.&index as MC        (1)
        menu CrewController.&listRoles as MC
        menu CrewController.&hierarchy as MC
        menuSearch this.&search as MethodClosure, q
    }
    m
}
1 Nessun parametro i18n
Nuovo codice per rowAction
if (hasActions) {
    rowColumn {
        rowAction ActionIcon.EDIT * IconStyle.SCALE_DOWN, this.&roleForm as MC, r.id (1)
    }
}
1 Nessun parametro i18n, nessun ultimo parametro isAjax

Versione 0.3.9

Nessun aggiornamento da troppo tempo, il letargo sta giungendo al termine. Questa versione offre:

  • Grails 6.2.0

  • Groovy 3.0.21

  • Aggiornamento di varie dipendenze …​ (vedere Changelog)