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″]