Commit 50b3c817 authored by Laurent Lecluse's avatar Laurent Lecluse
Browse files

Nouveau composant UTableAjax permettant de créer un tableau avec chargement...

Nouveau composant UTableAjax permettant de créer un tableau avec chargement des données en Ajax et affichage avec Vue, avec son côté serveur.
parent d5d01112
Loading
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
CHANGELOG
=========

6.2.1 (29/03/2024)
------------------

- Nouveau composant UTableAjax permettant de créer un tableau avec chargement des données en Ajax et affichage avec Vue, avec son côté serveur.
- Suppression de la fonction de formatage de date

6.2.0 (26/03/2024)
------------------

+199 −0
Original line number Diff line number Diff line
<template>
    <div class="dt-bootstrap5">
        <b-row>
            <b-col>
                Afficher <label>
                <select v-model="dSize" class="form-select form-select-sm">
                    <option v-for="ps in pageSizes" :key="ps" :value="ps">{{ ps }}</option>
                </select>
            </label> éléments
            </b-col>
            <b-col>
                <div class="float-end">
                    Rechercher : <label><input v-model="dSearch"
                                               class="form-control form-inline form-control-sm"/></label>
                </div>
            </b-col>
        </b-row>
        <table class="table table-bordered dataTable mb-2" ref="tableRef">
            <slot></slot>
        </table>
        <b-row>
            <b-col>Affichage de l'élément {{ elStart }} à {{ elEnd }} sur {{ dCount }} éléments</b-col>
            <b-col>
                <div class="dataTables_paginate paging_simple_numbers">
                    <b-pagination align="end" :page="page" v-model="page" :total-rows="dCount" :per-page="cSize"
                                  last-number="true"
                                  firstNumber="true"
                                  prev-text="Précédent"
                                  next-text="Suivant"
                    />
                </div>
            </b-col>
        </b-row>
    </div>
</template>
<script>

export default {
    name: "UTableAjax",
    props: {
        id: {required: false, type: String},
        size: {required: false, default: 10},
        count: {required: false},
        search: {required: false},
        dataUrl: {required: true, type: String},
    },
    data()
    {
        return {
            page: 1,
            pageSizes: [10, 25, 50, 100, 'Tous'],
            defaultSize: 10,
            dSize: this.size,
            dCount: this.count,
            dSearch: this.search,
            searchTimer: null,
            columns: {},
            loading: false,
            orderCol: undefined,
            orderDir: 'asc',
        };
    },
    computed: {
        cSize()
        {
            if (isNaN(this.dSize)) {
                return 9999999999999;
            }
            return this.dSize;
        },
        storageIdentifier()
        {
            return 'UTableAjax-' + this.id + '-' + window.location.href;
        },
        elStart()
        {
            if (isNaN(this.dSize)) {
                return 1;
            }
            return ((this.page - 1) * this.dSize) + 1;
        },
        elEnd()
        {
            if (isNaN(this.dCount)) {
                return (this.page - 1) * this.dSize + this.dSize;
            }
            if (isNaN(this.dSize)) {
                return this.dCount;
            }
            return Math.min(this.dCount, (this.page - 1) * this.dSize + this.dSize);
        },
    },
    watch: {
        dSize(newSize)
        {
            localStorage.setItem(this.storageIdentifier, newSize);
            this.getData();
        },
        dSearch(newSearch)
        {
            const that = this;
            clearTimeout(this.searchTimer);
            this.searchTimer = setTimeout(() => {
                if (this.page > 1) {
                    this.page = 1;
                }else {
                    that.getData();
                }
            }, 500);
        },
        page(newPage)
        {
            this.getData();
        },
    },
    methods: {
        getData()
        {
            unicaenVue.axios.post(this.dataUrl, {
                page: this.page,
                size: this.dSize,
                elStart: this.elStart,
                elEnd: this.elEnd,
                search: this.dSearch,
                orderCol: this.orderCol,
                orderDir: this.orderDir,
            }).then(response => {
                let data = response.data;
                this.dCount = data.count;
                this.$emit('data', data.data);
            });
        },
        orderBy(column)
        {
            const element = this.columns[column];
            for(let col in this.columns){
                if (col != column){
                    if (this.columns[col].classList.contains('sorting_asc')){
                        this.columns[col].classList.remove('sorting_asc');
                    }
                    if (this.columns[col].classList.contains('sorting_desc')){
                        this.columns[col].classList.remove('sorting_desc');
                    }
                    console.log(col)
                }
            }
            let newOrder = 'asc';

            if (element.classList.contains('sorting_asc')){
                newOrder = 'desc';
                element.classList.remove('sorting_asc');
                element.classList.add('sorting_desc');
            }else if (element.classList.contains('sorting_desc')){
                element.classList.remove('sorting_desc');
                element.classList.add('sorting_asc');
            }else{
                element.classList.add('sorting_asc');
            }

            this.orderCol = column;
            this.orderDir = newOrder;

            this.getData();
        },
    },
    mounted()
    {
        this.dSize = parseInt(localStorage.getItem(this.storageIdentifier)) || this.defaultSize;
        this.page = 1;
        this.dSize = this.size;
        this.dCount = this.count;
        const headEl = this.$refs.tableRef;
        const that = this;
        if (headEl) {
            // Récupération des éléments <th> qui ont l'attribut 'column'
            const thElements = headEl.querySelectorAll('th[column]');
            thElements.forEach(th => {
                Array.from(th.attributes).forEach(attr => {
                    if (attr.name == 'column') {
                        this.columns[attr.value] = th;
                        th.dataset.column = attr.value;
                        th.onclick = function () {
                            that.orderBy(this.dataset.column);
                        }
                        th.removeAttribute(attr.name);
                        th.classList.add('sorting');
                    }
                });
            });
        }

        this.getData();
    },
}
</script>

