├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── images └── toggle-scrolling.gif ├── package.json └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | yarn-error.log 3 | /node_modules/ 4 | dist 5 | 6 | .sw[a-p] 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | images 3 | .prettierrc 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Adrien POLY 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 | ## Run DataTables under Rails Turbolinks 2 | 3 | ## (This has been renamed to rails-datatables) 4 | 5 | A [Stimulus](https://github.com/stimulusjs/stimulus) wrapper for 6 | [DataTables](https://datatables.net) under Rails 7 | [Turbolinks](https://github.com/turbolinks/turbolinks). 8 | 9 | ![Toggle Scrolling demo](images/toggle-scrolling.gif) 10 | 11 | ## Examples 12 | 13 | Here is a zero config example. 14 | 15 | ```erb 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
FirstLast
TenzinGyatso
BarakObama
NelsonMandela
26 | ``` 27 | 28 | Here is an example of passing the ajax data path from Rails to DataTables. 29 | This uses the [ajax-datatables-rails](https://github.com/jbox-web/ajax-datatables-rails) gem. 30 | It also sets `{ debug: true }` to turn on console logging. 31 | 32 | ```erb 33 | 47 |
48 | ``` 49 | 50 | ## Setup 51 | 52 | Add [ajax-datatables-rails](https://github.com/jbox-web/ajax-datatables-rails) to your Gemfile and follow the data source setup instructions. 53 | 54 | ``` 55 | gem 'ajax-datatables-rails' 56 | ``` 57 | 58 | Add [stimulus-datatables](https://github.com/jgorman/stimulus-datatables) 59 | to package.json and register it with 60 | [Stimulus](https://github.com/stimulusjs/stimulus). 61 | 62 | 63 | ``` 64 | yarn add stimulus-datatables 65 | ``` 66 | 67 | ```js 68 | // If jQuery is not already registered in window.jQuery do it here. 69 | window.jQuery = window.$ = require('jquery') 70 | 71 | // The controller will call: window.jQuery(table).DataTable(config) 72 | require('datatables.net') 73 | 74 | // These examples use bootstrap4 and the scroller plugin. 75 | // See https://datatables.net/download for more options. 76 | require('datatables.net-bs4') 77 | require('datatables.net-scroller-bs4') 78 | 79 | // Stimulus setup. 80 | import { Application } from 'stimulus' 81 | const application = Application.start() 82 | 83 | // Register the stimulus-datatables controller. 84 | import Datatable from 'stimulus-datatables' 85 | application.register('datatable', Datatable) 86 | ``` 87 | 88 | There appears to be a Datatable problem with long lines not folding 89 | in the scroller plugin. Here is the scss setup for these examples 90 | with the line folding workaround. 91 | 92 | ```scss 93 | @import 'datatables.net-bs4/css/dataTables.bootstrap4'; 94 | @import 'datatables.net-scroller-bs4/css/scroller.bootstrap4.css'; 95 | 96 | table.dataTable tbody tr td { 97 | white-space: normal; 98 | } 99 | ``` 100 | 101 | ## Advanced Usage 102 | 103 | You can make custom stimulus controllers which extend the 104 | standard stimulus datatables controller. 105 | 106 | Datatables under Turbolinks triggers extra `initialize()` and `connect()` calls 107 | that are ignored by the controller. The `.DataTable()` call alters the table 108 | element which causes ghost initialization and connect events that need 109 | to be ignored. 110 | 111 | When we navigate to a page which is already in the Turbolinks cache, 112 | Turbolinks shows the cached copy as a preview page until 113 | the real page arrives from the server. 114 | 115 | There is no point in setting up DataTables for preview pages since we will 116 | need to wait for the ajax call to retrieve the data from the server for 117 | display in the real page. 118 | 119 | There are three events that you can customize. `initialize()` sets up the 120 | configuration options. `connect()` starts up DataTables. `teardown()` destroys 121 | the DataTable instance. 122 | 123 | When you override `initialize()` and `connect()` you will want to ignore 124 | the ghost events by testing with `this.isBooting()`. See the example below. 125 | `super.initialize()` and `super.connect()` return the config object 126 | while booting and return false for ghost events. 127 | 128 | Call `this.log(msg, data)` to write to the console log 129 | to debug custom controller setups. See below for an example. 130 | 131 | You can turn on debug messages by setting `{ debug: true }`. 132 | Call `this.debug(msg, data)` to write to console.log only when 133 | `this.config.debug` is true. 134 | 135 | ```js 136 | import DataTable from 'stimulus-datatables' 137 | 138 | export default class extends DataTable { 139 | initialize() { 140 | // Ignore ghost events. 141 | if (!this.isBooting()) return 142 | 143 | // Default settings here will be overridden by component configurations. 144 | this.config = { pagingType: 'full_numbers', debug: true } 145 | 146 | // Call the super method which gets the component configuration. 147 | super.initialize() 148 | 149 | // This sets the final config values. 150 | this.config.dom = 'lfriptrip' 151 | } 152 | 153 | connect() { 154 | // Ignore ghost events. 155 | if (!this.isBooting()) return 156 | 157 | // You can alter the config here before the connect. 158 | 159 | // Call the super method to start up DataTables. 160 | super.connect() 161 | 162 | // Any post connect actions here. 163 | } 164 | 165 | teardown() { 166 | // Any before or after teardown actions. Here we write to console.log. 167 | this.log('finished', { dt: this }) 168 | 169 | // Call the super method to destroy the DataTable instance. 170 | super.teardown() 171 | } 172 | } 173 | ``` 174 | 175 | ## External Control 176 | 177 | Sometimes we will want to make changes to a running DataTables instance. 178 | 179 | In order to facilitate this, the DOM table element is linked to its 180 | controller instance: `table.dt`. Each controller instance is 181 | linked back to the DOM table `dt.element`, the `dt.config`, and 182 | the live DataTable API object `dt.dataTable`. 183 | 184 | Here is an example of a custom controller which can change state between 185 | scrolling or paging depending on a `#toggle-scrolling` checkbox. 186 | This example is running in the animated gif above. 187 | 188 | As the comment mentions, we can reconfigure a DataTable by updating 189 | the config and calling `dt.teardown()`. The teardown process will alter 190 | the DOM element which will trigger a stimulus reconnect with the new config. 191 | 192 | Here is the html containing the checkbox and the table. 193 | 194 | ```erb 195 | 199 | 200 | 217 |
218 | ``` 219 | 220 | Here is the custom articles-datatable controller which toggles between 221 | scrolling and paging display modes. 222 | 223 | ```js 224 | import DataTable from 'stimulus-datatables' 225 | 226 | export default class extends DataTable { 227 | 228 | initialize() { 229 | if (!this.isBooting()) return 230 | 231 | // Link to the scrolling checkbox and back. 232 | const checkbox = $('#toggle-scrolling')[0] 233 | if (checkbox) { 234 | checkbox.dt = this 235 | this.checkbox = checkbox 236 | } 237 | 238 | // Get the table config and the scrolling state. 239 | super.initialize() 240 | this.setScrollingState() 241 | } 242 | 243 | toggle_scrolling = event => { 244 | const dt = this.element.dt 245 | if (dt) { 246 | dt.setScrollingState() 247 | dt.teardown() // This triggers a reconnect. 248 | } 249 | } 250 | 251 | setScrollingState = () => { 252 | const scrolling = this.checkbox && this.checkbox.checked 253 | const config = this.config || {} 254 | config.scroller = scrolling 255 | config.scrollY = scrolling ? 600 : undefined 256 | config.stateSave = !scrolling 257 | } 258 | } 259 | ``` 260 | -------------------------------------------------------------------------------- /images/toggle-scrolling.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgorman/stimulus-datatables/2383a896ff0a7c70f1951d7a8d310ceb1bc91f8d/images/toggle-scrolling.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stimulus-datatables", 3 | "version": "1.0.6", 4 | "description": "Run DataTables under Rails Turbolinks", 5 | "keywords": [ 6 | "rails", 7 | "datatables", 8 | "stimulus", 9 | "turbolinks" 10 | ], 11 | "homepage": "https://github.com/jgorman/stimulus-datatables#readme", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/jgorman/stimulus-datatables.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/jgorman/stimulus-datatables/issues" 18 | }, 19 | "author": { 20 | "name": "John Gorman", 21 | "email": "johngorman2@gmail.com", 22 | "url": "https://johngorman.io" 23 | }, 24 | "license": "MIT", 25 | "scripts": { 26 | "build": "babel src --out-dir dist --presets @babel/preset-env --plugins=@babel/proposal-class-properties", 27 | "prepare": "yarn run build" 28 | }, 29 | "main": "dist/index.js", 30 | "dependencies": { 31 | "datatables.net": "^1.10.20", 32 | "stimulus": "^1.1.1" 33 | }, 34 | "devDependencies": { 35 | "@babel/cli": "^7.6.4", 36 | "@babel/core": "^7.6.4", 37 | "@babel/plugin-proposal-class-properties": "^7.5.5", 38 | "@babel/preset-env": "^7.6.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Controller } from 'stimulus' 2 | 3 | /* 4 | 5 | " 17 | > 18 |
19 | 20 | */ 21 | 22 | var dt_id = 0 23 | 24 | class StimulusDataTables extends Controller { 25 | isTable = () => this.element.nodeName === 'TABLE' 26 | 27 | isDataTable = () => this.element.className.includes('dataTable') 28 | 29 | isPreview = () => 30 | document.documentElement.hasAttribute('data-turbolinks-preview') 31 | 32 | isLive = () => this.dataTable 33 | 34 | isBooting = () => 35 | this.isTable() && !this.isDataTable() && !this.isPreview() && !this.isLive() 36 | 37 | debug = (msg, extra = '') => { 38 | if (!this.config || !this.config.debug) return 39 | this.log(msg, extra) 40 | } 41 | 42 | log = (msg, extra = '') => { 43 | const id = this.element.id 44 | const pad = msg.length < 10 ? 10 - msg.length : 0 45 | console.log('DT', this.dt_id || 0, msg, ' '.repeat(pad), id, extra) 46 | } 47 | 48 | initialize() { 49 | if (!this.isBooting()) return false 50 | 51 | this.dt_id = ++dt_id 52 | this.element.dt = this 53 | 54 | // Setting scrollY fixes page reload bug in autoWidth. 55 | const pre_config = Object.assign({ scrollY: undefined }, this.config) 56 | const config_s = this.data.get('config') 57 | const config = config_s ? JSON.parse(config_s) : {} 58 | this.config = Object.assign({}, pre_config, config) 59 | 60 | this.debug('initialize', { config: this.config }) 61 | return this.config 62 | } 63 | 64 | connect() { 65 | if (!this.isBooting()) return false 66 | 67 | // Register the teardown listener and start up DataTable. 68 | document.addEventListener('turbolinks:before-render', this._teardown) 69 | this.dataTable = window 70 | .jQuery(this.element) 71 | .DataTable(Object.assign({}, this.config)) 72 | 73 | this.debug('connect', { dt: this }) 74 | return this.config 75 | } 76 | 77 | _teardown = () => this.teardown() 78 | 79 | teardown(event) { 80 | if (!this.isLive()) return false 81 | 82 | document.removeEventListener('turbolinks:before-render', this._teardown) 83 | this.dataTable.destroy() 84 | this.dataTable = undefined 85 | 86 | this.debug('teardown') 87 | return this.config 88 | } 89 | } 90 | 91 | export default StimulusDataTables 92 | --------------------------------------------------------------------------------