├── .babelrc ├── .editorconfig ├── .eslintignore ├── .gitignore ├── .npmignore ├── .postcssrc.js ├── LICENSE ├── README.md ├── _config.yml ├── build ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.build.config.js ├── webpack.dev.conf.js ├── webpack.prod.conf.js └── webpack.test.conf.js ├── changes.md ├── config ├── dev.env.js ├── index.js ├── prod.env.js └── test.env.js ├── dist ├── stats.json ├── vuetable-2-full.js ├── vuetable-2.css └── vuetable-2.js ├── docs ├── .nojekyll ├── API-vs-Data.md ├── Background.md ├── CSS-Styling.md ├── Callbacks.md ├── Data-Format-JSON.md ├── Data-Transformation.md ├── Detail-Row.md ├── FAQ.md ├── Fields-Definition.md ├── Filtering.md ├── Fixed-Header.md ├── Home.md ├── Overriding-Default-Query-String.md ├── Pagination.md ├── README.md ├── Row-Identifier.md ├── Sorting.md ├── Special-Fields.md ├── Vuetable-Computed.md ├── Vuetable-Data.md ├── Vuetable-Events.md ├── Vuetable-Methods.md ├── Vuetable-Properties.md ├── VuetablePagination.md ├── VuetablePaginationDropdown.md ├── VuetablePaginationInfo.md ├── VuetablePaginationInfoMixin.md ├── VuetablePaginationMixin.md ├── _sidebar.md └── index.html ├── index.html ├── package-lock.json ├── package.json ├── src ├── assets │ └── logo.png ├── components │ ├── Vuetable.vue │ ├── VuetablePagination.vue │ ├── VuetablePaginationDropdown.vue │ ├── VuetablePaginationInfo.vue │ ├── VuetablePaginationInfoMixin.vue │ └── VuetablePaginationMixin.vue ├── index.js └── main.js ├── static └── .gitkeep ├── stats.json ├── test ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ └── Vuetable.spec.js ├── upgrade-guide.md └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false, 8 | "env": { 9 | "test": { 10 | "presets": ["env", "stage-2"], 11 | "plugins": [ "istanbul" ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/static 4 | dist/index.html 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | selenium-debug.log 9 | test/unit/coverage 10 | test/e2e/reports 11 | .idea/ 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserlist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Rati Wannapanop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/vuetable-2.svg)](https://www.npmjs.com/package/vuetable-2) 2 | [![npm](https://img.shields.io/npm/dt/vuetable-2.svg)](https://www.npmjs.com/package/vuetable-2) 3 | [![npm](https://img.shields.io/npm/l/vuetable-2.svg?maxAge=2592000)](https://github.com/ratiw/vuetable-2/blob/master/LICENSE) 4 | 5 | # Vuetable-2 - data table simplify! 6 | 7 | --- 8 | 9 | ## Vuetable-2 v2.0-beta is available now! 10 | 11 | See the [`next`](https://github.com/ratiw/vuetable-2/tree/next) branch. 12 | 13 | --- 14 | 15 | ### Vuetable-2 works with Vue 2.x, vuetable is for Vue 1.x 16 | 17 | If you're looking for the version that's working with Vue 1.x, please go to [`vuetable`](https://github.com/ratiw/vue-table) repo. 18 | 19 | --- 20 | 21 | ### Documentation and Tutorial 22 | 23 | Documentation is still under development, but you can view it at [https://ratiw.github.io/vuetable-2](https://ratiw.github.io/vuetable-2). Thanks to @cristijora for the help. 24 | 25 | Meanwhile, check out 26 | - the [Tutorial](https://github.com/ratiw/vuetable-2-tutorial/wiki) 27 | with follow-along project [here](https://github.com/ratiw/vuetable-2-tutorial). It should be enough to get you started. 28 | 29 | - [Sample project](https://github.com/ratiw/vuetable-2-with-laravel-5.4) using Vuetable-2 with Laravel 5.4 and Laravel-Mix 30 | 31 | If you've been using Vuetable for Vue 1.x before, checkout [what's changed](https://github.com/ratiw/vuetable-2/blob/master/changes.md) for info on changes from Vuetable for Vue 1.x and the [upgrade guide](https://github.com/ratiw/vuetable-2/blob/master/upgrade-guide.md) on how you could upgrade from Vuetable for Vue 1.x. 32 | 33 | You can now make use of Vue's scoped slot using the new `__slot` special field, thanks to @sjmarve. That means you are able to define action buttons per instance of a data table without depending on a globally defined component. 34 | 35 | Use scoped slot in parent when defining the actions [Vue Doc for scopped Slots](https://vuejs.org/v2/guide/components.html#Scoped-Slots) 36 | 37 | e.g. 38 | ```html 39 | 45 | ``` 46 | 47 | the onClick function can now be defined in the parent and the parent has Access to rowData and rowIndex via props. :) 48 | 49 | The original functionality still works 50 | 51 | # Breaking Changes 52 | ## v1.6.0 53 | - The `icons` prop of VuetablePagination is now moved into the `css` prop object. See this [codepen](https://codepen.io/ratiw/pen/GmJayw). 54 | 55 | # Example Code 56 | - Clone the project 57 | - Go into the cloned directory 58 | - `npm install` 59 | - `npm run dev` 60 | - Open browser to `http://localhost:8080` 61 | 62 | # Usage 63 | ## NPM 64 | 65 | ```shell 66 | npm install vuetable-2 --save-dev 67 | ``` 68 | 69 | ## Javascript via CDN 70 | Thanks to @cristijora for providing helps on this. 71 | ```html 72 | // vuetable-2 dependencies 73 | 74 | 75 | // vuetable-2 76 | 77 | Vue.use(Vuetable) 78 | ``` 79 | This is demonstrated in this [jsfiddle](http://jsfiddle.net/CristiJ/z11fe07p/1318/). 80 | 81 | The `.use` from above will register all the components globally. 82 | ```javascript 83 | function install(Vue){ 84 | Vue.component("vuetable", Vuetable); 85 | Vue.component("vuetable-pagination", VueTablePagination); 86 | Vue.component("vuetable-pagination-dropdown", VueTablePaginationDropDown); 87 | Vue.component("vuetable-pagination-info", VueTablePaginationInfo); 88 | } 89 | ``` 90 | 91 | Also you have the ability to access certain components if you need them: 92 | ```javascript 93 | VueTable: VueTable.default/VueTable.VueTable, 94 | VueTablePagination: VueTable.VueTablePagination, 95 | VueTablePaginationInfo: VueTable.VueTablePaginationInfo, 96 | VueTablePaginationDropdown: VueTable.VueTablePaginationDropdown 97 | ``` 98 | 99 | 100 | # Contributions 101 | Any contribution to the code (via pull request would be nice) or any part of the documentation and any idea and/or suggestion are very welcome. 102 | 103 | > __Note__ 104 | > For any bug fix, the PR should be forked from the `master` branch. And for any suggestion or additional feature, the PR should be forked from the `develop` branch, where it can be integrated and rolled out in the next release. 105 | > 106 | > If you are not sure, please ask by openning a new issue. 107 | 108 | However, please do not feel bad if your pull requests or contributions do not get merged or implemented into Vuetable. 109 | 110 | Your contributions can, not only help make Vuetable better, but also push it away from what I intend to use it for. I just hope that you find it useful for your use or learn something useful from its source code. But remember, you can always fork it to make it work the way you want. 111 | 112 | # License 113 | Vuetable is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 114 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | var ora = require('ora') 6 | var rm = require('rimraf') 7 | var path = require('path') 8 | var chalk = require('chalk') 9 | var webpack = require('webpack') 10 | var config = require('../config') 11 | var webpackConfig = require('./webpack.prod.conf') 12 | 13 | var spinner = ora('building for production...') 14 | spinner.start() 15 | 16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 17 | if (err) throw err 18 | webpack(webpackConfig, function (err, stats) { 19 | spinner.stop() 20 | if (err) throw err 21 | process.stdout.write(stats.toString({ 22 | colors: true, 23 | modules: false, 24 | children: false, 25 | chunks: false, 26 | chunkModules: false 27 | }) + '\n\n') 28 | 29 | console.log(chalk.cyan(' Build complete.\n')) 30 | console.log(chalk.yellow( 31 | ' Tip: built files are meant to be served over an HTTP server.\n' + 32 | ' Opening index.html over file:// won\'t work.\n' 33 | )) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk') 2 | var semver = require('semver') 3 | var packageConfig = require('../package.json') 4 | var shell = require('shelljs') 5 | function exec (cmd) { 6 | return require('child_process').execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | ] 16 | 17 | if (shell.which('npm')) { 18 | versionRequirements.push({ 19 | name: 'npm', 20 | currentVersion: exec('npm --version'), 21 | versionRequirement: packageConfig.engines.npm 22 | }) 23 | } 24 | 25 | module.exports = function () { 26 | var warnings = [] 27 | for (var i = 0; i < versionRequirements.length; i++) { 28 | var mod = versionRequirements[i] 29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 30 | warnings.push(mod.name + ': ' + 31 | chalk.red(mod.currentVersion) + ' should be ' + 32 | chalk.green(mod.versionRequirement) 33 | ) 34 | } 35 | } 36 | 37 | if (warnings.length) { 38 | console.log('') 39 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 40 | console.log() 41 | for (var i = 0; i < warnings.length; i++) { 42 | var warning = warnings[i] 43 | console.log(' ' + warning) 44 | } 45 | console.log() 46 | process.exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | var config = require('../config') 4 | if (!process.env.NODE_ENV) { 5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 6 | } 7 | 8 | var opn = require('opn') 9 | var path = require('path') 10 | var express = require('express') 11 | var webpack = require('webpack') 12 | var proxyMiddleware = require('http-proxy-middleware') 13 | var webpackConfig = process.env.NODE_ENV === 'testing' 14 | ? require('./webpack.prod.conf') 15 | : require('./webpack.dev.conf') 16 | 17 | // default port where dev server listens for incoming traffic 18 | var port = process.env.PORT || config.dev.port 19 | // automatically open browser, if not set will be false 20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser 21 | // Define HTTP proxies to your custom API backend 22 | // https://github.com/chimurai/http-proxy-middleware 23 | var proxyTable = config.dev.proxyTable 24 | 25 | var app = express() 26 | var compiler = webpack(webpackConfig) 27 | 28 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 29 | publicPath: webpackConfig.output.publicPath, 30 | quiet: true 31 | }) 32 | 33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, { 34 | log: () => {} 35 | }) 36 | // force page reload when html-webpack-plugin template changes 37 | compiler.plugin('compilation', function (compilation) { 38 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 39 | hotMiddleware.publish({ action: 'reload' }) 40 | cb() 41 | }) 42 | }) 43 | 44 | // proxy api requests 45 | Object.keys(proxyTable).forEach(function (context) { 46 | var options = proxyTable[context] 47 | if (typeof options === 'string') { 48 | options = { target: options } 49 | } 50 | app.use(proxyMiddleware(options.filter || context, options)) 51 | }) 52 | 53 | // handle fallback for HTML5 history API 54 | app.use(require('connect-history-api-fallback')()) 55 | 56 | // serve webpack bundle output 57 | app.use(devMiddleware) 58 | 59 | // enable hot-reload and state-preserving 60 | // compilation error display 61 | app.use(hotMiddleware) 62 | 63 | // serve pure static assets 64 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 65 | app.use(staticPath, express.static('./static')) 66 | 67 | var uri = 'http://localhost:' + port 68 | 69 | var _resolve 70 | var readyPromise = new Promise(resolve => { 71 | _resolve = resolve 72 | }) 73 | 74 | console.log('> Starting dev server...') 75 | devMiddleware.waitUntilValid(() => { 76 | console.log('> Listening at ' + uri + '\n') 77 | // when env is testing, don't need open it 78 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 79 | opn(uri) 80 | } 81 | _resolve() 82 | }) 83 | 84 | var server = app.listen(port) 85 | 86 | module.exports = { 87 | ready: readyPromise, 88 | close: () => { 89 | server.close() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | 15 | var cssLoader = { 16 | loader: 'css-loader', 17 | options: { 18 | minimize: process.env.NODE_ENV === 'production', 19 | sourceMap: options.sourceMap 20 | } 21 | } 22 | 23 | // generate loader string to be used with extract text plugin 24 | function generateLoaders (loader, loaderOptions) { 25 | var loaders = [cssLoader] 26 | if (loader) { 27 | loaders.push({ 28 | loader: loader + '-loader', 29 | options: Object.assign({}, loaderOptions, { 30 | sourceMap: options.sourceMap 31 | }) 32 | }) 33 | } 34 | 35 | // Extract CSS when that option is specified 36 | // (which is the case during production build) 37 | if (options.extract) { 38 | return ExtractTextPlugin.extract({ 39 | use: loaders, 40 | fallback: 'vue-style-loader' 41 | }) 42 | } else { 43 | return ['vue-style-loader'].concat(loaders) 44 | } 45 | } 46 | 47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 48 | return { 49 | css: generateLoaders(), 50 | postcss: generateLoaders(), 51 | less: generateLoaders('less'), 52 | sass: generateLoaders('sass', { indentedSyntax: true }), 53 | scss: generateLoaders('sass'), 54 | stylus: generateLoaders('stylus'), 55 | styl: generateLoaders('stylus') 56 | } 57 | } 58 | 59 | // Generate loaders for standalone style files (outside of .vue) 60 | exports.styleLoaders = function (options) { 61 | var output = [] 62 | var loaders = exports.cssLoaders(options) 63 | for (var extension in loaders) { 64 | var loader = loaders[extension] 65 | output.push({ 66 | test: new RegExp('\\.' + extension + '$'), 67 | use: loader 68 | }) 69 | } 70 | return output 71 | } 72 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var config = require('../config') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | loaders: utils.cssLoaders({ 7 | sourceMap: isProduction 8 | ? config.build.productionSourceMap 9 | : config.dev.cssSourceMap, 10 | extract: isProduction 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var config = require('../config') 4 | var vueLoaderConfig = require('./vue-loader.conf') 5 | 6 | function resolve (dir) { 7 | return path.join(__dirname, '..', dir) 8 | } 9 | 10 | module.exports = { 11 | entry: { 12 | app: './src/main.js' 13 | }, 14 | output: { 15 | path: config.build.assetsRoot, 16 | filename: '[name].js', 17 | publicPath: process.env.NODE_ENV === 'production' 18 | ? config.build.assetsPublicPath 19 | : config.dev.assetsPublicPath 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.vue', '.json'], 23 | alias: { 24 | 'vue$': 'vue/dist/vue.esm.js', 25 | '@': resolve('src') 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.vue$/, 32 | loader: 'vue-loader', 33 | options: vueLoaderConfig 34 | }, 35 | { 36 | test: /\.js$/, 37 | loader: 'babel-loader', 38 | include: [resolve('src'), resolve('test')] 39 | }, 40 | { 41 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 42 | loader: 'url-loader', 43 | options: { 44 | limit: 10000, 45 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 46 | } 47 | }, 48 | { 49 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 50 | loader: 'url-loader', 51 | options: { 52 | limit: 10000, 53 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 54 | } 55 | } 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /build/webpack.build.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require('path') 3 | var version = require("./../package.json").version; 4 | var banner = "/**\n" + " * vuetable-2 v" + version + "\n" + " * https://github.com/ratiw/vuetable-2\n" + " * Released under the MIT License.\n" + " */\n"; 5 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 6 | var StatsPlugin = require("stats-webpack-plugin"); 7 | 8 | var utils = require('./utils') 9 | var merge = require('webpack-merge') 10 | var baseWebpackConfig = require('./webpack.base.conf') 11 | 12 | var cssFileName = "vuetable-2.css"; 13 | var jsFileName = "vuetable-2.js"; 14 | 15 | if(process.env.MINIFY && process.env.MINIFY === "false"){ 16 | jsFileName = "vuetable-2-full.js" 17 | } 18 | var minifyPlugins = [ 19 | new webpack.LoaderOptionsPlugin({ 20 | minimize: true, 21 | debug: false 22 | }), 23 | new webpack.optimize.UglifyJsPlugin({ 24 | compress: { 25 | warnings: false 26 | }, 27 | sourceMap:true, 28 | comments: false, 29 | beautify: false 30 | }), 31 | ]; 32 | 33 | var webpackConfig = merge(baseWebpackConfig, { 34 | module: { 35 | rules: utils.styleLoaders({sourceMap: true,extract: true}) 36 | }, 37 | entry: path.join(__dirname, '..', "src/index.js"), 38 | output: { 39 | path: path.join(__dirname, '..', "dist"), 40 | filename: jsFileName, 41 | library: "Vuetable", 42 | libraryTarget: "umd" 43 | }, 44 | plugins: [ 45 | new webpack.DefinePlugin({ 46 | "process.env": { 47 | NODE_ENV: '"production"' 48 | }, 49 | VERSION: JSON.stringify(require("../package.json").version) 50 | }), 51 | new webpack.BannerPlugin({ 52 | banner: banner, 53 | raw: true 54 | }), 55 | new ExtractTextPlugin({filename: cssFileName, allChunks: true}), 56 | new StatsPlugin('stats.json') 57 | ], 58 | resolve: { 59 | aliasFields: ["browser"] 60 | } 61 | }); 62 | 63 | if(process.env.MINIFY && process.env.MINIFY === "true"){ 64 | webpackConfig.plugins = webpackConfig.plugins.concat(minifyPlugins); 65 | } 66 | module.exports = webpackConfig; 67 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var webpack = require('webpack') 3 | var config = require('../config') 4 | var merge = require('webpack-merge') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 8 | 9 | // add hot-reload related code to entry chunks 10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 12 | }) 13 | 14 | module.exports = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: '#cheap-module-eval-source-map', 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': config.dev.env 23 | }), 24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }), 33 | new FriendlyErrorsPlugin() 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var webpack = require('webpack') 4 | var config = require('../config') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var CopyWebpackPlugin = require('copy-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 11 | 12 | var env = process.env.NODE_ENV === 'testing' 13 | ? require('../config/test.env') 14 | : config.build.env 15 | 16 | var webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? '#source-map' : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | new webpack.optimize.UglifyJsPlugin({ 35 | compress: { 36 | warnings: false 37 | }, 38 | sourceMap: true 39 | }), 40 | // extract css into its own file 41 | new ExtractTextPlugin({ 42 | filename: utils.assetsPath('css/[name].[contenthash].css') 43 | }), 44 | // Compress extracted CSS. We are using this plugin so that possible 45 | // duplicated CSS from different components can be deduped. 46 | new OptimizeCSSPlugin({ 47 | cssProcessorOptions: { 48 | safe: true 49 | } 50 | }), 51 | // generate dist index.html with correct asset hash for caching. 52 | // you can customize output by editing /index.html 53 | // see https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: process.env.NODE_ENV === 'testing' 56 | ? 'index.html' 57 | : config.build.index, 58 | template: 'index.html', 59 | inject: true, 60 | minify: { 61 | removeComments: true, 62 | collapseWhitespace: true, 63 | removeAttributeQuotes: true 64 | // more options: 65 | // https://github.com/kangax/html-minifier#options-quick-reference 66 | }, 67 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 68 | chunksSortMode: 'dependency' 69 | }), 70 | // split vendor js into its own file 71 | new webpack.optimize.CommonsChunkPlugin({ 72 | name: 'vendor', 73 | minChunks: function (module, count) { 74 | // any required modules inside node_modules are extracted to vendor 75 | return ( 76 | module.resource && 77 | /\.js$/.test(module.resource) && 78 | module.resource.indexOf( 79 | path.join(__dirname, '../node_modules') 80 | ) === 0 81 | ) 82 | } 83 | }), 84 | // extract webpack runtime and module manifest to its own file in order to 85 | // prevent vendor hash from being updated whenever app bundle is updated 86 | new webpack.optimize.CommonsChunkPlugin({ 87 | name: 'manifest', 88 | chunks: ['vendor'] 89 | }), 90 | // copy custom static assets 91 | new CopyWebpackPlugin([ 92 | { 93 | from: path.resolve(__dirname, '../static'), 94 | to: config.build.assetsSubDirectory, 95 | ignore: ['.*'] 96 | } 97 | ]) 98 | ] 99 | }) 100 | 101 | if (config.build.productionGzip) { 102 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 103 | 104 | webpackConfig.plugins.push( 105 | new CompressionWebpackPlugin({ 106 | asset: '[path].gz[query]', 107 | algorithm: 'gzip', 108 | test: new RegExp( 109 | '\\.(' + 110 | config.build.productionGzipExtensions.join('|') + 111 | ')$' 112 | ), 113 | threshold: 10240, 114 | minRatio: 0.8 115 | }) 116 | ) 117 | } 118 | 119 | if (config.build.bundleAnalyzerReport) { 120 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 122 | } 123 | 124 | module.exports = webpackConfig 125 | -------------------------------------------------------------------------------- /build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | // This is the webpack config used for unit tests. 2 | 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseConfig = require('./webpack.base.conf') 7 | 8 | var webpackConfig = merge(baseConfig, { 9 | // use inline sourcemap for karma-sourcemap-loader 10 | module: { 11 | rules: utils.styleLoaders() 12 | }, 13 | devtool: '#inline-source-map', 14 | resolveLoader: { 15 | alias: { 16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 18 | 'scss-loader': 'sass-loader' 19 | } 20 | }, 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': require('../config/test.env') 24 | }) 25 | ] 26 | }) 27 | 28 | // no need for app entry during tests 29 | delete webpackConfig.entry 30 | 31 | module.exports = webpackConfig 32 | -------------------------------------------------------------------------------- /changes.md: -------------------------------------------------------------------------------- 1 | # What's Changed 2 | - `vuetable-pagination` is no longer part of `vuetable` by default. 3 | + better customization 4 | + cleaner implementation 5 | + easy to setup 6 | * listen to `vuetable:pagination-data` event on `vuetable-table>` component to wire the pagination data to `vuetable-pagination` component. 7 | * listen to `vuetable-pagination:change-page` event on designated `vuetable-pagination` component to wire the page changing request to `vuetable-table` 8 | - Due to deprecations of $broadcast and $dispatch in Vue 2.0, all of the listening events where you used to be able to sent vuetable to perform task, e.g. `refresh()` are no longer existed. Please check documentation. You are now required to use `ref` to directly refer to the `vuetable` components to directly use its API for certain functionality. 9 | + Available events 10 | * vuetable:loading 11 | * vuetable:loaded 12 | * vuetable:load-success 13 | * vuetable:load-error 14 | * vuetable:pagination-data 15 | * vuetable:row-changed 16 | * vuetable:row-clicked 17 | * vuetable:detail-row-clicked 18 | * vuetable:cell-clicked 19 | * vuetable:cell-dblclicked 20 | * vuetable:checkbox-toggled 21 | + No longer available 22 | * vuetable:reload -- directly call `reload()` method instead 23 | * vuetable:refresh -- directly call `refresh()` method instead 24 | * vuetable:goto-page -- directly call `changePage()` method instead 25 | * vuetable-pagination:change-page -- no real use now 26 | * vuetable:action -- no longer use as `__actions` is removed. 27 | - `__actions` special field was removed in favor of `__component`. See example. 28 | - `appendParams` prop type changes from Array to Object to reflect the change in updating vue-resource to version 1.0 (Breaking Change) 29 | - vuetable should now work with IE, thanks to template compilation to virtual DOM. 30 | - Removed properties 31 | + `wrapper-class` 32 | + `table-wrapper` 33 | + `show-pagination` 34 | + `pagination-component` 35 | + `pagination-component-class` 36 | + `pagination-class` 37 | + `selected-to` 38 | + `http-data` -- vue-resource API change, use `http-options` instead 39 | - Moved properties 40 | + The following are properties related to CSS, which are now moved inside `css` prop. 41 | * `tableClass` 42 | * `loadingClass` 43 | * `ascendingIcon` 44 | * `descendingIcon` 45 | * `detailRowClass` 46 | + The following pagination info related properties are now moved into its own component `PaginationInfo`. 47 | * `pagination-info-class` 48 | * `pagination-info-template` 49 | * `pagination-info-no-data-template` 50 | - `vuetable:load-success` event is now fired right after the requested data has been successfully retrieved. This is logically how it should be. Previously, it was fired after the `tableData` and `tablePagination` have been set. 51 | 52 | # New Features 53 | - Data transformation via `transform` method. 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8080, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: {}, 31 | // CSS Sourcemaps off by default because relative paths are "buggy" 32 | // with this option, according to the CSS-Loader README 33 | // (https://github.com/webpack/css-loader#sourcemaps) 34 | // In our experience, they generally work as expected, 35 | // just be aware of this issue when enabling this option. 36 | cssSourceMap: false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /dist/vuetable-2.css: -------------------------------------------------------------------------------- 1 | /** 2 | * vuetable-2 v1.6.0 3 | * https://github.com/ratiw/vuetable-2 4 | * Released under the MIT License. 5 | */ 6 | 7 | [v-cloak][_v-0c70c9de]{display:none}.vuetable th.sortable[_v-0c70c9de]:hover{color:#2185d0;cursor:pointer}.vuetable-actions[_v-0c70c9de]{width:15%;padding:12px 0;text-align:center}.vuetable-pagination[_v-0c70c9de]{background:#f9fafb!important}.vuetable-pagination-info[_v-0c70c9de]{margin-top:auto;margin-bottom:auto}.vuetable-empty-result[_v-0c70c9de]{text-align:center} -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratiw/vuetable-2/1f01bcf540e3c799dd4fe4d064c9af45784ca481/docs/.nojekyll -------------------------------------------------------------------------------- /docs/API-vs-Data.md: -------------------------------------------------------------------------------- 1 | # API mode vs. Data mode 2 | 3 | Despite designing to work with API from the beginning, in version 1.6, Vuetable can now work with local data. 4 | 5 | This can be done by specifying that you want to disable the API Mode by setting `:api-mode="false"` and supplying your own data via `:data` prop. But this will also make the server related functionality (like sorting, paging, and page sizing, etc.) stop working. 6 | 7 | 8 | 9 | ## Note 10 | 11 | Vuetable is opinionated toward working with the API endpoints from the beginning. This is based solely on my experiences building applications either for desktop or for the web. 12 | 13 | I have a strong believe that this is the model for building app. The client side should handle most of the presentation that interacts with the user, while the server side should handle most of the logic behind the application, this also includes the database related functions. 14 | 15 | Most of the time, we have a very powerful and well developed database server on the server side where it can do an excellent and efficient tasks it was designed to do, like searching, sorting, and filtering of data. Another great benefit of this is that your app will also scale better instead of implementing the client side code to do these tasks. 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/Background.md: -------------------------------------------------------------------------------- 1 | ## A Background Story 2 | 3 | Vuetable was born out of a curiousity of learning more about Vue.js and turning a repeated task of creating a data table into a Vue component. 4 | 5 | Like many others, I feel that CRUD based system is easy to understand and implement. With that I usually break up things into modules. Each module will have its own functionality built around CRUD, which are 6 | - index page -- listing all the paginated data relevant to that module 7 | - view page -- showing a specific data record that might also allow deletion 8 | - create page -- allow creating new data record 9 | - edit page -- allow updating existing data record 10 | - other suppporing pages 11 | 12 | Somehow, creating a index page for each module has become copying and pasting existing code from another module and making necessary changes to fit another module's needs. 13 | 14 | Not to mention all the API request stuffs that mostly looks and behaves the same from the backend. This became a boring stuff to do. 15 | 16 | And that's when the [first version](https://github.com/ratiw/vue-table/commit/3143385d718c67e266b02a001e7035cb1c0ffd71) of Vuetable was created. 17 | 18 | When Vue 2 was introduced, Vuetable was also revised to work with this version to take advantage of the Virtual DOM that makes it possible for Vuetable-2 to work with other browsers besides Chrome. 19 | 20 | With the help, the requests, and the suggestions from the community, Vuetable has grown in its features to where it is today. 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/CSS-Styling.md: -------------------------------------------------------------------------------- 1 | # CSS Styling 2 | 3 | > __Note__ 4 | > The name inside the square bracket is optional. It may or may not appear depending on the relevant condition 5 | 6 | # Vuetable 7 | 8 | ```javascript 9 | { 10 | tableClass: 'ui blue selectable celled stackable attached table', 11 | loadingClass: 'loading', 12 | ascendingIcon: 'blue chevron up icon', 13 | descendingIcon: 'blue chevron down icon', 14 | detailRowClass: 'vuetable-detail-row', 15 | handleIcon: 'grey sidebar icon', 16 | sortableIcon: '', // since v1.7 17 | ascendingClass: 'sorted-asc', // since v1.7 18 | descendingClass: 'sorted-desc' // since v1.7 19 | } 20 | ``` 21 | 22 | ## Table `` 23 | 24 | ```html 25 |
26 | ``` 27 | 28 | ## Table Header Column `` 29 | 30 | ### Fields 31 | ```html 32 | 33 | ``` 34 | ### Special Fields 35 | 36 | - __sequence 37 | ```html 38 | 39 | ``` 40 | 41 | - __checkbox 42 | ```html 43 | 44 | ``` 45 | 46 | - __component 47 | ```html 48 | 49 | ``` 50 | 51 | - __slot 52 | ```html 53 | 54 | ``` 55 | 56 | ## Table Column `` 57 | 58 | ### Fields 59 | ```html 60 | 61 | ``` 62 | 63 | ### Special Fields 64 | 65 | - __sequence 66 | ```html 67 | 68 | ``` 69 | 70 | - __handle 71 | ```html 72 | 73 | ``` 74 | 75 | - __checkbox 76 | ```html 77 | 78 | ``` 79 | 80 | - __component 81 | ```html 82 | 83 | ``` 84 | 85 | - __slot 86 | ```html 87 | 88 | ``` 89 | 90 | ## Detail Row 91 | 92 | ```html 93 | 94 | ``` 95 | 96 | ## Other Elements 97 | 98 | ### # loadingClass 99 | 100 | ### # ascendingIcon 101 | 102 | ### # descendingIcon 103 | 104 | ### # handleIcon 105 | 106 | ## Pagination 107 | 108 | ```javascript 109 | { 110 | wrapperClass: 'ui right floated pagination menu', 111 | activeClass: 'active large', 112 | disabledClass: 'disabled', 113 | pageClass: 'item', 114 | linkClass: 'icon item', 115 | paginationClass: 'ui bottom attached segment grid', 116 | paginationInfoClass: 'left floated left aligned six wide column', 117 | dropdownClass: 'ui search dropdown', 118 | icons: { 119 | first: 'angle double left icon', 120 | prev: 'left chevron icon', 121 | next: 'right chevron icon', 122 | last: 'angle double right icon', 123 | } 124 | } 125 | ``` 126 | 127 | -------------------------------------------------------------------------------- /docs/Callbacks.md: -------------------------------------------------------------------------------- 1 | # Callbacks 2 | 3 | In Vuetable, each field can have an associated callback function that will allow you to format the value of the column for display. 4 | 5 | > __Note__ 6 | > The name `callback` might be changed to `format` in the near future to make it really reflect what it does. 7 | 8 | You can define the callback function inside the field definition using the `callback` option by specifying either the name of the function to be called or the function reference. 9 | 10 | Here is the example of a field defining a callback function. 11 | ```javascript 12 | // specifying the name of the function as string 13 | { 14 | name: 'gender', 15 | callback: 'gender' 16 | } 17 | 18 | // specifying the function reference. 19 | // in this case, function `gender` must be defined in the `methods` section. 20 | { 21 | name: 'gender', 22 | callback: gender 23 | } 24 | ``` 25 | 26 | In this case, the field named `gender` has a callback function, which is coincidently named `gender` as well. Vuetable will automatically look for this callback function in its parent Vue.js instance. 27 | 28 | The callbacks are defined inside the Vue.js instance where Vuetable component is defined. 29 | 30 | Let's look at the `gender` callback function. 31 | ```javascript 32 | new Vue({ 33 | el: '#app', 34 | methods: { 35 | gender: function(value) { 36 | return value == 'M' ? 'Male' : 'Female' 37 | } 38 | } 39 | }) 40 | ``` 41 | 42 | Vuetable will automatically pass the value of that field to the callback function. The callback function can then work on the value, and the value returned from the callback will be used to display to the user. 43 | 44 | In this case, if the `value` that gets passed to `gender` callback is `M`, it will return `Male`. Vuetable will display `Male` for this field instead of `M`. 45 | 46 | #### Passing Additional Parameters to Callback function 47 | Suppose you have a callback function to format the date value to be displayed in certain date format, but you also would like to be able to override that default date format as well. You can do so like this: 48 | ```javascript 49 | new Vue({ 50 | el: '#app', 51 | columns: [ 52 | { 53 | name: 'birthdate', 54 | callback: 'formatDate|D/MM/Y' 55 | } 56 | ], 57 | methods: { 58 | formatDate: function(value, fmt) { 59 | if (value == null) return '' 60 | fmt = (typeof fmt == 'undefined') ? 'D MMM YYYY' : fmt 61 | return moment(value, 'YYYY-MM-DD').format(fmt) 62 | } 63 | } 64 | }) 65 | ``` 66 | 67 | In this example, field `birthdate` has a callback named `formatDate`. The callback defines additional parameter using `|` character following the name of the callback function. 68 | 69 | When the `formatDate` callback is called the `value` of the field will be passed as well as the additional parameters. So, the callback function can use that additional parameters to decide what the return value should be based on those additional parameters. 70 | 71 | -------------------------------------------------------------------------------- /docs/Data-Format-JSON.md: -------------------------------------------------------------------------------- 1 | # Data Format (JSON) 2 | 3 | In order for Vuetable to do its task, it needs to work with a certain JSON data structure. Here is the default data format that Vuetable expects: 4 | 5 | ```json 6 | { 7 | "links": { 8 | "pagination": { 9 | "total": 50, 10 | "per_page": 15, 11 | "current_page": 1, 12 | "last_page": 4, 13 | "next_page_url": "...", 14 | "prev_page_url": "...", 15 | "from": 1, 16 | "to": 15, 17 | } 18 | }, 19 | "data": [ 20 | { 21 | "id": 1, 22 | "name": "xxxxxxxxx", 23 | "nickname": "xxxxxxx", 24 | "email": "xxx@xxx.xxx", 25 | "birthdate": "xxxx-xx-xx", 26 | "gender": "X", 27 | "group_id": 1, 28 | }, 29 | . 30 | . 31 | . 32 | { 33 | "id": 50, 34 | "name": "xxxxxxxxx", 35 | "nickname": "xxxxxxx", 36 | "email": "xxx@xxx.xxx", 37 | "birthdate": "xxxx-xx-xx", 38 | "gender": "X", 39 | "group_id": 3, 40 | } 41 | ] 42 | } 43 | ``` 44 | 45 | Mostly, it looks for two pieces of information; the array of data, the pagination information. 46 | 47 | It uses two keys (`data-path` and `pagination-path`) to look into those data to extract the information it is needed. 48 | 49 | - `data-path` points to the array of data 50 | - `pagination-path` points to the pagination information 51 | 52 | In the example above, the `data-path` is `data` and the `pagination-path` is `links.pagination`, which are the default value that Vuetable uses if none is provided. 53 | 54 | If you're familiar with [Laravel](https://laravel.com), you would know that Laravel automatically convert the query data to JSON format when Eloquent objects are returned from application's routes or controllers. And if you use `paginate()` function, the result would look something like this. 55 | 56 | ```json 57 | { 58 | "total": 50, 59 | "per_page": 15, 60 | "current_page": 1, 61 | "last_page": 4, 62 | "next_page_url": "...", 63 | "prev_page_url": "...", 64 | "from": 1, 65 | "to": 15, 66 | "data": [ 67 | { 68 | "id": 1, 69 | "name": "xxxxxxxxx", 70 | "nickname": "xxxxxxx", 71 | "email": "xxx@xxx.xxx", 72 | "birthdate": "xxxx-xx-xx", 73 | "gender": "X", 74 | "group_id": 1, 75 | }, 76 | . 77 | . 78 | . 79 | { 80 | "id": 50, 81 | "name": "xxxxxxxxx", 82 | "nickname": "xxxxxxx", 83 | "email": "xxx@xxx.xxx", 84 | "birthdate": "xxxx-xx-xx", 85 | "gender": "X", 86 | "group_id": 3, 87 | } 88 | ] 89 | } 90 | ``` 91 | 92 | In this case, you just specify values for `data-path` and `pagination-path` like so 93 | ```html 94 |
95 | 101 |
102 | ``` 103 | 104 | This tells Vuetable that the data is in the path named `data` inside the JSON structure returned from the server, and the pagination information is in the root of the JSON structure. 105 | -------------------------------------------------------------------------------- /docs/Data-Transformation.md: -------------------------------------------------------------------------------- 1 | # Data Transformation 2 | 3 | If the data you're working with is not in the default format that Vuetable uses, you can setup a `transform()` method, which accept response `data` as the argument, to transform it to the format that vuetable can work with. 4 | 5 | To setup the data transformation function, just create a `transform` method in the parent instance of Vuetable like so, 6 | 7 | ```javascript 8 | new Vue({ 9 | el: '#app', 10 | data: { 11 | //... 12 | }, 13 | methods: { 14 | transform: function(data) { 15 | var transformed = {} 16 | 17 | transformed.pagination = { 18 | total: data.total, 19 | per_page: data.per_page, 20 | current_page: data.current_page, 21 | last_page: data.last_page, 22 | next_page_url: data.next_page_url, 23 | prev_page_url: data.prev_page_url, 24 | from: data.from, 25 | to: data.to 26 | } 27 | 28 | transformed.mydata = [] 29 | 30 | for (var i=0; i < data.length; i++) { 31 | transformed.mydata.push({ 32 | id: data[i].id, 33 | fullname: data[i].name, 34 | email: data[i].email 35 | }) 36 | } 37 | 38 | return transformed 39 | } 40 | } 41 | }) 42 | ``` 43 | 44 | ```html 45 | 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/Detail-Row.md: -------------------------------------------------------------------------------- 1 | # Detail Row 2 | 3 | Detail row is the additional row hidden underneath each data row in the table. When you want to only display relevant data for each row, but also would like to display additional information when needed, Detail Row would be a great help. 4 | 5 | In order to use Detail row, you will need to create a component and register it using `Vue.component` helper. Then, you can use `detail-row-component` property to specify the name of your detail row component. 6 | 7 | Your detail row component should define the following two properties: 8 | - `row-data` -- the current row data will be passed to 9 | - `row-index` -- the current row index will be passed to 10 | 11 | Example: 12 | ```javascript 13 | //.. 14 | props: { 15 | rowData: { 16 | type: Object, 17 | required: true 18 | }, 19 | rowIndex: { 20 | type: Number 21 | }, 22 | } 23 | ``` 24 | 25 | ## How Vuetable track the state of each Detail Row 26 | 27 | Each detail row can be shown or hidden independently. So Vuetable needs a way to track the state of each detail row to update the UI accordingly. 28 | 29 | In order to do this, it is required that each data row must be uniquely identifiable. If your data contains an `id` column (which can uniquely identify each row), then you don't have to do anything else. Because, by default, Vuetable uses the `id` as the default one. See [Row Identifier](Row Identifier) for more detail. 30 | 31 | But if your data use (or has) different column name that can uniquely identify each row, you can specify that in the `track-by` property. 32 | 33 | For example, your data uses `uuid` instead of `id` column, you can tell Vuetable to use it. 34 | ```html 35 | 40 | ``` 41 | 42 | There are a number of methods that you can use to manipulate the detail row. Each of them requires a [row identifier](Row-Identifier) as an only parameter. 43 | - isVisibleDetailRow 44 | - showDetailRow 45 | - hideDetailRow 46 | - toggleDetailRow 47 | 48 | To call those methods, you can use `ref` tag, like so, 49 | ```javascript 50 | this.$refs.vuetable.toggleDetailRow(rowId) 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | ## FAQ 2 | - [Background](Background) 3 | - API mode vs. Data mode 4 | - Why my data does not show up? 5 | - Why the pagination does not show anything? 6 | - How to specify column width? 7 | - Can Vuetable do the data grouping? 8 | -------------------------------------------------------------------------------- /docs/Fields-Definition.md: -------------------------------------------------------------------------------- 1 | Fields can be defined as simpl array of string, array of object, or the mix. 2 | 3 | - Fields defined as simple array 4 | 5 | ```javascript 6 | let columns = ['name', 'email', 'birthdate', 'gender'] 7 | ``` 8 | 9 | - Fields defined as array of object 10 | ```javascript 11 | let columns = [ 12 | { 13 | name: 'name', 14 | sortField: 'name' 15 | }, 16 | { 17 | name: 'email', 18 | title: 'Email Address' 19 | }, 20 | { 21 | name: 'birthdate', 22 | sortField: 'birthdate', 23 | titleClass: 'center aligned', 24 | dataClass: 'center aligned', 25 | callback: 'formatDate|D/MM/Y' 26 | }, 27 | { 28 | name: 'gender', 29 | sortField: 'gender', 30 | titleClass: 'center aligned', 31 | dataClass: 'center aligned', 32 | callback: 'gender' 33 | }, 34 | ] 35 | ``` 36 | 37 | - Fields defined as array of the mix 38 | ```javascript 39 | let columns = [ 40 | 'name', 41 | 'email', 42 | { 43 | name: 'birthdate', 44 | sortField: 'birthdate', 45 | titleClass: 'center aligned', 46 | dataClass: 'center aligned', 47 | callback: 'formatDate|D/MM/Y' 48 | }, 49 | { 50 | name: 'gender', 51 | sortField: 'gender', 52 | titleClass: 'center aligned', 53 | dataClass: 'center aligned', 54 | callback: 'gender' 55 | }, 56 | //... 57 | ] 58 | ``` 59 | 60 | The difference is that if you defined a field as simple array of string. Vuetable will only display it as-is 61 | without any sorting or formatting options. Vuetable will convert the simple field definition 62 | to field object with only `name` property. 63 | 64 | ```javascript 65 | let columns = ['name'] 66 | // will be converted to this 67 | // let columns = [ { name: 'name' } ] 68 | ``` 69 | 70 | ## Field options 71 | 72 | Available options 73 | - [name](#-name) 74 | - [sortField](#-sortField) 75 | - [title](#-title) 76 | - [titleClass](#-titleClass) 77 | - [dataClass](#-dataClass) 78 | - [callback](#-callback) 79 | - [visible](#-visible) 80 | - [width](#-width) 81 | 82 | ### # name 83 | - type: _String_ 84 | 85 | Name of the data field to be display. 86 | 87 | ### # sortField 88 | - type: _String_ 89 | 90 | Usually, it will be the same as `name` option. But you can specify different value if 91 | you use different field name when querying data on the serve side, e.g. firstname. 92 | 93 | If specified, the field will be marked as sortable. `vuetable` will display appropriate 94 | clickable icon after the field title. `vuetable` will also make a new request to the server 95 | with the `sort=` query string appended. 96 | 97 | ### # title 98 | - type: [_String_, _Function_] 99 | 100 | If you would like to specify the title yourself, you can put it in here. Otherwise, `vuetable` 101 | will use the `name` option instead. 102 | 103 | You can even put the icon class in the title, see example below 104 | ```javascript 105 | //.. example using Semantic UI icon class 106 | { 107 | name: 'birthdate', 108 | title: ' Birthdate' 109 | } 110 | // 111 | ``` 112 | 113 | _New feature (after v1.6.5)_ 114 | You can also define `title` as a function that returns a string. Vuetable will use that returned string to render the title of the column. 115 | ```javascript 116 | //.. example using Semantic UI icon class 117 | { 118 | name: 'birthdate', 119 | title: () => ' Birthdate' 120 | } 121 | // 122 | ``` 123 | 124 | This allow total flexibility and make it possible to make use of external i18n library. 125 | 126 | ### # titleClass 127 | - type: _String_ 128 | 129 | The css class you would like to apply for the title of this field. 130 | 131 | ### # dataClass 132 | - type: _String_ 133 | 134 | The css class you would like to apply for the data of this field. 135 | 136 | ### # callback 137 | - type: [_String_, _Function_] 138 | 139 | The name of the callback function to be called to allow formatting of the value 140 | to be displayed. See [Callback](Callbacks) section for more info. 141 | 142 | ### # visible 143 | - type: _Boolean_ 144 | 145 | Whether this field should be visible or hidden when rendering the table. 146 | 147 | ### # width _(since v1.7)_ 148 | - type: _String_ 149 | 150 | Specify the width of the column as string, e.g. `600px`. See [Fixed Header](Fixed-Header) section for more info. 151 | 152 | 153 | ## Nested JSON Data 154 | If the JSON data structure contains nested objects, eg: 155 | ```json 156 | { 157 | "links": { 158 | "pagination": { 159 | "per_page": 15, 160 | } 161 | }, 162 | "data": [ 163 | { 164 | "id": 1, 165 | "name": "xxxxxxxxx", 166 | "email": "xxx@xxx.xxx", 167 | "group_id": 1, 168 | "address" { 169 | "street_address":"123 abc place", 170 | "city":"townsville", 171 | "province":"Harbor", 172 | "country":"Antegria" 173 | } 174 | } 175 | . 176 | . 177 | . 178 | { 179 | "id": 50, 180 | "name": "xxxxxxxxx", 181 | "email": "xxx@xxx.xxx", 182 | "group_id": 3, 183 | "address" { 184 | "street_address":"456 xyz street", 185 | "city":"cityville", 186 | "province":"Portia", 187 | "country":"Norland" 188 | } 189 | } 190 | ] 191 | } 192 | ``` 193 | In this case, the column names of nested objects can be specified in the following format: 194 | 195 | ```javascript 196 | let columns = ['name', 'email', 'address.city', 'address.country'] 197 | ``` 198 | -------------------------------------------------------------------------------- /docs/Filtering.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratiw/vuetable-2/1f01bcf540e3c799dd4fe4d064c9af45784ca481/docs/Filtering.md -------------------------------------------------------------------------------- /docs/Fixed-Header.md: -------------------------------------------------------------------------------- 1 | # Fixed Header _(since v1.7)_ 2 | 3 | Vuetable is now able to fixed the table header, so that when the table data height is more than the specified table height, the table body can be scrolled. 4 | 5 | This is mostly useful if you're using Vuetable in Data mode, where you manually supply the data to Vuetable instead of let Vuetable handle the API data request to the server side, or you need to limit the height of the table to a certain value. 6 | 7 | To enable fixed header, you have to specify the height of the table via [`table-height`](Vuetable-Properties#-table-height) prop. 8 | 9 | By default, `table-height` value is `null` so it shouldn't affect any previous code in ealier version. 10 | 11 | Once fixed header has been enabled, you can "disable" it by setting `table-height` prop to `null`. 12 | 13 | The `width` option has been added to [Fields definition](Fields-Definition) options, to allow specifying width for each column table. This is go well with the fixed header. So, you will definitely want to add `width` option to all your field definition. 14 | 15 | You can find the example in the `main.js` file in the `src` direction. 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | ## Vuetable-2 : datatable simplify! 2 | 3 | _Still A Work in Progress_ 4 | 5 | ### Table of Contents 6 | 7 | ### Vuetable 8 | - [Fields Definition](Fields-Definition) 9 | - [Callback / Formatter](Callbacks) 10 | - [Special Fields](Special-Fields) 11 | - [Row Identifier](Row-Identifier) 12 | - [Data Format (JSON)](Data-Format-(JSON)) 13 | - [Sorting](Sorting) 14 | - [Overriding Default Query String](Overriding-Default-Query-String) 15 | - [Data Transformation](Data-Transformation) 16 | - [Pagination](Pagination) 17 | - [Detail Row](Detail-Row) 18 | - [Fixed Header](Fixed-Header) 19 | - [CSS Styling](CSS-Styling) 20 | - [Tutorial](https://github.com/ratiw/vuetable-2-tutorial/blob/master/doc/README.md) 21 | - [FAQ](FAQ) 22 | 23 | ### Components API 24 | - Vuetable 25 | + [Properties](Vuetable-Properties) 26 | + [Computed properties](Vuetable-Computed) 27 | + [Data](Vuetable-Data) 28 | + [Methods](Vuetable-Methods) 29 | + [Events](Vuetable-Events) 30 | 31 | - [VuetablePagination](VuetablePagination) 32 | 33 | - [VuetablePaginationMixin](VuetablePaginationMixin) 34 | 35 | - [VuetablePaginationDropdown](VuetablePaginationDropdown) 36 | 37 | - [VuetablePaginationInfo](VuetablePaginationInfo) 38 | 39 | - [VuetablePaginationInfoMixin](VuetablePaginationInfoMixin) 40 | -------------------------------------------------------------------------------- /docs/Overriding-Default-Query-String.md: -------------------------------------------------------------------------------- 1 | # Overriding Default Query String 2 | 3 | By default, Vuetable uses the following key in the query string that will be sent to the server. 4 | - `sort` -- its value will contain the requested sort order 5 | - `page` -- its value will contain the requested page number 6 | - `per_page` -- its value will contain the requested records per page 7 | 8 | If you're making your own API backend, you can just use this default. 9 | 10 | But if you're using someone's else API or you already have your own existing API that do not correspond to this. You can just tell Vuetable how what key it should be using instead by supplying it via `query-params` property. 11 | 12 | Here is the default value of `query-params` property. 13 | ```javascript 14 | { 15 | sort: 'sort', 16 | page: 'page', 17 | perPage: 'per_page' 18 | } 19 | ``` 20 | 21 | Suppose your API uses `sort_order`, `page_no`, and `page_size`, you can easily tell Vuetable to use them like so, 22 | ```html 23 | 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/Pagination.md: -------------------------------------------------------------------------------- 1 | # Pagination 2 | 3 | Vuetable comes ready with two pagination components and a pagination information component which you can extend to easily build your own. 4 | - [VuetablePagination](VuetablePagination) 5 | - [VuetablePaginationDropdown](VuetablePaginationDropDown) 6 | - [VuetablePaginationInfo](VuetablePaginationInfo) 7 | 8 | You can see the usage from the [lesson 7](https://github.com/ratiw/vuetable-2-tutorial/blob/master/doc/lesson-07.md) of the tutorial. 9 | 10 | ## VuetablePagination 11 | 12 | This is a sliding pagination port from [Laravel](https://laravel.com). 13 | 14 | By default, it will render 5 sliding pages and navigation buttons for first page, previous page, next page, and last page. The number of sliding pages will also be even number with the current displayed page being in the center. 15 | 16 | The number of sliding pages can be configured via `on-each-side` prop and will be calculated using this formula. 17 | ``` 18 | number of sliding pages = onEachSide * 2 +1 19 | ``` 20 | 21 | ## VuetablePaginationDropdown 22 | 23 | It was designed to take up less space as all the pages will be put inside a dropdown for the user to select and jump to the selected page. 24 | 25 | Vuetable provides a `VautablePaginationMixin` and `VuetablePaginationInfoMixin` to help creating custom pagination component an easy task. Please see the source code as it is very straight forward, but you will need some knowledge about Vue.js. 26 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/vuetable-2.svg)](https://www.npmjs.com/package/vuetable-2) 2 | [![npm](https://img.shields.io/npm/l/vuetable-2.svg?maxAge=2592000)]() 3 | 4 | # Vuetable-2 - data table simplify! 5 | 6 | ### Vuetable-2 works with Vue 2.x, vuetable is for Vue 1.x 7 | 8 | If you're looking for the version that's working with Vue 1.x, please go to [`vuetable`](https://github.com/ratiw/vue-table) repo. 9 | 10 | --- 11 | 12 | ### Documentation and Tutorial 13 | 14 | Documentation is still under development, but you can view it at [https://ratiw.github.io/vuetable-2](https://ratiw.github.io/vuetable-2). Thanks to @cristijora for the help. 15 | 16 | Meanwhile, check out 17 | - the [Tutorial](https://github.com/ratiw/vuetable-2-tutorial/wiki) 18 | with follow-along project [here](https://github.com/ratiw/vuetable-2-tutorial). It should be enough to get you started. 19 | 20 | - [Sample project](https://github.com/ratiw/vuetable-2-with-laravel-5.4) using Vuetable-2 with Laravel 5.4 and Laravel-Mix 21 | 22 | If you've been using Vuetable for Vue 1.x before, checkout [what's changed](https://github.com/ratiw/vuetable-2/blob/master/changes.md) for info on changes from Vuetable for Vue 1.x and the [upgrade guide](https://github.com/ratiw/vuetable-2/blob/master/upgrade-guide.md) on how you could upgrade from Vuetable for Vue 1.x. 23 | 24 | You can now make use of Vue's scoped slot using the new `__slot` special field, thanks to @sjmarve. That means you are able to define action buttons per instance of a data table without depending on a globally defined component. 25 | 26 | Use scoped slot in parent when defining the actions [Vue Doc for scopped Slots](https://vuejs.org/v2/guide/components.html#Scoped-Slots) 27 | 28 | e.g. 29 | ```html 30 | 36 | ``` 37 | 38 | the onClick function can now be defined in the parent and the parent has Access to rowData and rowIndex via props. :) 39 | 40 | The original functionality still works 41 | 42 | # Breaking Changes 43 | ## v1.6.0 44 | - The `icons` prop of VuetablePagination is now moved into the `css` prop object. See this [codepen](https://codepen.io/ratiw/pen/GmJayw). 45 | 46 | # Example Code 47 | - Clone the project 48 | - Go into the cloned directory 49 | - `npm install` 50 | - `npm run dev` 51 | - Open browser to `http://localhost:8080` 52 | 53 | # Usage 54 | ## NPM 55 | 56 | ```shell 57 | npm install vuetable-2 --save-dev 58 | ``` 59 | 60 | ## Javascript via CDN 61 | Thanks to @cristijora for providing helps on this. 62 | ```html 63 | // vuetable-2 dependencies 64 | 65 | 66 | // vuetable-2 67 | 68 | Vue.use(Vuetable) 69 | ``` 70 | This is demonstrated in this [jsfiddle](http://jsfiddle.net/CristiJ/z11fe07p/1318/). 71 | 72 | The `.use` from above will register all the components globally. 73 | ```javascript 74 | function install(Vue){ 75 | Vue.component("vuetable", Vuetable); 76 | Vue.component("vuetable-pagination", VueTablePaginationInfo); 77 | Vue.component("vuetable-pagination-dropdown", VueTablePaginationDropDown); 78 | Vue.component("vuetable-pagination-info", VueTablePaginationInfo); 79 | } 80 | ``` 81 | 82 | Also you have the ability to access certain components if you need them: 83 | ```javascript 84 | VueTable: VueTable.default/VueTable.VueTable, 85 | VueTablePagination: VueTable.VueTablePagination, 86 | VueTablePaginationInfo: VueTable.VueTablePaginationInfo, 87 | VueTablePaginationDropdown: VueTable.VueTablePaginationDropdown 88 | ``` 89 | 90 | 91 | # Contributions 92 | Any contribution to the code (via pull request would be nice) or any part of the documentation and any idea and/or suggestion are very welcome. 93 | 94 | > __Note__ 95 | > For any bug fix, the PR should be forked from the `master` branch. And for any suggestion or additional feature, the PR should be forked from the `develop` branch, where it can be integrated and rolled out in the next release. 96 | > 97 | > If you are not sure, please ask by openning a new issue. 98 | 99 | However, please do not feel bad if your pull requests or contributions do not get merged or implemented into Vuetable. 100 | 101 | Your contributions can, not only help make Vuetable better, but also push it away from what I intend to use it for. I just hope that you find it useful for your use or learn something useful from its source code. But remember, you can always fork it to make it work the way you want. 102 | 103 | # License 104 | Vuetable is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 105 | -------------------------------------------------------------------------------- /docs/Row-Identifier.md: -------------------------------------------------------------------------------- 1 | Normally, this is the `id` column value of the data/table structure. It is used to uniquely identify a specific row of data in the dataset. 2 | 3 | If you data structure uses different column, you can use `track-by` prop to specify it. -------------------------------------------------------------------------------- /docs/Sorting.md: -------------------------------------------------------------------------------- 1 | # Sorting 2 | 3 | > __Note__ 4 | > This works in ApiMode only! 5 | 6 | If your API endpoint supports sorting, Vuetable can also automatically interact with it when you specify which column in the data is sortable. 7 | 8 | To specify that a particular column is sortable, you add `sortField` option to the field definition of that column. 9 | 10 | ```javascript 11 | { 12 | name: 'email', 13 | sortField: 'email' 14 | } 15 | ``` 16 | 17 | The sortable column will be rendered with `.sortable` CSS class in the `` and it will respond to the click to toggle between 18 | - setting the column as sort order if it was not the current sort order. 19 | - toggle between ascending and descending if it is the current sort order. 20 | 21 | What it does is Vuetable will send a new request to the server endpoint with `sort` parameter specifying which `sortOrder.field` and which `sortOrder.direction` it expects from the server. 22 | 23 | Here is the sample sort parameter in the query string. 24 | ```json 25 | http://api.example.com/users?sort=email|asc 26 | ``` 27 | where, `sort=email|asc` is the `sort` parameter constructed from the default sort order field `email` and the defualt sort direction `asc`. They are concatenated by a pipe character (`|`). 28 | 29 | If your API endpoint uses different construct, you can override this behavior by supplying your own function to construct the sort parameter. See below for more info. 30 | 31 | ### Initial Sorting Order 32 | 33 | To provide initial sorting order, use `sort-order` prop to specify the default sorting column. 34 | 35 | ```html 36 | 40 | ``` 41 | ```javascript 42 | new Vue({ 43 | //... 44 | data: { 45 | //... 46 | sortOrder: [ 47 | { 48 | field: 'email', 49 | direction: 'asc' 50 | } 51 | ] 52 | } 53 | }) 54 | ``` 55 | 56 | ### Multi-column Sorting 57 | 58 | Multi-column sorting can be enabled by setting `multi-sort` prop value to `true`. 59 | 60 | Once enabled, the user can use Alt+Click (Option+Click on mac) to work with multi-column sorting feature. If you would like to use other key than the `alt`, use `multi-sort-key` prop to specify one of the following value 61 | - `alt` -- Alt / Option 62 | - `ctrl` -- Ctrl / Control 63 | - `meta` -- Window / Command 64 | - `shift` -- Shift 65 | 66 | ### Overriding the Sort Query String 67 | 68 | You can override how the sort parameter in the query string is constructed by defining a method named 'getSortParam()' in your main vue instance. 69 | 70 | When Vuetable needs to make a request to the server, it will first check if this method is existed in its parent. If so, it will call this method, passing `sortOrder` prop to it, and will use whatever returned from the method to build the `sort` query string to be sent to the server. 71 | 72 | ```javascript 73 | // If 74 | // sortOrder = [ 75 | // { field: "gender", direction: "desc"}, 76 | // { field: "name", direction: "asc"}, 77 | // ] 78 | // This will return 79 | // '-gender,name' 80 | // 81 | getSortParam: function(sortOrder) { 82 | return sortOrder.map(function(sort) { 83 | return (sort.direction === 'desc' ? '-' : '') + sort.field 84 | }).join(',') 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/Special-Fields.md: -------------------------------------------------------------------------------- 1 | ## Special Fields 2 | 3 | Special fields are predefined field for specific purpose and their names are always started with double underscore characters `__`. 4 | 5 | Vuetable currently have the following special fields 6 | - [__sequence](#-__sequence) 7 | - [__handle](#-__handle) 8 | - [__checkbox](#-__checkbox) 9 | - [__component](#-__componentltnamegt) 10 | - [__slot](#-__slotltnamegt) 11 | 12 | You can see the usage of these special fields in the [Vuetable-2 Tutorial - Using special fields](https://github.com/ratiw/vuetable-2-tutorial/blob/master/doc/lesson-11.md). 13 | 14 | #### # __sequence 15 | 16 | If you would like to display the sequence number of the records based on the pagination information, you can do that using __sequence special field. 17 | 18 | > __Note__ 19 | > __sequence special field only work in API Mode (`:api-mode="true"`) as it 20 | > requires pagination information to work correctly. 21 | 22 | The generated HTML output will be like this 23 | ```html 24 | 25 | ... 26 | 27 | 28 | 29 | ... 30 | 31 | ``` 32 | 33 | #### # __handle 34 | 35 | This special field can be used to display CSS-class icon. 36 | 37 | This allow you to use external library like [Sortable.js]() with Vuetable. 38 | 39 | The generated HTML output will be like this 40 | ```html 41 | 42 | 44 | ``` 45 | 46 | The `css.handleIcon` can be overriden using `css` prop. 47 | 48 | See also: [`css`]() 49 | 50 | #### # __checkbox 51 | 52 | Events emitted 53 | - `vuetable:checkbox-toggled` -- when the user check/uncheck the checkbox in each row 54 | - `vuetable:checkbox-toggled-all` -- when the user check/uncheck the checkbox on the table header 55 | 56 | The `__checkbox` special field will display checkbox column for each row and also on the header row to allow select/unselect all functionality. 57 | 58 | In order for Vuetable to keep track of the checkbox state of each data row. By default, `__checkbox` will use `id` field as [row identifier]() as it usually unique for each row. That also means your data will have to have `id` column. 59 | 60 | But if you do not have `id` column in your data structure, or you want to use another available field that could also uniquely identify each data row, you can use `track-by` prop to specify it. 61 | 62 | For example, if you want to use `item_code` field as you row identifier, you can do so like this. 63 | ```html 64 | 67 | ``` 68 | 69 | The row identifier of the selected row will be store in [`selectedTo`]() prop. If you `ref` to define reference as the above code, you can access it using using Vue's `refs` property. 70 | ```javascript 71 | this.$refs.vuetable.selectedTo 72 | ``` 73 | 74 | The generated HTML output will be like this 75 | ```html 76 | 77 | ... 78 | 79 | 80 | 81 | 82 | ``` 83 | 84 | See also: 85 | - [Row Identifier]() 86 | - [`selectedTo` prop]() 87 | - [`vuetable:checkbox-toggled` event]() 88 | - [`vuetable:checkbox-toggled-all` event]() 89 | 90 | #### # __component:<name> 91 | 92 | The `__component` special field allow you to create a component to handle the display of the row data as you want. 93 | 94 | `` is the name of the component registered globally using `Vue.component` that will be used to handle the rendering of the column data. 95 | 96 | Vuetable will pass 3 props to the component 97 | - `row-data` -- the data of the current row being rendered 98 | - `row-index` -- the current row index 99 | - `row-field` -- the current `sortField` in field definition 100 | 101 | You can see a sample use of `__component` special field in the [Vuetable-2 Tutorial -- Using special fields](https://github.com/ratiw/vuetable-2-tutorial/wiki/lesson-11) 102 | 103 | The generated HTML output will be like this 104 | ```html 105 | 106 | ... 107 | 108 | ... 109 | 110 | ...component code.. 111 | 112 | ``` 113 | 114 | 115 | #### # __slot:<name> 116 | 117 | The `__slot` special field allows you to use Vue's scoped slot inside Vuetable. 118 | 119 | > [Scoped Slots](https://vuejs.org/v2/guide/components.html#Scoped-Slots) is availble in Vue 2.1.0 onward. Please make sure you use at least the specified version of Vue.js. 120 | 121 | Vuetable will pass 3 props to the slot 122 | - `row-data` -- the data of the current row being rendered 123 | - `row-index` -- the current row index 124 | - `row-field` -- the current `sortField` in field definition 125 | 126 | You can see a sample use of `__slot` special field in the [Vuetable-2 Tutorial -- Using special fields](https://github.com/ratiw/vuetable-2-tutorial/blob/master/doc/lesson-11.md) 127 | 128 | The generated HTML output will be like this 129 | ```html 130 | 131 | ... 132 | 133 | ... 134 | 135 | ... 136 | 137 | ``` 138 | -------------------------------------------------------------------------------- /docs/Vuetable-Computed.md: -------------------------------------------------------------------------------- 1 | ## Vuetable - Computed Properties 2 | 3 | - [blankRows](#-blankrows) 4 | - [countTableData](#-counttabledata) 5 | - [countVisibleFields](#-countvisiblefields) 6 | - [displayEmptyDataRow](#-displayemptydatarow) 7 | - [isApiMode](#-isapimode) 8 | - [isDataMode](#-isdatamode) 9 | - [lessThanMinRows](#-lessthanminrows) 10 | - [useDetailRow](#-usedetailrow) 11 | 12 | ### # countTableData 13 | - return: _Number_ 14 | - description 15 | 16 | Return the number of rows for the current data or zero (0) if the `tableData` is `null`. 17 | 18 | ### # countVisibleFields 19 | - return: _Number_ 20 | - description 21 | 22 | Return the number of visible fields in the table by checking the field's `visible` option. 23 | 24 | ### # displayEmptyDataRow 25 | - return: _Boolean_ 26 | - description 27 | 28 | Determine if Vuetable should display empty data row message. 29 | 30 | ### # lessThanMinRows 31 | - return: _Boolean_ 32 | - description 33 | 34 | Determine if the number of data rows available is less than the number specified in `min-rows` prop. 35 | 36 | ### # blankRows 37 | - return: _Number_ 38 | - description 39 | 40 | Return the number of blank rows that Vuetable needs to render. If the number of row data is greater than 41 | or equal to `min-rows`, it returns 0. 42 | 43 | ### # useDetailRow 44 | - return: _Boolean_ 45 | - description 46 | 47 | Determine if detail row should be rendered by inspecting the availability of the data and various properties. 48 | 49 | ### # isApiMode 50 | - return: _Boolean_ 51 | - description 52 | 53 | Determine if Vuetable is currently in API mode. 54 | 55 | ### # isDataMode 56 | - return: _Boolean_ 57 | - description 58 | 59 | Determine if Vuetable is currently in Data mode. 60 | -------------------------------------------------------------------------------- /docs/Vuetable-Data.md: -------------------------------------------------------------------------------- 1 | ## Vuetable - Data 2 | 3 | - [eventPrefix](#-eventprefix) 4 | - [tableFields](#-tablefields) 5 | - [tableData](#-tabledata) 6 | - [tablePagination](#-tablepagination) 7 | - [currentPage](#-currentpage) 8 | - [selectedTo](#-selectedto) 9 | - [visibleDetailRows](#-visibledetailrows) 10 | 11 | ### # eventPrefix 12 | - type: _String_ 13 | - default: `vuetable:` 14 | - description 15 | 16 | The event prefix that Vuetable is going to use when emitting the event. 17 | 18 | ### # tableFields 19 | - type: _Array_ 20 | - default: `[]` _(empty array)_ 21 | - description 22 | 23 | The normalized version of fields definition. This is done during the `created` hook. 24 | 25 | ### # tableData 26 | - type: _Array_ 27 | - default: `null` 28 | - description 29 | 30 | In `api-mode`, this stores the data that returned from the server after the sucessful AJAX request. Otherwise, 31 | it stores the data assigned to via `data` prop or `setData` method. Vuetable always use `tableData` for table 32 | rendering. 33 | 34 | ### # tablePagination 35 | - type: _Object_ 36 | - default: `null` 37 | - description 38 | 39 | If the data returned from the server contains pagination information specified in the `pagination-path`, this is where it gets stored. 40 | 41 | ### # currentPage 42 | - type: _Number_ 43 | - default: `1` 44 | - description 45 | 46 | Vuetable use this to keep track of the current page being diplayed. 47 | 48 | ### # selectedTo 49 | - type: _Array_ 50 | - default: `[]` _(empty array)_ 51 | - description 52 | 53 | When `__checkbox` field option is used and the user selected/unselected any checkbox, its row indentifier is either stored in or remove from here. The row identifier can be specified using `track-by` option. 54 | 55 | ### # visibleDetailRows 56 | - type: _Array_ 57 | - default: `[]` _(empty array)_ 58 | - description 59 | 60 | This stores the row identifier of any row where its detail row is visible. 61 | 62 | -------------------------------------------------------------------------------- /docs/Vuetable-Events.md: -------------------------------------------------------------------------------- 1 | ## Vuetable - Events 2 | 3 | - [vuetable:initialized](#-vuetableinitialized) 4 | - [vuetable:pagination-data](#-vuetablepagination-data) 5 | - [vuetable:loading](#-vuetableloading) 6 | - [vuetable:load-success](#-vuetableload-success) 7 | - [vuetable:load-error](#-vuetableload-error) 8 | - [vuetable:loaded](#-vuetableloaded) 9 | - [vuetable:row-changed](#-vuetablerow-changed) 10 | - [vuetable:row-clicked](#-vuetablerow-clicked) 11 | - [vuetable:row-dblclicked](#-vuetablerow-dblclicked) 12 | - [vuetable:cell-clicked](#-vuetablecell-clicked) 13 | - [vuetable:cell-dblclicked](#-vuetablecell-dblclicked) 14 | - [vuetable:cell-rightclicked](#-vuetablecell-rightclicked) 15 | - [vuetable:detail-row-clicked](#-vuetabledetail-row-clicked) 16 | - [vuetable:checkbox-toggled](#-vuetablecheckbox-toggled) 17 | - [vuetable:checkbox-toggled-all](#-vuetablecheckbox-toggled-all) 18 | - [vuetable:data-reset](#-vuetabledata-reset) 19 | 20 | ### # vuetable:initialized 21 | - payload: 22 | - tableFields: _Array_ -- the normalized fields definition 23 | - description 24 | 25 | This event will be fired after the fields definition have been normalized during the `created` lifecycle hook. 26 | 27 | ### # vuetable:pagination-data 28 | - payload: 29 | - tablePagination: _Object_ -- pagination information 30 | - description 31 | 32 | This event will be fired when the data has sucessfully been retrieved from the server and there is pagination information available. 33 | 34 | ### # vuetable:loading 35 | - payload: _none_ 36 | - description 37 | 38 | This event will be fired before Vuetable starts to request the data from the server. This is useful for triggering the display of loading image. 39 | 40 | ### # vuetable:load-success 41 | - payload: 42 | - response: _Object_ -- the response returned from the server 43 | - description 44 | 45 | This event will be fired when the data was successfully loaded from the server. 46 | 47 | ### # vuetable:load-error 48 | - payload: 49 | - response: _Object_ -- the response returned from the server 50 | - description 51 | 52 | This event will be fired when up the data cannot be retrieved from the server or the server responses with an error. 53 | 54 | ### # vuetable:loaded 55 | - payload: _none_ 56 | - description 57 | 58 | This event will be fired after Vuetable got response back from the server. This event does not indicate whether the request was successful or failed. It just indicates that the request is finished and it got the response back. 59 | 60 | This is useful for ending/hiding the loading image. 61 | 62 | ### # vuetable:row-changed 63 | - payload: 64 | - dataItem: _Object_ -- the current data item 65 | - description 66 | 67 | This event will be fired when Vuetable loops through the data during table row rendering. This will be useful if you want to do some processing with the data, e.g. summing up the values. 68 | 69 | > __Important!__ 70 | > Please note that you MUST NOT change the pass-in data item or try to update any instance data during this event, or it will cause "indefinite update loop". The only way to work inside this event is to use the variable define outside of Vue.js instance 71 | 72 | 73 | ### # vuetable:row-clicked 74 | - payload: 75 | - dataItem: _Object_ -- the current data item 76 | - event: _Event_ -- the MouseObject event 77 | - description 78 | 79 | This event will be fired when the user clicked on any column in the row. You can use the pass-in event object to target the DOM element that you want to manipulate. The pass-in data item argument is the actual data that Vuetable received from the server and it is reactived. Which means if you changed its value, the data displayed in the table will also be changed. 80 | 81 | > Changing the pass-in data in this event will not cause "indefinite update loop" However, the change only affects the current displaying data. It does not change anything on the server side. 82 | 83 | ### # vuetable:row-dblclicked 84 | - payload: 85 | - dataItem: _Object_ -- the current data item 86 | - event: _Event_ -- the MouseObject event 87 | - description 88 | 89 | This event will be fired when the user "double-clicked" on any column in the row. You can use the pass-in event object to target the DOM element that you want to manipulate. The pass-in data item argument is the actual data that Vuetable received from the server and it is reactived. Which means if you changed its value, the data displayed in the table will also be changed. 90 | 91 | > Changing the pass-in data in this event will not cause "indefinite update loop" However, the change only affects the current displaying data. It does not change anything on the server side. 92 | 93 | ### # vuetable:cell-clicked 94 | - payload: 95 | - dataItem: _Object_ -- the current data item 96 | - field: _Object_ -- the field object that causes this event 97 | - event: _Event_ -- the MouseObject event 98 | - description 99 | 100 | This event will be fired when a cell in the tabel body has been clicked. 101 | 102 | 103 | 104 | ### # vuetable:cell-dblclicked 105 | - payload: 106 | - dataItem: _Object_ -- the current data item 107 | - field: _Object_ -- the field object that causes this event 108 | - event: _Event_ -- the MouseObject event 109 | - description 110 | 111 | This event will be fired when a cell in the table body has been double-clicked. 112 | 113 | ### # vuetable:cell-rightclicked 114 | - payload: 115 | - dataItem: _Object_ -- the current data item 116 | - field: _Object_ -- the field object that causes this event 117 | - event: _Event_ -- the MouseObject event 118 | - description 119 | 120 | This event will be fired when a cell in the table body has been right-clicked. 121 | 122 | ### # vuetable:detail-row-clicked 123 | - payload: 124 | - dataItem: _Object_ -- the current data item 125 | - event: _Event_ -- the MouseObject event 126 | - description 127 | 128 | This event will be fired when an area of detail row has been clicked. 129 | 130 | ### # vuetable:checkbox-toggled 131 | - payload: 132 | - isChecked: _Boolean_ -- the state of the checkbox 133 | - dataItem: _Object_ -- the current data item 134 | - description 135 | 136 | This event will be fired whenever the checkbox has been toggled. 137 | 138 | ### # vuetable:checkbox-toggled-all 139 | - payload: 140 | - isChecked: _Boolean_ -- the state of the checkbox 141 | - description 142 | 143 | This event will be fired when the select-all checkbox has been toggled. 144 | 145 | ### # vuetable:data-reset 146 | - payload: _none_ 147 | - description 148 | 149 | This event will be fired as a result from calling `resetData` method to clear in `tableData` and `tablePagination`. 150 | -------------------------------------------------------------------------------- /docs/Vuetable-Methods.md: -------------------------------------------------------------------------------- 1 | ## Vuetable - Methods 2 | 3 | ### fields related 4 | - [normalizeFields](#-normalizeFields) 5 | - [showField](#-showfield) 6 | - [hideField](#-hidefield) 7 | - [toggleField](#-togglefield) 8 | 9 | ### data related 10 | - [setData](#-setdata) 11 | - [reload](#-reload) 12 | - [refresh](#-refresh) 13 | - [resetData](#-resetdata) 14 | - [transform](#-transform) 15 | 16 | ### pagination related 17 | - [gotoPreviousPage](#-gotopreviouspage) 18 | - [gotoNextPage](#-gotonextpage) 19 | - [gotoPage](#-gotopage) 20 | - [changePage](#-changepage) 21 | 22 | ### detail row related 23 | - [isVisibleDetailRow](#-isvisibledetailrow) 24 | - [showDetailRow](#-showdetailrow) 25 | - [hideDetailRow](#-hidedetailrow) 26 | - [toggleDetailRow](#-toggledetailrow) 27 | 28 | ### # normalizeFields 29 | - params: _none_ 30 | - description 31 | 32 | Parse `fields` definition to field objects usable by Vuetable. This method is called automatically "once" during 33 | the `created` life cycle hook. 34 | 35 | If you dynamically change the `fields` prop, you will need to manually call `normalizeFields` method to properly 36 | parse the `fields` definition as Vuetable will not be able to pickup the change and will not work as expected. 37 | 38 | ### # setData 39 | - params: 40 | - data: _Array_ | _Object_ 41 | - description 42 | 43 | You can use this method to manually set the data that Vuetable will be used for table rendering instead of requesting data from the server. 44 | 45 | If the `data` parameter is of type Array, Vuetable will use those array as the data to render the table. 46 | 47 | If the `data` parameter is of type Object, it must be conform to the [Data Structure](#) that Vuetable expects (e.g. contains both data and pagination information). 48 | 49 | > __Note__ 50 | > The method will automatically set `api-mode` to `false`. 51 | 52 | ### # reload 53 | - params: _none_ 54 | - description 55 | 56 | Force Vuetable to reload the data from the server using the current value of parameters. However, the page number will not be reset. 57 | 58 | ### # refresh 59 | - params: _none_ 60 | - description 61 | 62 | Force Vuetable to reload the data from the server and the page number will be reset to 1. It's the same as using goto-page page event to load page 1. 63 | 64 | ### # resetData 65 | - params: _none_ 66 | - description 67 | 68 | This will set `tableData` and `tablePagination` to `null` resulting in not displaying any data in the table (as there is no data to display). This method will also fire `vuetable:data-reset` event which can be captured to force update pagination component accordingly. 69 | 70 | ### # transform 71 | - params: 72 | - data: _Array_ 73 | - must return: _Array_ 74 | - description 75 | 76 | In case, the data returned from the server is not in the format that Vuetable expected, you can define `transform` method on the main Vue instance 77 | and Vuetable will automatically called it with the data from the server. 78 | 79 | The `transform` method must return the array of data that Vuetable expected. 80 | 81 | See also: Data Format (JSON) 82 | 83 | ### # gotoPreviousPage 84 | - params: _none_ 85 | - description 86 | 87 | __** API Mode Only **__ 88 | 89 | This method will automatically request the previous page of data from the server. 90 | 91 | ### # gotoNextPage 92 | - params: _none_ 93 | - description 94 | 95 | __** API Mode Only **__ 96 | 97 | This method will automatically request the next page of data from the server. 98 | 99 | ### # gotoPage 100 | - params: 101 | - page: _Number_ 102 | - description 103 | 104 | __** API Mode Only **__ 105 | 106 | This method will automatically request the specified page of data from the server. 107 | 108 | ### # changePage 109 | - params: 110 | - page: _String_, _Number_ 111 | - description 112 | 113 | __** API Mode Only **__ 114 | 115 | This method will automatically request the specified page of data from the server. You can either pass in the page number, or 'prev' string for previous page, or 'next' string for next page. 116 | 117 | ### # isVisibleDetailRow 118 | - params: 119 | - rowId: _RowIdentifier_ 120 | - description 121 | 122 | Determine if the detail row of the given row identifier is marked as visible. 123 | 124 | See also: [Row Identifier](Row-Identifier) 125 | 126 | ### # showDetailRow 127 | - params: 128 | - rowId: _RowIdentifier_ 129 | - description 130 | 131 | Force displaying the detail row of the given row. 132 | 133 | See also: [Row Identifier](Row-Identifier) 134 | 135 | ### # hideDetailRow 136 | - params: 137 | - rowId: _RowIdentifier_ 138 | - description 139 | 140 | Force hiding the detail row of the given row. 141 | 142 | See also: [Row Identifier](Row-Identifier) 143 | 144 | ### # toggleDetailRow 145 | - params: 146 | - rowId: _RowIdentifier_ 147 | - description 148 | 149 | Toggle the display of the detail row of the given row. 150 | 151 | See also: [Row Identifier](Row-Identifier) 152 | 153 | ### # showField 154 | - params: 155 | - index: _Number_ 156 | - description 157 | 158 | Force displaying the specified field. 159 | 160 | ### # hideField 161 | - params: 162 | - index: _Number_ 163 | - description 164 | 165 | Force hiding the specified field. 166 | 167 | ### # toggleField 168 | - params: 169 | - index: _Number_ 170 | - description 171 | 172 | Toggle display of the specified field. 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/Vuetable-Properties.md: -------------------------------------------------------------------------------- 1 | ## Vuetable - Properties 2 | 3 | - [append-params](#-append-params) 4 | - [api-mode](#-api-mode) 5 | - [api-url](#-api-url) 6 | - [css](#-css) 7 | - [data](#-data) 8 | - data-manager 9 | - [data-path](#-data-path) 10 | - data-total 11 | - [detail-row-component](#-detail-row-component) 12 | - [detail-row-transition](#-detail-row-transition) 13 | - [fields](#-fields) 14 | - [http-fetch](#-http-fetch) 15 | - [http-options](#-http-options) 16 | - [http-method](#-http-method) 17 | - [initial-page](#-initial-page) 18 | - [load-on-start](#-load-on-start) 19 | - [min-rows](#-min-rows) 20 | - [multi-sort](#-multi-sort) 21 | - [multi-sort-key](#-multi-sort-key) 22 | - [no-data-template](#-no-data-template) 23 | - [pagination-path](#-pagination-path) 24 | - [per-page](#-per-page) 25 | - [query-params](#-query-params) 26 | - [reactive-api-url](#-reactive-api-url) 27 | - [render-icon](#-render-icon) 28 | - [row-class](#-row-class) 29 | - [show-sort-icons](#-show-sort-icons) 30 | - [sort-order](#-sort-order) 31 | - [table-height](#-table-height) 32 | - [track-by](#-track-by) 33 | 34 | ### # api-mode 35 | - type: _Boolean_ 36 | - default: `true` 37 | - description 38 | 39 | By default, Vuetable will load the data from and send the request to the API endpoint specified in `api-url`. If you prefer to 40 | supply your own data instead of automatically requesting the data from server, you have to set `api-mode` to `false`. Then, pass 41 | the data via `data` prop. 42 | 43 | Please note that disabling `api-mode` (by setting it to `false`) will also **disable** pagination, sorting, and filtering functions. 44 | 45 | > __Note__ 46 | > Setting `api-mode` to `false` is not recommended. It will not scale well when you have to handle a large dataset 47 | > yourself instead of letting database manager handles it for you. 48 | 49 | ### # api-url 50 | - works in `api-mode` only 51 | - type: _String_ 52 | - default: `''` _(empty string)_ 53 | - description 54 | 55 | The URL of the api endpoint that Vuetable will interact with. If the API supports sorting, filtering, pagination of the data, 56 | Vuetable can automatically append appropriate information to the server via query string. 57 | 58 | ### # reactive-api-url 59 | - works in `api-mode` only 60 | - type: _Boolean_ 61 | - default: `true` 62 | - description 63 | 64 | Tells Vuetable whether it should watch for the change in `api-url` prop and refresh the data automatically. 65 | 66 | ### # fields 67 | - type: _Array_ 68 | - default: _none_ 69 | - required: _true_ 70 | - description 71 | 72 | Array of field definition that Vuetable will be used to map to the data structure in order to render the table for presentation. 73 | 74 | ### # data 75 | - type: _Array_ | _Object_ 76 | - default: `null` 77 | - description 78 | 79 | The data that Vuetable will be used to render the table when `api-mode` is set to `false`. 80 | 81 | __New in v1.7__ 82 | You can utilize the pagination functionality of Vuetable if the `data` you supplied is an object that has data structure as described in [Data Format](https://github.com/ratiw/vuetable-2/wiki/Data-Format-(JSON)). 83 | 84 | > __Note__ 85 | > The prop only works when `api-mode` is `false`. 86 | 87 | ### # data-manager (v1.7) 88 | - type: _Function_ 89 | - default: `null` 90 | - description 91 | 92 | [TODO] 93 | 94 | > __Note__ 95 | > The prop only works when `api-mode` is `false`. 96 | 97 | ### # data-path 98 | - works in `api-mode` only 99 | - type: _String_ 100 | - default: `data` 101 | - description 102 | 103 | The path inside the data structure that actually contains the data. If the data is at the root of the structure, set 104 | this prop to empty string, e.g. `data-path=""`. 105 | 106 | ### # data-total (v1.7) 107 | - type: _Number_ 108 | - default: 109 | - description 110 | 111 | [TODO] 112 | 113 | > __Note__ 114 | > The prop only works when `api-mode` is `false`. 115 | 116 | ### # pagination-path 117 | - works in `api-mode` only 118 | - type: _String_ 119 | - default: `links.pagination` 120 | - description 121 | 122 | The pagination path inside the data structure that contains the pagination information. If the your data from the server 123 | does not have pagination information, you should set the prop to empty string, e.g. `pagination-path=""`, to suppress 124 | Vuetable warning. 125 | 126 | ### # load-on-start 127 | - works in `api-mode` only 128 | - type: _Boolean_ 129 | - default: `true` 130 | - required: _true_ 131 | - description 132 | 133 | Whether Vuetable should immediately load the data from the server. 134 | 135 | ### # append-params 136 | - works in `api-mode` only 137 | - type: _Object_ 138 | - default: `{}` _(empty object)_ 139 | - description 140 | 141 | Additional parameters that Vuetable should append to the query string when requesting data from the server. 142 | 143 | See also: [Appending Other Parameters to the Query String](#) 144 | 145 | ### # query-params 146 | - works in `api-mode` only 147 | - type: _Object_ 148 | - default: 149 | ``` 150 | { 151 | sort: 'sort', 152 | page: 'page', 153 | perPage: 'per_page' 154 | } 155 | ``` 156 | - description 157 | 158 | The text to be used as keys in query string that will be sent to the server. If your API endpoint uses different keys, you can 159 | specified them via this prop. 160 | 161 | See also: [Sorting, Paging, and Page Sizing of Data](#) 162 | 163 | ### # http-fetch (v1.7) 164 | - works in `api-mode` only 165 | - type: _Function_ 166 | - default: `null` 167 | - description: 168 | 169 | Allow specifying external http request function to fetch the data via AJAX. If `null`, Vuetable will fallback to using `axios` internally. 170 | 171 | If specified, Vuetable will call the given function passing `apiUrl` and already constructed `httpOptions` to the function. 172 | 173 | Vuetable would expect the given function to make an AJAX call to the `apiUrl` with the `httpOptions` and return a [_Promise_](https://developer.mozilla.org/th/docs/Web/JavaScript/Reference/Global_Objects/Promise). Vuetable would then handle the success or failure of the AJAX call after the Promise has been resolved. 174 | 175 | See `loadData` and `fetch` methods in `Vuetable.vue` source code if you're confused. 176 | 177 | Here's an example using `vue-resource` 178 | ```javascript 179 | //... 180 | myFetch(apiUrl, httpOptions) { 181 | return Vue.$http.get(apiUrl, httpOptions) 182 | }, 183 | //... 184 | ``` 185 | 186 | ### # http-options 187 | - works in `api-mode` only 188 | - type: _Object_ 189 | - default: `{}` _(empty object)_ 190 | - description 191 | 192 | Allow passing additional options to the server during AJAX call. Internally, Vuetable uses [`axios`](https://github.com/mzabriskie/axios) 193 | to handle AJAX request. 194 | 195 | ### # http-method 196 | - works in `api-mode` only 197 | - type: _String_ 198 | - default: `get` 199 | - description 200 | 201 | Only support `get` or `post` method. Please note that it must be the __lowercase__ string. 202 | 203 | ### # track-by 204 | - type: _String_ 205 | - default: `id` 206 | - description 207 | 208 | The key that uses to unqiuely identified each row in the data array to help track the state of detail row and checkbox 209 | features of Vuetable. This is necessary for the detail row and checkbox features to function correctly. 210 | 211 | For detail row feature, whenever the user click to expand the detail row, Vuetable will insert the `id` of that row into 212 | its internal array (`visibleDetailRows`). And when that detail row is hidden, the `id` of that detail row is removed from 213 | the array. 214 | 215 | For checkbox feature, when the user select (checked) a row, Vuetable will insert the `id` of the row into its internal 216 | array (`selectedTo`). And when that row is unselected (unchecked), the `id` of that row is removed from the array. 217 | 218 | See also: [`visibleDetailRows`](#), and [`selectedTo`](#) 219 | 220 | ### # show-sort-icons 221 | - type: _Boolean_ 222 | - default: `true` 223 | - description 224 | 225 | Tells Vuetable whether or not icons should be added to `th` elements whenever a given column is used to sort. 226 | 227 | ### # sort-order 228 | - works in `api-mode` only 229 | - type: _Array_ 230 | - default: `[]` _(empty array)_ 231 | - description 232 | 233 | The default sort order that Vuetable should use when requesting the data from server. 234 | 235 | ### # multi-sort 236 | - works in `api-mode` only 237 | - type: _Boolean_ 238 | - default: `false` 239 | - description 240 | 241 | Enable multiple sort columns interaction. 242 | 243 | ### # multi-sort-key 244 | - works in `api-mode` only 245 | - type: _String_ 246 | - default: `alt` 247 | - possible values: `alt`, `ctrl`, `shift`, `meta` 248 | - description 249 | 250 | The key to trigger sort colum adding/subtracting when multi-sort is enabled. 251 | 252 | ### # per-page 253 | - works in `api-mode` only 254 | - type: _Number_ 255 | - default: `10` 256 | - description 257 | 258 | The number of data to be requested per page. 259 | 260 | ### # initial-page (since v1.7) 261 | - works in `api-mode` only 262 | - type: _Number_ 263 | - default: `1` 264 | - description 265 | 266 | The initial page number of data to be requested on the first time. 267 | 268 | ### # css 269 | - type: _Object_ 270 | - default: 271 | ``` 272 | { 273 | tableClass: 'ui blue selectable celled stackable attached table', 274 | loadingClass: 'loading', 275 | ascendingIcon: 'blue chevron up icon', 276 | descendingIcon: 'blue chevron down icon', 277 | detailRowClass: 'vuetable-detail-row', 278 | handleIcon: 'grey sidebar icon', 279 | sortableIcon: '' // since v1.7 280 | } 281 | ``` 282 | - description 283 | 284 | This is where you should override the CSS classes that Vuetable uses to render HTML table that should help you style the table 285 | to your needs. 286 | 287 | See also: [CSS Styling](#) 288 | 289 | ### # render-icon 290 | - type: _Function_ 291 | - default: `null` 292 | - description 293 | 294 | By default, Vuetable uses `` tag to render icon according to Semantic UI CSS. But you can override this by providing your own icon rendering callback function via this `render-icon` property. 295 | 296 | The following icon rendering in Vuetable will use this provided callback instead of the default one if provided. 297 | - sort icon on the table header `` 298 | - handle icon of the table column `` 299 | 300 | The callback will receive two parameters. 301 | 1) the array of CSS `classes` 302 | 2) the optional string `options` expected to be appended to the tag 303 | 304 | See example below: 305 | ```vue 306 | 310 | ``` 311 | ```javascript 312 | //... 313 | methods: { 314 | renderBootstrapIcon (classes, options) { 315 | return `` 316 | } 317 | } 318 | ``` 319 | 320 | ### # min-rows 321 | - type: _Number_ 322 | - default: `0` 323 | - description 324 | 325 | The minimum number of rows that should be displayed when rendering the table. 326 | 327 | If the number of row available is less than the number specified in `min-rows` prop, Vuetable will render empty table rows to 328 | satisfy that minimum rows. 329 | 330 | > __Note__ 331 | > The prop only works when `api-mode` is `false`. 332 | 333 | ### # row-class 334 | - type: _String_, _Function_ 335 | - default: `''` _(empty string)_ 336 | - description 337 | 338 | The CSS class name that will be applied to each table row. 339 | 340 | If `row-class` prop refers to a method, Vuetable will automatically call the given method on each row, passing the row data and row index to it. Vuetable will then use the returned string from the given method as CSS class for that row. 341 | 342 | Here is the example on using a method to return the row class for styling. 343 | ```vue 344 | 349 | 357 | ``` 358 | 359 | ### # detail-row-component 360 | - type: _String_ 361 | - default: `''` _(empty string)_ 362 | - description 363 | 364 | A component name to be used as the content of detail row to be displayed underneath the current row. 365 | 366 | ### # detail-row-transition 367 | - type: _String_ 368 | - default: `''` _(empty string)_ 369 | - description 370 | 371 | The CSS class to apply to detail row during transition. 372 | 373 | ### # no-data-template 374 | - type: _String_ 375 | - default: `''` _(empty string)_ 376 | - description 377 | 378 | Template when there are no records in the table. Inserted into table cell `td` 379 | 380 | ### # table-height 381 | - type: _String_ 382 | - default: `null` 383 | - description 384 | 385 | When set will enable Vuetable to generate Fixed Header table where if the table body's rows exceeding the specified table height, the table body will be scrollable but the table will stay fixed at the top. 386 | 387 | The fixed header is another HTML table which contain only the header, so table with fixed header should specify width for each column in the [fields definition]. 388 | 389 | To disable fixed header, just set `table-height` prop to `null`. 390 | 391 | - see also: [Fixed Header](Fixed-Header) 392 | -------------------------------------------------------------------------------- /docs/VuetablePagination.md: -------------------------------------------------------------------------------- 1 | ## VuetablePagination 2 | 3 | This component contains only the template and utilizes what are available in `VuetablePaginationMixin`. 4 | 5 | You can use this component as a guide to create your own pagination component without having to implement additional properties or methods. If needed, you can always add properties and/or methods like `VuetablePaginationDropdown` did. 6 | 7 | ### Uses 8 | - [VuetablePaginationMixin](VuetablePaginationMixin) 9 | 10 | -------------------------------------------------------------------------------- /docs/VuetablePaginationDropdown.md: -------------------------------------------------------------------------------- 1 | ## VuetablePaginationDropdown 2 | 3 | `VuetablePaginationDropdown` uses `VuetablePaginationMixin`, so all properties, computed properties, methods, and events in `VuetablePaginationMixin` are also available in this class. 4 | 5 | ### Uses 6 | - [VuetablePaginationMixin](VuetablePaginationMixin) 7 | 8 | ### Properties 9 | #### # page-text 10 | - type: _String_ 11 | - default: `Page` 12 | - description 13 | 14 | The text to be displayed in the select page dropdown. 15 | 16 | ### Methods 17 | No additional methods 18 | 19 | ### Events 20 | 21 | #### # vuetable-pagination:change-page 22 | - See [VuetablePaginationMixin](VuetablePaginationMixin#-vuetable-paginationchange-page) 23 | 24 | #### # vuetable-pagination:pagination-data 25 | - type: _listen_ 26 | - params: 27 | - tablePagination: _Object_ -- pagination information 28 | - description: 29 | 30 | This event will have pagination information in its payload that will be used for rendering of pagination component. 31 | -------------------------------------------------------------------------------- /docs/VuetablePaginationInfo.md: -------------------------------------------------------------------------------- 1 | ## VuetablePaginationInfo - Properties 2 | 3 | ### Uses 4 | - [VuetablePaginationInfoMixin](VuetablePaginationInfoMixin) 5 | 6 | ### Properties 7 | 8 | _None_ 9 | 10 | ### Computed 11 | 12 | _None_ 13 | 14 | ### Data 15 | 16 | _None_ 17 | 18 | ### Methods 19 | 20 | _None_ 21 | 22 | ### Events 23 | 24 | _None_ 25 | 26 | -------------------------------------------------------------------------------- /docs/VuetablePaginationInfoMixin.md: -------------------------------------------------------------------------------- 1 | ## VuetablePaginationInfoMixin 2 | 3 | ### Properties 4 | 5 | #### # css 6 | - type: _Object_ 7 | - default: 8 | ``` 9 | { 10 | infoClass: 'left floated left aligned six wide column' 11 | } 12 | ``` 13 | - description: 14 | 15 | The `css` property holds most of the CSS classes that VuetablePaginationInfo uses in its template. 16 | 17 | #### # info-template 18 | - type: _String_ 19 | - default: `Displaying {from} to {to} of {total} items` 20 | - description: 21 | 22 | The template string to be used to display the pagination information. 23 | 24 | Available placeholders 25 | - {from} the starting record number displayed 26 | - {to} the ending record number displayed 27 | - {total} the total number of records available 28 | 29 | #### # no-data-template 30 | - type: _Object_ 31 | - default: `No relevant data` 32 | - description: 33 | 34 | The template string to be showned when there is no data to display. 35 | 36 | ### Data 37 | 38 | #### # tablePagination 39 | - type: _Object_ 40 | - default: `null` 41 | 42 | The pagination information received from Vuetable. 43 | 44 | ### Computed 45 | 46 | #### # paginationInfo 47 | - params: _none_ 48 | - return: _String_ 49 | 50 | The actual pagination information to be displayed after replacing all the placeholders with corresponding values. 51 | 52 | ### Methods 53 | 54 | #### # setPaginationData 55 | - params: 56 | - tablePagination: _Object_ -- pagination information 57 | - description 58 | 59 | Setting the `tablePagination` data to be used when rendering pagination component. 60 | 61 | #### # resetData 62 | - params: _none_ 63 | - description 64 | 65 | This method will set `tablePagination` to null. 66 | 67 | ### Events 68 | 69 | _None_ 70 | -------------------------------------------------------------------------------- /docs/VuetablePaginationMixin.md: -------------------------------------------------------------------------------- 1 | ## VuetablePaginationMixin 2 | 3 | This mixin provides sliding style pagination functionality where the current page (when not at the first or last position) is always in the middle of pagination list. 4 | 5 | ### Properties 6 | 7 | #### # css 8 | - type: _Object_ 9 | - default: 10 | ```javascript 11 | { 12 | wrapperClass: 'ui right floated pagination menu', 13 | activeClass: 'active large', 14 | disabledClass: 'disabled', 15 | pageClass: 'item', 16 | linkClass: 'icon item', 17 | paginationClass: 'ui bottom attached segment grid', 18 | paginationInfoClass: 'left floated left aligned six wide column', 19 | dropdownClass: 'ui search dropdown', 20 | icons: { 21 | first: 'angle double left icon', 22 | prev: 'left chevron icon', 23 | next: 'right chevron icon', 24 | last: 'angle double right icon', 25 | } 26 | } 27 | ``` 28 | - description 29 | 30 | The `css` property holds most of the CSS classes that VuetablePagination uses in its template. 31 | 32 | #### # on-each-side 33 | - type: _Number_ 34 | - default: `2` 35 | - description 36 | 37 | The value of this property specifies the slide size on each side of the center. 38 | 39 | ### Computed 40 | 41 | #### # totalPage 42 | - return: _Number_ 43 | - description 44 | 45 | The total number of available pages. This value is taken from the `last_page` field of pagination information. 46 | 47 | #### # isOnFirstPage 48 | - return: _Boolean_ 49 | - description 50 | 51 | Returns `true` if the current page number is the first page; otherwise, returns `false`. 52 | 53 | #### # isOnLastPage 54 | - return: _Boolean_ 55 | - description 56 | 57 | Returns `true` if the current page number is the last page; otherwise, returns `false`. 58 | 59 | #### # notEnoughPages 60 | - return: _Boolean_ 61 | - description 62 | 63 | Determine if the total number of pages is enough to be displayed without sliding. 64 | 65 | #### # windowSize 66 | - return: _Number_ 67 | - description 68 | 69 | The size of the sliding window calculating from `on-each-side` * 2 + 1. 70 | 71 | #### # windowStart 72 | - return: _Number_ 73 | - description 74 | 75 | Return the first page number to be shown on the leftmost. 76 | 77 | ### Data 78 | #### # tablePagination 79 | - type: _Object_ 80 | - default: `null` 81 | 82 | The pagination information received from Vuetable. 83 | 84 | ### Methods 85 | 86 | #### # isCurrentPage 87 | - params: 88 | - page: _Number_ 89 | - description 90 | 91 | Determine if the given page number is the current page. 92 | 93 | #### # setPaginationData 94 | - params: 95 | - tablePagination: _Object_ -- pagination information 96 | - description 97 | 98 | Setting the `tablePagination` data to be used when rendering pagination component. 99 | 100 | #### # resetData 101 | - params: _none_ 102 | - description 103 | 104 | This method will set `tablePagination` to null. 105 | 106 | ### Events 107 | 108 | #### # vuetable-pagination:change-page 109 | - type: _emit_ 110 | - payload: 111 | - page: _Number_, _String_ 112 | - description: 113 | 114 | This event was fired when the user clicks on any pagination item requesting data for that given page number. 115 | -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - Vuetable 4 | - [Fields Definition](Fields-Definition) 5 | - [Callback / Formatter](Callbacks) 6 | - [Special Fields](Special-Fields) 7 | - [Row Identifier](Row-Identifier) 8 | - [Data Format JSON](Data-Format-JSON) 9 | - [Sorting](Sorting) 10 | - [Overriding Default Query String](Overriding-Default-Query-String) 11 | - [Data Transformation](Data-Transformation) 12 | - [Pagination](Pagination) 13 | - [Detail Row](Detail-Row) 14 | - [Fixed Header](Fixed-Header) 15 | - [CSS Styling](CSS-Styling) 16 | - [Tutorial](https://github.com/ratiw/vuetable-2-tutorial/wiki) 17 | - [FAQ](FAQ) 18 | 19 | - Components API 20 | - Vuetable 21 | + [Properties](Vuetable-Properties) 22 | + [Computed properties](Vuetable-Computed) 23 | + [Data](Vuetable-Data) 24 | + [Methods](Vuetable-Methods) 25 | + [Events](Vuetable-Events) 26 | 27 | - [VuetablePagination](VuetablePagination) 28 | 29 | - [VuetablePaginationMixin](VuetablePaginationMixin) 30 | 31 | - [VuetablePaginationDropdown](VuetablePaginationDropdown) 32 | 33 | - [VuetablePaginationInfo](VuetablePaginationInfo) 34 | 35 | - [VuetablePaginationInfoMixin](VuetablePaginationInfoMixin) 36 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | vue2 8 | 9 | 10 | 68 | 69 | 70 | 71 | 72 |
73 |
74 | 75 |
76 | 77 |
78 | 79 |

List of Users

80 | 81 |
82 |
83 |
84 |
Search:
85 | 86 | 87 |
88 | 89 | 90 |
91 |
92 | 96 |
97 |
98 | 99 |
100 | 101 | 121 |
122 | 125 | 128 |
129 | 130 |
131 | 132 | 135 | 136 |
137 | 138 |
139 |
140 | 141 |
142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuetable-2", 3 | "version": "1.7.5", 4 | "description": "Datatable component for Vue 2.x", 5 | "main": "dist/vuetable-2.js", 6 | "license": "MIT", 7 | "author": "Rati Wannapanop ", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ratiw/vuetable-2.git" 11 | }, 12 | "private": false, 13 | "keywords": [ 14 | "vue", 15 | "component", 16 | "table", 17 | "datatable", 18 | "pagination-components", 19 | "json" 20 | ], 21 | "browserify": { 22 | "transform": [ 23 | "babelify", 24 | [ 25 | "vueify", 26 | { 27 | "_flags": { 28 | "debug": true 29 | } 30 | } 31 | ] 32 | ] 33 | }, 34 | "scripts": { 35 | "dev": "node build/dev-server.js", 36 | "start": "node build/dev-server.js", 37 | "build": "node build/build.js && npm run bundle", 38 | "bundle:min": "cross-env MINIFY=true webpack --progress --config build/webpack.build.config.js", 39 | "bundle:full": "cross-env MINIFY=false webpack --progress --config build/webpack.build.config.js", 40 | "bundle": "npm run bundle:min && npm run bundle:full", 41 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 42 | "e2e": "node test/e2e/runner.js", 43 | "test": "npm run unit && npm run e2e" 44 | }, 45 | "dependencies": { 46 | "axios": "^0.15.3" 47 | }, 48 | "devDependencies": { 49 | "autoprefixer": "^6.7.2", 50 | "babel-core": "^6.22.1", 51 | "babel-loader": "^6.2.10", 52 | "babel-plugin-istanbul": "^4.1.1", 53 | "babel-plugin-transform-runtime": "^6.22.0", 54 | "babel-polyfill": "^6.23.0", 55 | "babel-preset-env": "^1.3.2", 56 | "babel-preset-stage-2": "^6.22.0", 57 | "babel-register": "^6.22.0", 58 | "babel-runtime": "^6.0.0", 59 | "babelify": "^7.3.0", 60 | "chai": "^3.5.0", 61 | "chalk": "^1.1.3", 62 | "connect-history-api-fallback": "^1.3.0", 63 | "copy-webpack-plugin": "^4.0.1", 64 | "cross-env": "^4.0.0", 65 | "cross-spawn": "^5.0.1", 66 | "css-loader": "^0.28.0", 67 | "eventsource-polyfill": "^0.9.6", 68 | "express": "^4.14.1", 69 | "extract-text-webpack-plugin": "^2.0.0", 70 | "file-loader": "^0.11.1", 71 | "friendly-errors-webpack-plugin": "^1.1.3", 72 | "html-webpack-plugin": "^2.28.0", 73 | "http-proxy-middleware": "^0.17.3", 74 | "inject-loader": "^3.0.0", 75 | "isparta-loader": "^2.0.0", 76 | "json-loader": "^0.5.4", 77 | "karma": "^1.4.1", 78 | "karma-coverage": "^1.1.1", 79 | "karma-mocha": "^1.3.0", 80 | "karma-phantomjs-launcher": "^1.0.2", 81 | "karma-phantomjs-shim": "^1.4.0", 82 | "karma-sinon-chai": "^1.3.1", 83 | "karma-sourcemap-loader": "^0.3.7", 84 | "karma-spec-reporter": "0.0.30", 85 | "karma-webpack": "^2.0.2", 86 | "lolex": "^1.5.2", 87 | "mocha": "^3.2.0", 88 | "nightwatch": "^0.9.12", 89 | "opn": "^4.0.2", 90 | "optimize-css-assets-webpack-plugin": "^1.3.0", 91 | "ora": "^1.2.0", 92 | "phantomjs-prebuilt": "^2.1.3", 93 | "promise-polyfill": "^6.0.2", 94 | "rimraf": "^2.6.0", 95 | "selenium-server": "^3.0.1", 96 | "semver": "^5.3.0", 97 | "shelljs": "^0.7.6", 98 | "sinon": "^2.1.0", 99 | "sinon-chai": "^2.8.0", 100 | "stats-webpack-plugin": "0.4.3", 101 | "url-loader": "^0.5.8", 102 | "vue": "^2.5.2", 103 | "vue-hot-reload-api": "^1.2.0", 104 | "vue-html-loader": "1.2.3", 105 | "vue-loader": "^12.1.0", 106 | "vue-style-loader": "^3.0.1", 107 | "vue-template-compiler": "^2.5.2", 108 | "vueify": "^9.4.1", 109 | "webpack": "^2.6.1", 110 | "webpack-bundle-analyzer": "^2.2.1", 111 | "webpack-dev-middleware": "^1.10.0", 112 | "webpack-hot-middleware": "^2.18.0", 113 | "webpack-merge": "^4.1.0" 114 | }, 115 | "engines": { 116 | "node": ">= 4.0.0", 117 | "npm": ">= 3.0.0" 118 | }, 119 | "browserslist": [ 120 | "> 1%", 121 | "last 2 versions", 122 | "not ie <= 8" 123 | ] 124 | } 125 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratiw/vuetable-2/1f01bcf540e3c799dd4fe4d064c9af45784ca481/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Vuetable.vue: -------------------------------------------------------------------------------- 1 | 310 | 311 | 1295 | 1296 | 1345 | -------------------------------------------------------------------------------- /src/components/VuetablePagination.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 49 | -------------------------------------------------------------------------------- /src/components/VuetablePaginationDropdown.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 46 | -------------------------------------------------------------------------------- /src/components/VuetablePaginationInfo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/components/VuetablePaginationInfoMixin.vue: -------------------------------------------------------------------------------- 1 | 52 | -------------------------------------------------------------------------------- /src/components/VuetablePaginationMixin.vue: -------------------------------------------------------------------------------- 1 | 86 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Vuetable from './components/Vuetable.vue' 2 | import VuetablePagination from './components/VuetablePagination.vue' 3 | import VuetablePaginationDropDown from './components/VuetablePaginationDropdown.vue' 4 | import VuetablePaginationInfo from './components/VuetablePaginationInfo.vue' 5 | import VuetablePaginationMixin from './components/VuetablePaginationMixin.vue' 6 | import VuetablePaginationInfoMixin from './components/VuetablePaginationInfoMixin.vue' 7 | import Promise from 'promise-polyfill' 8 | 9 | if (!window.Promise) { 10 | window.Promise = Promise 11 | } 12 | 13 | function install(Vue){ 14 | Vue.component("vuetable", Vuetable); 15 | Vue.component("vuetable-pagination", VuetablePagination); 16 | Vue.component("vuetable-pagination-dropdown", VuetablePaginationDropDown); 17 | Vue.component("vuetable-pagination-info", VuetablePaginationInfo); 18 | } 19 | export { 20 | Vuetable, 21 | VuetablePagination, 22 | VuetablePaginationDropDown, 23 | VuetablePaginationInfo, 24 | VuetablePaginationMixin, 25 | VuetablePaginationInfoMixin, 26 | install 27 | }; 28 | 29 | export default Vuetable; 30 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuetable from './components/Vuetable.vue' 3 | import VuetablePagination from './components/VuetablePagination.vue' 4 | import VuetablePaginationDropdown from './components/VuetablePaginationDropdown.vue' 5 | import VuetablePaginationInfo from './components/VuetablePaginationInfo.vue' 6 | import axios from 'axios' 7 | 8 | let E_SERVER_ERROR = 'Error communicating with the server' 9 | 10 | Vue.component('custom-actions', { 11 | template: [ 12 | '
', 13 | '', 14 | '', 15 | '', 16 | '
' 17 | ].join(''), 18 | props: { 19 | rowData: { 20 | type: Object, 21 | required: true 22 | } 23 | }, 24 | methods: { 25 | onClick (action, data) { 26 | console.log('actions: on-click', data.name) 27 | sweetAlert(action, data.name) 28 | }, 29 | } 30 | }) 31 | 32 | Vue.component('my-detail-row', { 33 | template: [ 34 | '
', 35 | '
', 36 | '', 37 | '{{rowData.name}}', 38 | '
', 39 | '
', 40 | '', 41 | '{{rowData.email}}', 42 | '
', 43 | '
', 44 | '', 45 | '{{rowData.nickname}}', 46 | '
', 47 | '
', 48 | '', 49 | '{{rowData.birthdate}}', 50 | '
', 51 | '
', 52 | '', 53 | '{{rowData.gender}}', 54 | '
', 55 | '
' 56 | ].join(''), 57 | props: { 58 | rowData: { 59 | type: Object, 60 | required: true 61 | } 62 | }, 63 | methods: { 64 | onClick (event) { 65 | console.log('my-detail-row: on-click', event.target) 66 | } 67 | }, 68 | }) 69 | 70 | Vue.component('settings-modal', { 71 | template: ` 72 | 127 | `, 128 | props: ['vuetableFields'], 129 | data () { 130 | return { 131 | } 132 | }, 133 | methods: { 134 | getFieldTitle (field) { 135 | if (typeof(field.title) === 'function') return field.title(true) 136 | 137 | let title = field.title 138 | if (title !== '') return this.stripHTML(title) 139 | 140 | title = '' 141 | if (field.name.slice(0, 2) === '__') { 142 | title = field.name.indexOf(':') >= 0 143 | ? field.name.split(':')[1] 144 | : field.name.replace('__', '') 145 | } 146 | 147 | return title 148 | }, 149 | stripHTML (str) { 150 | return str ? str.replace(/(<([^>]+)>)/ig,"") : '' 151 | }, 152 | toggleField (index, event) { 153 | console.log('toggleField: ', index, event.target.checked) 154 | this.$parent.$refs.vuetable.toggleField(index) 155 | }, 156 | setTableHeight (event) { 157 | if (event.target.checked) { 158 | this.$parent.tableHeight = '600px' 159 | return 160 | } 161 | 162 | this.$parent.tableHeight = null 163 | } 164 | } 165 | }) 166 | 167 | let lang = { 168 | 'nickname': 'Nickname', 169 | 'birthdate': 'Birthdate', 170 | } 171 | 172 | let tableColumns = [ 173 | { name: '__handle', 174 | width: '50px' 175 | }, 176 | { 177 | name: '__sequence', 178 | title: 'No.', 179 | titleClass: 'right aligned', 180 | dataClass: 'right aligned', 181 | width: '50px' 182 | }, 183 | { 184 | name: '__checkbox', 185 | width: '30px', 186 | title: 'checkbox', 187 | titleClass: 'center aligned', 188 | dataClass: 'center aligned' 189 | }, 190 | { 191 | name: 'id', 192 | title: ' Detail', 193 | dataClass: 'center aligned', 194 | width: '100px', 195 | callback: 'showDetailRow' 196 | 197 | }, 198 | { 199 | name: 'name', 200 | title: ' Full Name', 201 | sortField: 'name', 202 | width: '150px' 203 | }, 204 | { 205 | name: 'email', 206 | title: ' Email', 207 | sortField: 'email', 208 | width: '200px', 209 | dataClass: "vuetable-clip-text", 210 | visible: true 211 | }, 212 | { 213 | name: 'nickname', 214 | title: (nameOnly = false) => { 215 | return nameOnly 216 | ? lang['nickname'] 217 | : ` ${lang['nickname']}` 218 | }, 219 | sortField: 'nickname', 220 | callback: 'allCap', 221 | width: '120px' 222 | }, 223 | { 224 | name: 'birthdate', 225 | title: (nameOnly = false) => { 226 | return nameOnly 227 | ? lang['birthdate'] 228 | : ` ${lang['birthdate']}` 229 | }, 230 | sortField: 'birthdate', 231 | width: '100px', 232 | callback: 'formatDate|D/MM/Y' 233 | }, 234 | { 235 | name: 'gender', 236 | title: 'Gender', 237 | sortField: 'gender', 238 | titleClass: 'center aligned', 239 | dataClass: 'center aligned', 240 | callback: 'gender', 241 | width: '100px', 242 | }, 243 | { 244 | name: '__component:custom-actions', 245 | title: 'Actions', 246 | titleClass: 'center aligned', 247 | dataClass: 'center aligned', 248 | width: '150px' 249 | } 250 | ] 251 | 252 | /* eslint-disable no-new */ 253 | let vm = new Vue({ 254 | el: '#app', 255 | components: { 256 | Vuetable, 257 | VuetablePagination, 258 | VuetablePaginationDropdown, 259 | VuetablePaginationInfo, 260 | }, 261 | data: { 262 | loading: '', 263 | searchFor: '', 264 | moreParams: { aa: 1111, bb: 222 }, 265 | fields: tableColumns, 266 | tableHeight: '600px', 267 | vuetableFields: false, 268 | sortOrder: [{ 269 | field: 'name', 270 | direction: 'asc', 271 | }], 272 | multiSort: true, 273 | paginationComponent: 'vuetable-pagination', 274 | perPage: 10, 275 | paginationInfoTemplate: 'Showing record: {from} to {to} from {total} item(s)', 276 | lang: lang, 277 | }, 278 | watch: { 279 | 'perPage' (val, oldVal) { 280 | this.$nextTick(function() { 281 | this.$refs.vuetable.refresh() 282 | }) 283 | }, 284 | 'paginationComponent' (val, oldVal) { 285 | this.$nextTick(function() { 286 | this.$refs.pagination.setPaginationData(this.$refs.vuetable.tablePagination) 287 | }) 288 | } 289 | }, 290 | methods: { 291 | transform (data) { 292 | let transformed = {} 293 | transformed.pagination = { 294 | total: data.total, 295 | per_page: data.per_page, 296 | current_page: data.current_page, 297 | last_page: data.last_page, 298 | next_page_url: data.next_page_url, 299 | prev_page_url: data.prev_page_url, 300 | from: data.from, 301 | to: data.to 302 | } 303 | 304 | transformed.data = [] 305 | data = data.data 306 | for (let i = 0; i < data.length; i++) { 307 | transformed['data'].push({ 308 | id: data[i].id, 309 | name: data[i].name, 310 | nickname: data[i].nickname, 311 | email: data[i].email, 312 | age: data[i].age, 313 | birthdate: data[i].birthdate, 314 | gender: data[i].gender, 315 | address: data[i].address.line1 + ' ' + data[i].address.line2 + ' ' + data[i].address.zipcode 316 | }) 317 | } 318 | 319 | return transformed 320 | }, 321 | showSettingsModal () { 322 | let self = this 323 | $('#settingsModal').modal({ 324 | detachable: true, 325 | onVisible () { 326 | $('.ui.checkbox').checkbox() 327 | } 328 | }).modal('show') 329 | }, 330 | showLoader () { 331 | this.loading = 'loading' 332 | }, 333 | hideLoader () { 334 | this.loading = '' 335 | }, 336 | allCap (value) { 337 | return value.toUpperCase() 338 | }, 339 | formatDate (value, fmt) { 340 | if (value === null) return '' 341 | fmt = (typeof(fmt) === 'undefined') ? 'D MMM YYYY' : fmt 342 | return moment(value, 'YYYY-MM-DD').format(fmt) 343 | }, 344 | gender (value) { 345 | return value === 'M' 346 | ? 'Male' 347 | : 'Female' 348 | }, 349 | showDetailRow (value) { 350 | let icon = this.$refs.vuetable.isVisibleDetailRow(value) ? 'down' : 'right' 351 | return [ 352 | '', 353 | '', 354 | '' 355 | ].join('') 356 | }, 357 | setFilter () { 358 | this.moreParams['filter'] = this.searchFor 359 | this.$nextTick(function() { 360 | this.$refs.vuetable.refresh() 361 | }) 362 | }, 363 | resetFilter () { 364 | this.searchFor = '' 365 | this.setFilter() 366 | }, 367 | preg_quote ( str ) { 368 | // http://kevin.vanzonneveld.net 369 | // + original by: booeyOH 370 | // + improved by: Ates Goral (http://magnetiq.com) 371 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 372 | // + bugfixed by: Onno Marsman 373 | // * example 1: preg_quote("$40"); 374 | // * returns 1: '\$40' 375 | // * example 2: preg_quote("*RRRING* Hello?"); 376 | // * returns 2: '\*RRRING\* Hello\?' 377 | // * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:"); 378 | // * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:' 379 | 380 | return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); 381 | }, 382 | highlight (needle, haystack) { 383 | return haystack.replace( 384 | new RegExp('(' + this.preg_quote(needle) + ')', 'ig'), 385 | '$1' 386 | ) 387 | }, 388 | rowClassCB (data, index) { 389 | return (index % 2) === 0 ? 'odd' : 'even' 390 | }, 391 | /* 392 | * Example of defining queryParams as a function 393 | */ 394 | // queryParams (sortOrder, currentPage, perPage) { 395 | // return { 396 | // 'sort': sortOrder[0].field + '|' + sortOrder[0].direction, 397 | // 'order': sortOrder[0].direction, 398 | // 'page': currentPage, 399 | // 'per_page': perPage 400 | // } 401 | // }, 402 | onCellClicked (data, field, event) { 403 | console.log('cellClicked', field.name) 404 | if (field.name !== '__actions') { 405 | this.$refs.vuetable.toggleDetailRow(data.id) 406 | } 407 | }, 408 | onCellDoubleClicked (data, field, event) { 409 | console.log('cellDoubleClicked:', field.name) 410 | }, 411 | onCellRightClicked (data, field, event) { 412 | console.log('cellRightClicked:', field.name) 413 | }, 414 | onLoadSuccess (response) { 415 | // set pagination data to pagination-info component 416 | this.$refs.paginationInfo.setPaginationData(response.data) 417 | 418 | let data = response.data.data 419 | if (this.searchFor !== '') { 420 | for (let n in data) { 421 | data[n].name = this.highlight(this.searchFor, data[n].name) 422 | data[n].email = this.highlight(this.searchFor, data[n].email) 423 | } 424 | } 425 | }, 426 | onLoadError (response) { 427 | if (response.status == 400) { 428 | sweetAlert('Something\'s Wrong!', response.data.message, 'error') 429 | } else { 430 | sweetAlert('Oops', E_SERVER_ERROR, 'error') 431 | } 432 | }, 433 | onPaginationData (tablePagination) { 434 | this.$refs.paginationInfo.setPaginationData(tablePagination) 435 | this.$refs.pagination.setPaginationData(tablePagination) 436 | }, 437 | onChangePage (page) { 438 | this.$refs.vuetable.changePage(page) 439 | }, 440 | onInitialized (fields) { 441 | console.log('onInitialized', fields) 442 | this.vuetableFields = fields 443 | }, 444 | onDataReset () { 445 | console.log('onDataReset') 446 | this.$refs.paginationInfo.resetData() 447 | this.$refs.pagination.resetData() 448 | }, 449 | }, 450 | }) 451 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratiw/vuetable-2/1f01bcf540e3c799dd4fe4d064c9af45784ca481/static/.gitkeep -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | 3 | // http://nightwatchjs.org/guide#settings-file 4 | module.exports = { 5 | "src_folders": ["test/e2e/specs"], 6 | "output_folder": "test/e2e/reports", 7 | "custom_assertions_path": ["test/e2e/custom-assertions"], 8 | 9 | "selenium": { 10 | "start_process": true, 11 | "server_path": "node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar", 12 | "host": "127.0.0.1", 13 | "port": 4444, 14 | "cli_args": { 15 | "webdriver.chrome.driver": require('chromedriver').path 16 | } 17 | }, 18 | 19 | "test_settings": { 20 | "default": { 21 | "selenium_port": 4444, 22 | "selenium_host": "localhost", 23 | "silent": true 24 | }, 25 | 26 | "chrome": { 27 | "desiredCapabilities": { 28 | "browserName": "chrome", 29 | "javascriptEnabled": true, 30 | "acceptSslCerts": true 31 | } 32 | }, 33 | 34 | "firefox": { 35 | "desiredCapabilities": { 36 | "browserName": "firefox", 37 | "javascriptEnabled": true, 38 | "acceptSslCerts": true 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 4 | 5 | // 2. run the nightwatch test suite against it 6 | // to run in additional browsers: 7 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 8 | // 2. add it to the --env flag below 9 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 10 | // For more information on Nightwatch's config file, see 11 | // http://nightwatchjs.org/guide#settings-file 12 | var opts = process.argv.slice(2) 13 | if (opts.indexOf('--config') === -1) { 14 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 15 | } 16 | if (opts.indexOf('--env') === -1) { 17 | opts = opts.concat(['--env', 'chrome']) 18 | } 19 | 20 | var spawn = require('cross-spawn') 21 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 22 | 23 | runner.on('exit', function (code) { 24 | server.close() 25 | process.exit(code) 26 | }) 27 | 28 | runner.on('error', function (err) { 29 | server.close() 30 | throw err 31 | }) 32 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | browser 7 | .url('http://localhost:8080') 8 | .waitForElementVisible('#app', 5000) 9 | .assert.elementPresent('.logo') 10 | .assert.containsText('h1', 'Hello Vue!') 11 | .assert.elementCount('p', 3) 12 | .end() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | /* eslint-disable no-extend-native */ 3 | Function.prototype.bind = require('function-bind') 4 | 5 | // require all test files (files that ends with .spec.js) 6 | var testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | var srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.conf') 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], 16 | // chai config 17 | client: { 18 | chai: { 19 | truncateThreshold: 0 20 | } 21 | }, 22 | reporters: ['spec', 'coverage'], 23 | files: [ 24 | '../../node_modules/babel-polyfill/dist/polyfill.js', 25 | './index.js' 26 | ], 27 | preprocessors: { 28 | './index.js': ['webpack', 'sourcemap'] 29 | }, 30 | webpack: webpackConfig, 31 | webpackMiddleware: { 32 | noInfo: true 33 | }, 34 | coverageReporter: { 35 | dir: './coverage', 36 | reporters: [ 37 | { type: 'lcov', subdir: '.' }, 38 | { type: 'text-summary' } 39 | ] 40 | } 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/specs/Vuetable.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuetable from '../../../src/components/Vuetable.vue' 3 | const VuetableInjector = require('!!vue-loader?inject!../../../src/components/Vuetable') 4 | 5 | Vue.config.productionTip = false 6 | 7 | describe('data requests', () => { 8 | let VuetableWithMocks 9 | let AxiosGetStub 10 | let sandbox = sinon.sandbox.create() 11 | 12 | beforeEach(function() { 13 | AxiosGetStub = sinon.stub().resolves({data: {data: [{name: 'john', description:'foo bar'}]}}); 14 | VuetableWithMocks = VuetableInjector({ 15 | 'axios': { 16 | get: AxiosGetStub 17 | } 18 | }) 19 | 20 | }) 21 | 22 | afterEach(function() { 23 | sandbox.restore() 24 | }) 25 | 26 | it('should loadData() to the given api when mounted', () => { 27 | const vm = new Vue({ 28 | template: '', 29 | components: {'vuetable': VuetableWithMocks }, 30 | data: { 31 | columns: [ 32 | 'name', 'description' 33 | ], 34 | } 35 | }).$mount() 36 | 37 | expect(AxiosGetStub).to.have.been.calledWith('http://example.com/api/test/loadOnMount', {params: {page: 1, per_page: 10, sort: '' }}) 38 | }) 39 | 40 | it('should not loadData() when load-on-start set to false', () => { 41 | const vm = new Vue({ 42 | template: '', 43 | components: {'vuetable': VuetableWithMocks}, 44 | data: { 45 | columns: [ 46 | 'name', 'description' 47 | ], 48 | } 49 | }).$mount() 50 | 51 | expect(vm.$refs.vuetable.loadOnStart).to.equal.false 52 | expect(AxiosGetStub).to.not.have.been.called 53 | }) 54 | 55 | it('should refresh the data if the api-url changes', done => { 56 | const vm = new Vue({ 57 | template: '', 58 | components: {'vuetable': VuetableWithMocks}, 59 | data: { 60 | columns: [ 61 | 'name', 'description' 62 | ], 63 | apiUrl: 'http://example.com/api/test' 64 | } 65 | }).$mount() 66 | 67 | let refreshSpy = sandbox.spy(vm.$refs.vuetable, 'refresh') 68 | 69 | const newApiUrl = 'http://example.com/api/test/apiUrlChange'; 70 | vm.apiUrl = newApiUrl 71 | 72 | vm.$nextTick().then(() => { 73 | expect(refreshSpy).to.have.been.called 74 | expect(AxiosGetStub).to.have.been.calledWith('http://example.com/api/test/apiUrlChange', {params: {page: 1, per_page: 10, sort: ''}}) 75 | }).then(done, done) 76 | }) 77 | 78 | it('should not refresh the data if the api-url changes and reactive api url is disabled', done => { 79 | const vm = new Vue({ 80 | template: '', 81 | components: {'vuetable': VuetableWithMocks}, 82 | data: { 83 | columns: [ 84 | 'name', 'description' 85 | ], 86 | apiUrl: 'http://example.com/api/test' 87 | } 88 | }).$mount() 89 | 90 | let refreshSpy = sandbox.spy(vm.$refs.vuetable, 'refresh') 91 | 92 | const newApiUrl = 'http://example.com/api/test/noReactiveApiUrl'; 93 | vm.apiUrl = newApiUrl 94 | 95 | vm.$nextTick().then(() => { 96 | expect(refreshSpy).to.not.have.been.called 97 | expect(AxiosGetStub).to.not.have.been.calledWith('http://example.com/api/test/noReactiveApiUrl', {params: {page: 1, per_page: 10, sort: ''}}) 98 | }) 99 | .then(done, done) 100 | }) 101 | }) 102 | 103 | /** 104 | * Properties 105 | */ 106 | describe('Properties', () => { 107 | 108 | describe('fields', () => { 109 | it('should create error when fields prop is not provided', () => { 110 | sinon.stub(console, 'error') 111 | 112 | const vm = new Vue({ 113 | template: '', 114 | components: { Vuetable } 115 | }).$mount() 116 | 117 | expect(console.error).to.have.been.called 118 | 119 | console.error.restore() 120 | }) 121 | 122 | it('should parse basic array of fields definition correctly', () => { 123 | const vm = new Vue({ 124 | template: '', 125 | components: { Vuetable }, 126 | data: { 127 | columns: ['code', 'description'] 128 | } 129 | }).$mount() 130 | let comp = vm.$children[0] 131 | expect(comp.tableFields).to.have.lengthOf(2) 132 | // deep equal does not work as expected in Safari 133 | // as it sees only Getter/Setter functions, not the real vulue 134 | // but deep equal works as expected on both Chrome and Firefox 135 | expect(comp.tableFields).to.satisfy(function(arr) { 136 | return ( 137 | arr[0].name === 'code' && 138 | arr[0].title === 'Code' && 139 | arr[0].titleClass === '' && 140 | arr[0].dataClass === '' && 141 | arr[0].callback === null && 142 | arr[0].visible 143 | ) && ( 144 | arr[1].name === 'description' && 145 | arr[1].title === 'Description' && 146 | arr[1].titleClass === '' && 147 | arr[1].dataClass === '' && 148 | arr[1].callback === null && 149 | arr[1].visible 150 | ) 151 | }) 152 | let nodes = comp.$el.querySelectorAll('table thead tr th') 153 | expect(nodes[0].attributes.id.value).to.equal('_code') 154 | expect(nodes[1].attributes.id.value).to.equal('_description') 155 | }) 156 | 157 | it('should parse array of object of fields definition correctly', () => { 158 | const vm = new Vue({ 159 | template: '', 160 | components: { Vuetable }, 161 | data: { 162 | columns: [ 163 | { name: 'code' }, 164 | { name: 'description' } 165 | ] 166 | } 167 | }).$mount() 168 | let comp = vm.$children[0] 169 | expect(comp.tableFields).to.have.lengthOf(2) 170 | expect(comp.tableFields[0].name).to.equal('code') 171 | expect(comp.tableFields[0].title).to.equal('Code') 172 | expect(comp.tableFields[0].titleClass).to.be.empty 173 | expect(comp.tableFields[0].dataClass).to.be.empty 174 | expect(comp.tableFields[0].callback).to.be.empty 175 | expect(comp.tableFields[0].visible).to.be.true 176 | 177 | let nodes = comp.$el.querySelectorAll('table thead tr th') 178 | expect(nodes[0].attributes.id.value).to.equal('_code') 179 | expect(nodes[1].attributes.id.value).to.equal('_description') 180 | }) 181 | 182 | it('should set default field title to capitalized name', () => { 183 | const vm = new Vue({ 184 | template: '', 185 | components: { Vuetable }, 186 | data: { 187 | columns: [{ 188 | name: 'code' 189 | }] 190 | } 191 | }).$mount() 192 | expect(vm.$children[0].tableFields[0].title).to.equal('Code') 193 | }) 194 | 195 | it('should correctly override field title when specified', () => { 196 | const vm = new Vue({ 197 | template: '', 198 | components: { Vuetable }, 199 | data: { 200 | columns: [{ 201 | name: 'code', 202 | title: 'My Title' 203 | }] 204 | } 205 | }).$mount() 206 | expect(vm.$children[0].tableFields[0].title).to.equal('My Title') 207 | }) 208 | 209 | it('should use the given titleClass to render field title', () => { 210 | const vm = new Vue({ 211 | template: '', 212 | components: { Vuetable }, 213 | data: { 214 | columns: [{ 215 | name: 'code', 216 | titleClass: 'foo-bar' 217 | }] 218 | } 219 | }).$mount() 220 | let comp = vm.$refs.vuetable 221 | expect(comp.tableFields[0].titleClass).to.equal('foo-bar') 222 | let nodes = comp.$el.querySelectorAll('table thead tr th') 223 | expect(nodes[0].attributes.id.value).to.equal('_code') 224 | expect(nodes[0].classList.contains('foo-bar')).to.be.true 225 | }) 226 | 227 | 228 | }) 229 | }) 230 | -------------------------------------------------------------------------------- /upgrade-guide.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | - Pagination and Pagination Info 3 | 4 | Pagination and pagination info are still in the package but no longer part of `vuetable`. That means you will have to explicitly include them in your template and wire them up to `vuetable` using availble events. 5 | 6 | This is very easy though. Basically, you just need to listen to `vuetable:pagination-data` event to get the pagination information, then call `setPaginationData()` on both `Pagination` and `PaginationInfo` components. 7 | Please see the provided example. 8 | 9 | The reason behind this is that uncoupling the pagination and pagination info components from `vuetable` makes it more flexible and light weight. If you do not need pagination component, you don't have to use it. Or if you want to use your own pagination component or put it somewhere else, you can easily do that. 10 | 11 | - Vuetable Events 12 | 13 | Due to the change in Vue 2.0 the deprecates `$broadcast` and `$dispatch` the events and encourages the use of centralized event hub, you can no longer send an event to `vuetable` to perform the task (e.g. `vuetable:reload`, `vuetable:refresh`) as before. 14 | 15 | In earlier version of `vuetable`, it usually uses and provides those events for communications between its internal components (e.g. Vuetable, VuetablePagination, etc). In this case, moving all those events to `vuex` seems over-kill and inappropriate since `vuex` is intended to be use as an centralized state management for an application. 16 | 17 | Although, those events were removed in this version, but almost all the functionality are still there in the methods inside `vuetable`. You can easily call it by referencing `vuetable` component in your code using [`ref`](http://vuejs.org/api/#ref) attribute. 18 | 19 | For the list of availble and removed events, please see the [What's Change] section. 20 | 21 | - `__actions` field 22 | 23 | The `__actions` special field was deprecated and can easily be replaced by the `__component` special field, which is more useful and shouldn't have any limitation since you can now use the full power of Vue.js. 24 | 25 | Please see the provided example for the replacing action component. 26 | 27 | - `append-params` 28 | 29 | The type of `append-params` has been changed from `Array` to `Object` type. 30 | 31 | - CSS styling 32 | 33 | All the related CSS classes has been moved into `css` prop, which is an `Object` type. This should make the template shorter and cleaner to look. 34 | 35 | --------------------------------------------------------------------------------