<style scoped>

</style>
 No newline at end of file

doc/composants.md

0 → 100644
+4 −0
Original line number Diff line number Diff line
# Composants proposés en standard

- [UCalendar]() Calendrier pour la saisie d'informations par mois (à documenter)
- [UTableAjax](composants/UTableAjax.md) Permet d'afficher des tableaux de données chargés en Ajax et formatés avec Vue.js
+103 −0
Original line number Diff line number Diff line
# UTableAjax

Permet de dessiner un pseudo DataTable avec un chargement des données en Ajax et un affichage en VueJS.

Exemple d'utilisation côté client, dans un composant Vue :

```vue
<template>
    <u-table-ajax :data-url="this.dataUrl" @data="maj">
        <thead>
        <tr>
            <th column="ID">Id</th><!-- l'attribut column doit être renseigné pour pouvoir la rendre triable -->
            <th column="LIBELLE">Libellé</th>
            <th column="FORMULE">Formule</th>
            <th column="ANNEE">Année</th>
            <th>&nbsp;</th>
        </tr>
        </thead>
        <tbody>
        <!-- On liste toute les lignes et on les affiche ici -->
        <tr v-for="(line,i) in lines" :key="i">
            <td>{{ line['ID'] }}</td>
            <td>{{ line['LIBELLE'] }}</td>
            <td>{{ line['FORMULE'] }}</td>
            <td>{{ line['ANNEE'] }}</td>
            <td><a :href="editUrl(line['ID'])">Modifier</a></td>
        </tr>
        </tbody>
    </u-table-ajax>
</template>

<script>

export default {
    name: 'Test',
    data()
    {
        return {
            dataUrl: unicaenVue.url('.../data'),
            
            lines: [],
        };
    },
    methods: {
        maj(lines)
        {
            this.lines = lines;
        },
        editUrl(id)
        {
            return unicaenVue.url('mon-url/:id', {id: id});
        },
    },
}

</script>
<style scoped>

</style>
```

Exemple de génération de données côté serveur, dans un contrôleur :
```php
<?php

namespace MonModule\Controller;

use Laminas\Mvc\Controller\AbstractActionController;
use UnicaenVue\Util;

class TestController extends AbstractActionController
{

    public function dataAction()
    {
        // Requête SQL
        // Elle doit avoir un paramètre :search pour traiter les recherches
        // Elle ne doit pas avoir d'OrderBY : il sera ajouté ensuite selon le besoin
        $sql = "
        SELECT 
          fti.id      id, 
          fti.libelle libelle,
          f.libelle   formule,
          a.libelle   annee
        FROM 
          formule_test_intervenant fti
          JOIN formule f ON f.id = fti.formule_id
          JOIN annee a ON a.id = fti.annee_id
        WHERE
          lower(fti.libelle || ' ' || f.libelle || ' ' || a.libelle) like :search
        ";

        // renvoie un axiosModel avec les données issues du requêtage
        $em = /* Récupération de l'entityManager de Doctrine */;
        
        return Util::tableAjaxData($em, $this->axios()->fromPOst(), $sql);
    }
}
```

Résultat :

![UTableAjax](UTableAjax.png)
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@

[Partie serveur PHP](serveur.md)

[Composants proposés en standard](composants.md)

Pour faire vos premiers pas avec Vue :

- [Doc d'introduction à VueJS de Stéphane](https://git.unicaen.fr/bouvry/presentation-dev/-/blob/master/src/vuejs.md)
Loading