Praktické príklady s Vue – 1/x
Keď už sme v predchádzajúcom článku s tabuľkami začali, lepšie povedané v druhej časti skončili príkladom so znovu použiteľným komponentom, ktorý vykresloval dve nezávislé tabuľky s rôznymi údajmi, tak prvý z praktických príkladov bude nečakane …
Tabuľka
Ale tento krát je “fulltextovo” filtrovateľná a zoraditeľná podľa stĺpca – stačí kliknúť na názov stĺpca. A ako bonus prebehne zoradenie animovane…
Celý príklad je zámerne napísaný takto:
- Definícia šablóny tento krát nie je súčasťou JavaScriptu, ale vyňatá do TEMPLATE značky. Po prvé preto, aby ste videli, že aj tak to ide, a po druhé preto, aby viniklo, že JavaScript pre túto tabuľku vôbec nie je dlhý, ani komplikovaný.
- Animácie sú použité dvoma spôsobmi. Aj pomocou príslušných CSS tried, aj pomocou externej Velocity knižnice.
- Na zoraďovanie údajov a filtrovanie riadkov na základe vyhľadávanie je zasa zámerne využitá Underscore knižnica. Netreba nanovo vymýšľať koleso a skúsiť to písať po svojom. Za tých 5 či 6 kilobajtov navyše nestojí za to lámať si hlavu vlastným algoritmom.
- No a tabuľka je tabuľkou urobená pomocou CSS, značka TABLE nebola použitá. To kvôli tomu, aby šli presúvať celé riadky tabuľky – TR značky. Avšak CSS berte “as is”. Žiadny SASS, LESS, ani len BEM, o CSS tu naprosto nejde, tak som ho flákol.
Príklad je ako vždy plne funkčný, ale stále platí, že ak by predsa len nefungoval a nezobrazoval zoznam užívateľov, je to problém s certifikátom mockaroo.com. Kliknite na nasledovné url zdroja údajov ( https://api.mockaroo.com/api/b5f38710?count=8&key=cdbbbcd0 ) – otvorí sa v novom okne – a potvrďte bezpečnostnú výnimku. Že to funguje sa prejaví tak, že vám z tej url stiahne json súbor. Ten len zmažte, zatvorte kartu a znovu spustite fiddle prepnutím sa z karty Result na napríklad na kartu HTML a potom späť na Result.
<style> .data-grid { width: 98%; margin: 10px auto; padding: 2px; background-color: white; border: 2px solid #3F51B5; overflow: hidden; } .table { display: table; width: 100%; font-size: 13px; font-family: Arial, sans-serif; color: #263238; cursor: pointer; } .thead { display: table-header-group; } .tbody { display: table-row-group; } .tr { display: table-row; } .td { display: table-cell; position: relative; } .tr .td:not(:last-child) { border-right: 2px solid white; } .thead .td { padding: 5px 14px; background-color: #3F51B5; color: white; font-weight: bold; text-align: center; } .tbody .td { padding: 4px; color: #263238; text-align: center; } .tbody .tr:hover .td { background-color: #C5CAE9; } .tbody .tr:hover .td:not(:last-child) { border-right: 2px solid #C5CAE9; } .tools { margin: 10px; box-sizing: border-box; } .tools:after { content: ""; display: block; clear: both; } .search { float: right; } .arrow { display: inline-block; position: absolute; width: 0; height: 0; margin-left: 5px; margin-top: 5px; border-left: 6px solid transparent; border-right: 6px solid transparent; transition: all .6s; } .asc { border-bottom: 6px solid white; } .desc { border-top: 6px solid white; } .users-move { transition: transform .6s; } .users-enter-active, .users-leave-active { transition: all .6s; } .users-enter, .users-leave-to { opacity: 0; transform: translateY(30px); } </style> <div id="app"> <data-grid url="https://api.mockaroo.com/api/b5f38710?count=8&key=cdbbbcd0" :columns="{id: 'ID', nick: 'Nick name', first: 'First name', last: 'Last name'}" ></data-grid> <data-grid url="https://api.mockaroo.com/api/03f2c610?count=8&key=cdbbbcd0" :columns="{id: 'ID', first: 'First name', last: 'Last name', email: 'Email'}" ></data-grid> </div> <template id="data-grid"> <transition appear v-on:before-appear="onCreate" v-on:appear="onData" > <div class="data-grid"> <div class="tools"> <div class="search"> <input type="text" @input="updateQuery" :value="query" placehorder="search..." > <button class="clear" @click="clearQuery">clear</button> </div> </div> <div class="table"> <div class="thead" @click="sortUsers"> <div class="tr"> <span v-for="(col, key) in columns" class="td" :data-key=key> {{ col }} <span v-if="prevKey === key" :class="['arrow', orderDesc ? 'desc' : 'asc']"> </span> </span> </div> </div> <transition-group name="users" tag="div" class="tbody"> <div class="tr" v-for="row in filteredUsers" :key="row.id"> <span class="td" v-for="column in row">{{ column }}</span> </div> </transition-group> </div> </div> </transition> </template> <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script> <script src="https://unpkg.com/underscore@1.8.3/underscore-min.js"></script> <script src="https://unpkg.com/vue@2.4.4/dist/vue.min.js"></script> <script src="https://unpkg.com/axios@0.16.2/dist/axios.min.js"></script> <script> Vue.component('data-grid', { template: '#data-grid', props: ['url', 'columns'], data () { return { users: [], query: '', prevKey: 'id', orderDesc: false } }, computed: { filteredUsers () { return _.filter(this.users, user => _.find(user, prop => new RegExp(this.query, 'i').test(prop) ) ) } }, methods: { sortUsers (evt) { var key = evt.target.dataset.key if (this.prevKey === key) { this.users.reverse() this.orderDesc = !this.orderDesc } else { this.users = _.sortBy(this.users, key) this.orderDesc = false this.prevKey = key } }, updateQuery: _.debounce(function (evt) { this.query = evt.target.value }, 300), clearQuery () { this.query = '' }, onCreate (elm) { elm.style.opacity = 0 }, async onData (elm) { this.users = await axios .get(this.url) .then(res => res.data) Velocity(elm, "fadeIn", {duration: 600}) } } }) new Vue({ el: '#app' }) </script>
[iframe src=”https://jsfiddle.net/provuecateur/xbre1er8/embedded/result,html,css,js/” width=”100%” height=”600″]