├── .gitignore ├── README.md ├── jsconfig.json ├── package.json ├── gulpfile.js ├── sample └── index.html ├── dist ├── in-line-grid-editing-min.js └── in-line-grid-editing-debug.js └── src └── in-line-grid-editing.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /demo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # In-Line-Grid-Editing 2 | A flexible plug-in data editing in table 3 | 4 | The In line grid edinting supports data editing operations (create, update, remove) via a simple configuration. 5 | All you have to do to enable table to editing; 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=759670 3 | // for the documentation about the jsconfig.json format 4 | "compilerOptions": { 5 | "target": "es6", 6 | "module": "commonjs", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | "bower_components", 12 | "jspm_packages", 13 | "tmp", 14 | "temp" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inlinegridediting", 3 | "version": "1.0.0", 4 | "description": "A flexible plug-in data editing in table", 5 | "main": "in-line-grid-editing.js", 6 | "scripts": { 7 | "test": "setup" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/chasoliveira/In-Line-Grid-Editing.git" 12 | }, 13 | "keywords": [ 14 | "grid", 15 | "edit", 16 | "inline", 17 | "in line grid editing" 18 | ], 19 | "author": "Charles Oliveira", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/chasoliveira/In-Line-Grid-Editing/issues" 23 | }, 24 | "homepage": "https://github.com/chasoliveira/In-Line-Grid-Editing#readme", 25 | "devDependencies": { 26 | "dell": "^1.0.0", 27 | "gulp-concat": "^2.6.0", 28 | "gulp-minify": "0.0.14", 29 | "gulp-uglify": "^2.0.0", 30 | "gulp-useref": "^3.1.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var minify = require('gulp-minify'); 3 | var useref = require('gulp-useref'); 4 | var concat = require('gulp-concat') 5 | gulp.task('build', function () { 6 | return gulp.src('src/*.*') // Get source files with gulp.src 7 | .pipe(useref()) 8 | .pipe(concat('in-line-grid-editing.js')) 9 | .pipe(minify({ext:{ 10 | src:'-debug.js', 11 | min:'-min.js' 12 | }})) 13 | .pipe(gulp.dest('dist')) // Outputs the file in the destination folder 14 | }); 15 | gulp.task('copyToDemo', function () { 16 | return gulp.src('src/*.*') // Get source files with gulp.src 17 | .pipe(useref()) 18 | .pipe(concat('in-line-grid-editing.js')) 19 | .pipe(minify({ext:{ 20 | src:'-debug.js', 21 | min:'-min.js' 22 | }})) 23 | .pipe(gulp.dest('demo/InLineGridEditing/Scripts')) // Outputs the file in the destination folder 24 | }); 25 | -------------------------------------------------------------------------------- /sample/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/in-line-grid-editing-min.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";var n="ilgeEditDelete",t="ilgeApplyCancel",i="ilgeCustom",a=function(a,c){c=c||{},a=e(a);var l=function(n){function t(n,t,i){n.append(e("",{type:"hidden",name:t+"-hidden",id:t+"-hidden",value:i}))}function i(e,n){void 0!=e.actionFormatter&&"function"==typeof e.actionFormatter&&e.actionFormatter(n)}function a(e){return n.find("td:nth-child("+e.column+")")}function c(n){return"select"===n.type?e("",{type:n.type,name:n.name,id:n.name,class:n.class}):e("",{type:n.type,name:n.name,id:n.name,class:n.class})}this.textField=function(e){var n=c(e),l=a(e),o=l.html();n.val(o),l.html(n),i(e,n),t(l,e.name,o)},this.numericField=function(e){var n=c(e),l=a(e),o=l.html();n.val(o),i(e,n),l.html(n),t(l,e.name,o)},this.decimalField=function(e){var n=c(e),l=a(e),o=l.html();n.val(o),i(e,n),l.html(n),t(l,e.name,o)},this.boolField=function(e){var n=c(e),i=a(e),l="true"===i.html().toLowerCase();n.val(l),n.attr("checked",l),i.html(n),t(i,e.name,l)},this.dateField=function(e){var n=c(e),l=a(e),o=l.html();n.val(o),i(e,n),l.html(n),t(l,e.name,o)},this.dateTimeField=function(e){var n=c(e),l=a(e),o=l.html();n.val(o),i(e,n),l.html(n),t(l,e.name,o)},this.selectField=function(e){var n=c(e),i=a(e),l=i.html();i.html(n),t(i,e.name,l)}},o=function(e){this.textField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a)},this.numericField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a)},this.decimalField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a)},this.boolField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a.toString())},this.dateField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a)},this.dateTimeField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("input[name="+n.name+"]").val();i.html(a)},this.selectField=function(n,t){var i=e.find("td:nth-child("+n.column+")"),a=t?i.find("input[name='"+n.name+"-hidden']").val():i.find("select[name='"+n.name+"'] option:selected").html();i.html(a)}},d=function(n,t){var i={text:"text",numeric:"numeric",decimal:"decimal",bool:"bool",date:"date",datetime:"datetime",select:"select"},a=new l(t),c=new o(t),d=function(n,i){var a=[];n.parameter.loadBy.keys.forEach(function(e){t.find("[name='"+e.from+"']option:selected");e.isInRow?a.push(e.key+"="+t.find("[name='"+e.from+"']").val()):a.push(e.key+"="+t.closest("body").find("[name='"+e.from+"']").val())});var c=r(n.parameter.uri,a.join("&"));e.getJSON(c,function(e){var t=s(e,n);i(t)})},u=function(n,t){e.getJSON(n.parameter.uri,function(e){var i=s(e,n);t(i)})},s=function(e,n){var t=[];return e.forEach(function(e){t.push({value:e[n.parameter.valueField],html:e[n.parameter.textField]})}),t},m=function(e,n){void 0!=e.parameter.loadBy?d(e,n):u(e,n)},f=0,h=function(n){n.forEach(function(n){var i=t.find("td:nth-child("+n.column+")"),a=i.find("input[name='"+n.name+"-hidden']").val(),c=t.find("select[name='"+n.name+"']");console.log("currentField: "+n.name),m(n,function(t){console.log("currentField: "+n.name+" items: "+t.length),t.forEach(function(n){var t=n.html==a;c.append(e("",{value:n.value,html:n.html,selected:t}))})}),f++})},p=function(){var e=n.filter(function(e){return e.type===i.select}),t=e.filter(function(e){return void 0===e.parameter.loadBy}),a=e.filter(function(e){return"object"==typeof e.parameter.loadBy});t.forEach(function(e){e.fieldsToLoad=a.filter(function(n){return n.parameter.loadBy.keys.filter(function(n){return n.from===e.name})})}),h(t)};this.setUpInputsOnRow=function(e){n.forEach(function(e){switch(e.type.toLowerCase()){case i.text:a.textField(e);break;case i.numeric:a.numericField(e);break;case i.decimal:a.decimalField(e);break;case i.bool:a.boolField(e);break;case i.date:a.dateField(e);break;case i.datetime:a.dateTimeField(e);break;case i.select:a.selectField(e)}}),p()},this.setDownInputsOnRow=function(e){n.forEach(function(n){switch(n.type.toLowerCase()){case i.text:c.textField(n,e);break;case i.numeric:c.numericField(n,e);break;case i.decimal:c.decimalField(n,e);break;case i.bool:c.boolField(n,e);break;case i.date:c.dateField(n,e);break;case i.datetime:c.dateTimeField(n,e);break;case i.select:c.selectField(n,e)}})}},r=function(e,n){return e.lastIndexOf("/")==e.length-1?e+"?"+n:e+"/?"+n},u=function(i,a){var c={controller:{uriSave:"",uriUpdate:"",uriDelete:"",messageReturn:function(){}},fields:[]},l=a.fields||c.fields,o=function(){return void 0===l?null:l.filter(function(e){return"buttons"!=e.type&&e.edit===!0})};this.getFieldsButton=function(){return void 0===l?null:l.filter(function(e){return"buttons"==e.type})};var u=function(e){e.find("."+n).toggle(),e.find("."+t).toggle()},s=function(e){if(void 0===l)return null;var n=l.filter(function(e){return e.isKey===!0}),t="",i=0;return n.forEach(function(a){var c=e.find("td:nth-child("+a.column+")").html();t+=a.name+"="+c,i>0&&i",{class:n});c.editDelete.forEach(function(n){d.append(e("",{name:n.name,class:n.class,type:"button"}).append(e("",{class:n.icon})))}),o.append(d);var r=e("",{class:t});c.cancelApply.forEach(function(n){r.append(e("",{name:n.name,class:n.class,type:"button"}).append(e("",{class:n.icon})))}),r.hide(),o.append(r);var u=e("",{class:i});c.custom.forEach(function(n){var t=e("",{name:n.name,class:n.class,type:"button"}),i=e("",{class:n.icon});t.append(i),t.append(n.text),u.append(t),n.showcheck||t.addClass("hide")}),o.append(u)})},r=function(e){var n={apply:"apply",cancel:"cancel",edit:"edit",remove:"remove",custom:"custom"};e.forEach(function(e){var t=a.find("[name='"+e.name+"']");switch(e.type){case n.apply:t.on("click",l.addItemInGrid);break;case n.cancel:t.on("click",l.cancelEditOrAddInGrid);break;case n.edit:t.on("click",l.editItemInGrid);break;case n.remove:t.on("click",l.removeItemFromGrid);break;case n.custom:t.on("click",e.onClick)}})};o.forEach(function(e){d(e.buttons,e.column),r(e.buttons.editDelete),r(e.buttons.cancelApply),r(e.buttons.custom)})}};e.fn.ilge=function(e){var n=new a(this,e);n.setUpGrid()}}(jQuery); -------------------------------------------------------------------------------- /dist/in-line-grid-editing-debug.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | var divClassButtonsEditDelete = "ilgeEditDelete"; 4 | var divClassButtonsApplyCancel = "ilgeApplyCancel"; 5 | var divClassButtonsCustom = "ilgeCustom"; 6 | var inLineGridEditing = function (container, options) { 7 | options = options || {}; 8 | container = $(container); 9 | var createElement = function (row) { 10 | function createHidden(td, name, value) { 11 | td.append($("", { type: "hidden", name: name + "-hidden", id: name + "-hidden", value: value })); 12 | } 13 | function actionFormatter(field, target) { 14 | if (field.actionFormatter != undefined && typeof field.actionFormatter === "function") 15 | field.actionFormatter(target); 16 | } 17 | function getCurrentTd(field) { 18 | return row.find("td:nth-child(" + field.column + ")"); 19 | } 20 | function createInput(field) { 21 | if (field.type === 'select') 22 | return $("", { type: field.type, name: field.name, id: field.name, "class": field.class }); 23 | return $("", { type: field.type, name: field.name, id: field.name, "class": field.class }); 24 | } 25 | this.textField = function (field) { 26 | var textInput = createInput(field); 27 | var td = getCurrentTd(field); 28 | var currentValue = td.html(); 29 | textInput.val(currentValue); 30 | td.html(textInput); 31 | actionFormatter(field, textInput); 32 | createHidden(td, field.name, currentValue); 33 | } 34 | this.numericField = function (field) { 35 | var numericInput = createInput(field); 36 | var td = getCurrentTd(field); 37 | var currentValue = td.html(); 38 | numericInput.val(currentValue); 39 | actionFormatter(field, numericInput); 40 | td.html(numericInput); 41 | createHidden(td, field.name, currentValue); 42 | } 43 | this.decimalField = function (field) { 44 | var textInput = createInput(field); 45 | var td = getCurrentTd(field); 46 | var currentValue = td.html(); 47 | textInput.val(currentValue); 48 | actionFormatter(field, textInput); 49 | td.html(textInput); 50 | createHidden(td, field.name, currentValue); 51 | } 52 | this.boolField = function (field) { 53 | var checkInput = createInput(field); 54 | var td = getCurrentTd(field) 55 | var value = td.html().toLowerCase() === "true"; 56 | checkInput.val(value); 57 | checkInput.attr("checked", value); 58 | td.html(checkInput); 59 | createHidden(td, field.name, value); 60 | } 61 | this.dateField = function (field) { 62 | var dateInput = createInput(field); 63 | var td = getCurrentTd(field); 64 | var currentValue = td.html(); 65 | dateInput.val(currentValue); 66 | actionFormatter(field, dateInput); 67 | td.html(dateInput); 68 | createHidden(td, field.name, currentValue); 69 | } 70 | this.dateTimeField = function (field) { 71 | var textInput = createInput(field); 72 | var td = getCurrentTd(field); 73 | var currentValue = td.html(); 74 | textInput.val(currentValue); 75 | actionFormatter(field, textInput); 76 | td.html(textInput); 77 | createHidden(td, field.name, currentValue); 78 | } 79 | 80 | this.selectField = function (field) { 81 | var selectInput = createInput(field); 82 | var td = getCurrentTd(field); 83 | var currentValue = td.html(); 84 | td.html(selectInput); 85 | createHidden(td, field.name, currentValue); 86 | } 87 | 88 | } 89 | var revertElement = function (row) { 90 | this.textField = function (field, toOldValue) { 91 | var td = row.find("td:nth-child(" + field.column + ")"); 92 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 93 | td.html(htmlValue); 94 | } 95 | this.numericField = function (field, toOldValue) { 96 | var td = row.find("td:nth-child(" + field.column + ")"); 97 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 98 | td.html(htmlValue); 99 | } 100 | this.decimalField = function (field, toOldValue) { 101 | var td = row.find("td:nth-child(" + field.column + ")"); 102 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 103 | td.html(htmlValue); 104 | } 105 | this.boolField = function (field, toOldValue) { 106 | var td = row.find("td:nth-child(" + field.column + ")"); 107 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 108 | td.html(htmlValue.toString()); 109 | } 110 | this.dateField = function (field, toOldValue) { 111 | var td = row.find("td:nth-child(" + field.column + ")"); 112 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 113 | td.html(htmlValue); 114 | } 115 | this.dateTimeField = function (field, toOldValue) { 116 | var td = row.find("td:nth-child(" + field.column + ")"); 117 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 118 | td.html(htmlValue); 119 | } 120 | this.selectField = function (field, toOldValue) { 121 | var td = row.find("td:nth-child(" + field.column + ")"); 122 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("select[name='" + field.name + "'] option:selected").html(); 123 | td.html(htmlValue); 124 | } 125 | } 126 | var fieldElements = function (fields, row) { 127 | var fieldType = { 128 | text: "text", 129 | numeric: "numeric", 130 | decimal: "decimal", 131 | bool: "bool", 132 | date: "date", 133 | datetime: "datetime", 134 | select: "select" 135 | } 136 | var create = new createElement(row); 137 | var revert = new revertElement(row); 138 | 139 | var getItemnsWithParameters = function (field, callback) { 140 | var parameter = []; 141 | field.parameter.loadBy.keys.forEach(function (current) { 142 | var value = row.find("[name='" + current.from + "']option:selected"); 143 | if (current.isInRow) 144 | parameter.push(current.key + "=" + row.find("[name='" + current.from + "']").val()); 145 | else 146 | parameter.push(current.key + "=" + row.closest("body").find("[name='" + current.from + "']").val()); 147 | }); 148 | 149 | var uri = setUrl(field.parameter.uri, parameter.join("&")); 150 | $.getJSON(uri, function (data) { 151 | var result = fillDataFromResult(data, field); 152 | callback(result); 153 | }); 154 | } 155 | var getItemnsWithOutParameters = function (field, callback) { 156 | $.getJSON(field.parameter.uri, function (data) { 157 | var result = fillDataFromResult(data, field); 158 | callback(result); 159 | }); 160 | } 161 | var fillDataFromResult = function (result, field) { 162 | var dataReturn = []; 163 | result.forEach(function (item) { 164 | dataReturn.push({ value: item[field.parameter.valueField], html: item[field.parameter.textField] }); 165 | }); 166 | return dataReturn; 167 | } 168 | var getItemns = function (field, callback) { 169 | if (field.parameter.loadBy != undefined) 170 | getItemnsWithParameters(field, callback); 171 | else 172 | getItemnsWithOutParameters(field, callback); 173 | } 174 | var count = 0; 175 | var setUpFillSelectFields = function (selectFields) { 176 | selectFields.forEach(function (currentField) { 177 | var td = row.find("td:nth-child(" + currentField.column + ")"); 178 | var currentValue = td.find("input[name='" + currentField.name + "-hidden']").val(); 179 | var currentSelect = row.find("select[name='" + currentField.name + "']"); 180 | console.log("currentField: " + currentField.name); 181 | getItemns(currentField, function (items) { 182 | console.log("currentField: " + currentField.name + " items: " + items.length); 183 | items.forEach(function (item) { 184 | var isSelected = item.html == currentValue; 185 | currentSelect.append($("", { value: item.value, html: item.html, selected: isSelected })); 186 | }); 187 | }); 188 | count++; 189 | }); 190 | } 191 | var setUpSelects = function () { 192 | var selectFields = fields.filter(function (field) { return field.type === fieldType.select; }); 193 | var selectFieldsIndependents = selectFields.filter(function (field) { return field.parameter.loadBy === undefined; }); 194 | var selectFieldsDependents = selectFields.filter(function (field) { return typeof field.parameter.loadBy === "object"; }); 195 | selectFieldsIndependents.forEach(function (currentField) { 196 | currentField.fieldsToLoad = selectFieldsDependents.filter(function (field) { 197 | return field.parameter.loadBy.keys.filter(function (item) { 198 | return item.from === currentField.name; 199 | }); 200 | }); 201 | }); 202 | setUpFillSelectFields(selectFieldsIndependents); 203 | // independents.promise.then(setUpFillSelectFields(selectFieldsDependents)); 204 | } 205 | 206 | this.setUpInputsOnRow = function (callback) { 207 | fields.forEach(function (currentField) { 208 | switch (currentField.type.toLowerCase()) { 209 | case fieldType.text: create.textField(currentField); break; 210 | case fieldType.numeric: create.numericField(currentField); break; 211 | case fieldType.decimal: create.decimalField(currentField); break; 212 | case fieldType.bool: create.boolField(currentField); break; 213 | case fieldType.date: create.dateField(currentField); break; 214 | case fieldType.datetime: create.dateTimeField(currentField); break; 215 | case fieldType.select: create.selectField(currentField); break; 216 | default: break; 217 | } 218 | }); 219 | setUpSelects(); 220 | } 221 | this.setDownInputsOnRow = function (toOldValue) { 222 | fields.forEach(function (currentField) { 223 | switch (currentField.type.toLowerCase()) { 224 | case fieldType.text: revert.textField(currentField, toOldValue); break; 225 | case fieldType.numeric: revert.numericField(currentField, toOldValue); break; 226 | case fieldType.decimal: revert.decimalField(currentField, toOldValue); break; 227 | case fieldType.bool: revert.boolField(currentField, toOldValue); break; 228 | case fieldType.date: revert.dateField(currentField, toOldValue); break; 229 | case fieldType.datetime: revert.dateTimeField(currentField, toOldValue); break; 230 | case fieldType.select: revert.selectField(currentField, toOldValue); break; 231 | default: break; 232 | } 233 | }); 234 | } 235 | } 236 | 237 | var setUrl = function (urls, parameters) { 238 | if (urls.lastIndexOf("/") == urls.length - 1) 239 | return urls + "?" + parameters; 240 | else 241 | return urls + "/?" + parameters; 242 | } 243 | 244 | var inLineGridConfig = function (container, options) { 245 | var defaultOptions = { 246 | controller: { 247 | uriSave: "", 248 | uriUpdate: "", 249 | uriDelete: "", 250 | messageReturn: function () { } 251 | }, 252 | fields: [] 253 | } 254 | var fields = options.fields || defaultOptions.fields; 255 | 256 | var getFields = function () { 257 | if (fields === undefined) return null; 258 | return fields.filter(function (field) { return field.type != "buttons" && field.edit === true; }); 259 | } 260 | this.getFieldsButton = function () { 261 | if (fields === undefined) return null; 262 | return fields.filter(function (field) { return field.type == "buttons"; }); 263 | } 264 | 265 | var showCancelApplyChangesButtons = function (tr) { 266 | tr.find("." + divClassButtonsEditDelete).toggle(); 267 | tr.find("." + divClassButtonsApplyCancel).toggle(); 268 | } 269 | var getKeyAsUrlParameter = function (currentRow) { 270 | if (fields === undefined) return null; 271 | var keys = fields.filter(function (field) { return field.isKey === true }); 272 | var keyAsUrl = ""; 273 | var count = 0; 274 | keys.forEach(function (current) { 275 | var value = currentRow.find("td:nth-child(" + current.column + ")").html(); 276 | keyAsUrl += current.name + "=" + value; 277 | if (count > 0 && count < keys.length) 278 | keyAsUrl += "&"; 279 | count++; 280 | }); 281 | return keyAsUrl; 282 | }; 283 | 284 | this.addItemInGrid = function (event) { 285 | var currentRow = $(this).closest("tr"); 286 | }; 287 | this.editItemInGrid = function (event) { 288 | var currentRow = $(this).closest("tr"); 289 | showCancelApplyChangesButtons(currentRow); 290 | var editFields = getFields(); 291 | var fieldElement = new fieldElements(editFields, currentRow); 292 | fieldElement.setUpInputsOnRow(); 293 | }; 294 | this.removeItemFromGrid = function (event) { 295 | var currentRow = $(this).closest("tr"); 296 | var keysToDelete = getKeyAsUrlParameter(currentRow); 297 | var url = setUrl(options.controller.uriDelete.url, keysToDelete); 298 | $.ajax({ 299 | method: options.controller.uriDelete.verb, 300 | url: url, 301 | cache: false, 302 | error: function (XMLHttpRequest, textStatus, errorThrown) { 303 | options.controller.messageReturn(errorThrown); 304 | }, 305 | success: function (data) { 306 | if (data.result) { 307 | currentRow.remove(); 308 | } 309 | options.controller.messageReturn(data); 310 | } 311 | }); 312 | } 313 | this.cancelEditOrAddInGrid = function (event) { 314 | var currentRow = $(this).closest("tr"); 315 | var editFields = getFields(); 316 | var fieldElement = new fieldElements(editFields, currentRow); 317 | fieldElement.setDownInputsOnRow(true); 318 | showCancelApplyChangesButtons(currentRow); 319 | } 320 | }; 321 | 322 | this.setUpGrid = function () { 323 | var config = new inLineGridConfig(container, options); 324 | var buttons = config.getFieldsButton(); 325 | var addButtonOnGrid = function (buttons, tdIdx) { 326 | var rows = container.find("tbody tr"); 327 | rows.each(function (idx) { 328 | var td = $(this).find("td:nth-child(" + tdIdx + ")"); 329 | var divEditDelete = $("", { "class": divClassButtonsEditDelete }); 330 | buttons.editDelete.forEach(function (button) { 331 | divEditDelete.append($("", { name: button.name, "class": button.class, type: "button" }).append($("", { "class": button.icon }))); 332 | }); 333 | td.append(divEditDelete); 334 | 335 | var divCancelApply = $("", { "class": divClassButtonsApplyCancel, }); 336 | buttons.cancelApply.forEach(function (button) { 337 | divCancelApply.append($("", { name: button.name, "class": button.class, type: "button" }).append($("", { "class": button.icon }))); 338 | }); 339 | divCancelApply.hide(); 340 | td.append(divCancelApply); 341 | 342 | var divCustom = $("", { "class": divClassButtonsCustom, }); 343 | buttons.custom.forEach(function (button) { 344 | var btn = $("", { name: button.name, "class": button.class, type: "button" }); 345 | var icon = $("", { "class": button.icon }); 346 | btn.append(icon); 347 | btn.append(button.text); 348 | divCustom.append(btn); 349 | if (!button.showcheck) 350 | btn.addClass("hide"); 351 | }); 352 | td.append(divCustom); 353 | }); 354 | }; 355 | var setUpEventButtons = function (buttons) { 356 | var typeBtn = { 357 | apply: "apply", 358 | cancel: "cancel", 359 | edit: "edit", 360 | remove: "remove", 361 | custom: "custom", 362 | } 363 | buttons.forEach(function (currentBtn) { 364 | var actionButton = container.find("[name='" + currentBtn.name + "']"); 365 | switch (currentBtn.type) { 366 | case typeBtn.apply: actionButton.on("click", config.addItemInGrid); break; 367 | case typeBtn.cancel: actionButton.on("click", config.cancelEditOrAddInGrid); break; 368 | case typeBtn.edit: actionButton.on("click", config.editItemInGrid); break; 369 | case typeBtn.remove: actionButton.on("click", config.removeItemFromGrid); break; 370 | case typeBtn.custom: actionButton.on("click", currentBtn.onClick); break; 371 | default: break; 372 | } 373 | }); 374 | }; 375 | 376 | buttons.forEach(function (current) { 377 | addButtonOnGrid(current.buttons, current.column); 378 | setUpEventButtons(current.buttons.editDelete); 379 | setUpEventButtons(current.buttons.cancelApply); 380 | setUpEventButtons(current.buttons.custom); 381 | }); 382 | }; 383 | } 384 | $.fn.ilge = function (options) { 385 | var inlineGrigEditing = new inLineGridEditing(this, options); 386 | inlineGrigEditing.setUpGrid(); 387 | } 388 | })(jQuery); -------------------------------------------------------------------------------- /src/in-line-grid-editing.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | var divClassButtonsEditDelete = "ilgeEditDelete"; 4 | var divClassButtonsApplyCancel = "ilgeApplyCancel"; 5 | var divClassButtonsCustom = "ilgeCustom"; 6 | var inLineGridEditing = function (container, options) { 7 | options = options || {}; 8 | container = $(container); 9 | var createElement = function (row) { 10 | function createHidden(td, name, value) { 11 | td.append($("", { type: "hidden", name: name + "-hidden", id: name + "-hidden", value: value })); 12 | } 13 | function actionFormatter(field, target) { 14 | if (field.actionFormatter != undefined && typeof field.actionFormatter === "function") 15 | field.actionFormatter(target); 16 | } 17 | function getCurrentTd(field) { 18 | return row.find("td:nth-child(" + field.column + ")"); 19 | } 20 | function createInput(field) { 21 | if (field.type === 'select') 22 | return $("", { type: field.type, name: field.name, id: field.name, "class": field.class }); 23 | return $("", { type: field.type, name: field.name, id: field.name, "class": field.class }); 24 | } 25 | this.textField = function (field) { 26 | var textInput = createInput(field); 27 | var td = getCurrentTd(field); 28 | var currentValue = td.html(); 29 | textInput.val(currentValue); 30 | td.html(textInput); 31 | actionFormatter(field, textInput); 32 | createHidden(td, field.name, currentValue); 33 | } 34 | this.numericField = function (field) { 35 | var numericInput = createInput(field); 36 | var td = getCurrentTd(field); 37 | var currentValue = td.html(); 38 | numericInput.val(currentValue); 39 | actionFormatter(field, numericInput); 40 | td.html(numericInput); 41 | createHidden(td, field.name, currentValue); 42 | } 43 | this.decimalField = function (field) { 44 | var textInput = createInput(field); 45 | var td = getCurrentTd(field); 46 | var currentValue = td.html(); 47 | textInput.val(currentValue); 48 | actionFormatter(field, textInput); 49 | td.html(textInput); 50 | createHidden(td, field.name, currentValue); 51 | } 52 | this.boolField = function (field) { 53 | var checkInput = createInput(field); 54 | var td = getCurrentTd(field) 55 | var value = td.html().toLowerCase() === "true"; 56 | checkInput.val(value); 57 | checkInput.attr("checked", value); 58 | td.html(checkInput); 59 | createHidden(td, field.name, value); 60 | } 61 | this.dateField = function (field) { 62 | var dateInput = createInput(field); 63 | var td = getCurrentTd(field); 64 | var currentValue = td.html(); 65 | dateInput.val(currentValue); 66 | actionFormatter(field, dateInput); 67 | td.html(dateInput); 68 | createHidden(td, field.name, currentValue); 69 | } 70 | this.dateTimeField = function (field) { 71 | var textInput = createInput(field); 72 | var td = getCurrentTd(field); 73 | var currentValue = td.html(); 74 | textInput.val(currentValue); 75 | actionFormatter(field, textInput); 76 | td.html(textInput); 77 | createHidden(td, field.name, currentValue); 78 | } 79 | 80 | this.selectField = function (field) { 81 | var selectInput = createInput(field); 82 | var td = getCurrentTd(field); 83 | var currentValue = td.html(); 84 | td.html(selectInput); 85 | createHidden(td, field.name, currentValue); 86 | } 87 | 88 | } 89 | var revertElement = function (row) { 90 | this.textField = function (field, toOldValue) { 91 | var td = row.find("td:nth-child(" + field.column + ")"); 92 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 93 | td.html(htmlValue); 94 | } 95 | this.numericField = function (field, toOldValue) { 96 | var td = row.find("td:nth-child(" + field.column + ")"); 97 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 98 | td.html(htmlValue); 99 | } 100 | this.decimalField = function (field, toOldValue) { 101 | var td = row.find("td:nth-child(" + field.column + ")"); 102 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 103 | td.html(htmlValue); 104 | } 105 | this.boolField = function (field, toOldValue) { 106 | var td = row.find("td:nth-child(" + field.column + ")"); 107 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 108 | td.html(htmlValue.toString()); 109 | } 110 | this.dateField = function (field, toOldValue) { 111 | var td = row.find("td:nth-child(" + field.column + ")"); 112 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 113 | td.html(htmlValue); 114 | } 115 | this.dateTimeField = function (field, toOldValue) { 116 | var td = row.find("td:nth-child(" + field.column + ")"); 117 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("input[name=" + field.name + "]").val(); 118 | td.html(htmlValue); 119 | } 120 | this.selectField = function (field, toOldValue) { 121 | var td = row.find("td:nth-child(" + field.column + ")"); 122 | var htmlValue = (toOldValue) ? td.find("input[name='" + field.name + "-hidden']").val() : td.find("select[name='" + field.name + "'] option:selected").html(); 123 | td.html(htmlValue); 124 | } 125 | } 126 | var fieldElements = function (fields, row) { 127 | var fieldType = { 128 | text: "text", 129 | numeric: "numeric", 130 | decimal: "decimal", 131 | bool: "bool", 132 | date: "date", 133 | datetime: "datetime", 134 | select: "select" 135 | } 136 | var create = new createElement(row); 137 | var revert = new revertElement(row); 138 | 139 | var getItemnsWithParameters = function (field, callback) { 140 | var parameter = []; 141 | field.parameter.loadBy.keys.forEach(function (current) { 142 | var value = row.find("[name='" + current.from + "']option:selected"); 143 | if (current.isInRow) 144 | parameter.push(current.key + "=" + row.find("[name='" + current.from + "']").val()); 145 | else 146 | parameter.push(current.key + "=" + row.closest("body").find("[name='" + current.from + "']").val()); 147 | }); 148 | 149 | var uri = setUrl(field.parameter.uri, parameter.join("&")); 150 | $.getJSON(uri, function (data) { 151 | var result = fillDataFromResult(data, field); 152 | callback(result); 153 | }); 154 | } 155 | var getItemnsWithOutParameters = function (field, callback) { 156 | $.getJSON(field.parameter.uri, function (data) { 157 | var result = fillDataFromResult(data, field); 158 | callback(result); 159 | }); 160 | } 161 | var fillDataFromResult = function (result, field) { 162 | var dataReturn = []; 163 | result.forEach(function (item) { 164 | dataReturn.push({ value: item[field.parameter.valueField], html: item[field.parameter.textField] }); 165 | }); 166 | return dataReturn; 167 | } 168 | var getItemns = function (field, callback) { 169 | if (field.parameter.loadBy != undefined) 170 | getItemnsWithParameters(field, callback); 171 | else 172 | getItemnsWithOutParameters(field, callback); 173 | } 174 | var count = 0; 175 | var setUpFillSelectFields = function (selectFields, callback) { 176 | var totalSelects = selectFields.length - 1; 177 | var countFill = 0; 178 | function fillSelect() { 179 | if (countFill > totalSelects) { 180 | if (typeof callback === 'function') 181 | callback(); 182 | return; 183 | }; 184 | var currentField = selectFields[countFill]; 185 | getItemns(currentField, function (items) { 186 | var td = row.find("td:nth-child(" + currentField.column + ")"); 187 | var currentValue = td.find("input[name='" + currentField.name + "-hidden']").val(); 188 | var currentSelect = row.find("select[name='" + currentField.name + "']"); 189 | console.log(totalSelects + "currentField: " + currentField.name); 190 | console.log(totalSelects + "currentField: " + currentField.name + " items: " + items.length); 191 | items.forEach(function (item) { 192 | var isSelected = item.html == currentValue; 193 | currentSelect.append($("", { value: item.value, html: item.html, selected: isSelected })); 194 | }); 195 | countFill++; 196 | fillSelect(); 197 | }); 198 | } 199 | fillSelect(); 200 | } 201 | var setUpSelectsDependents = function () { 202 | var selectFields = fields.filter(function (field) { return field.type === fieldType.select; }); 203 | var selectFieldsDependents = selectFields.filter(function (field) { return typeof field.parameter.loadBy === "object"; }); 204 | setUpFillSelectFields(selectFieldsDependents); 205 | } 206 | var setUpSelects = function (callback) { 207 | var selectFields = fields.filter(function (field) { return field.type === fieldType.select; }); 208 | var selectFieldsIndependents = selectFields.filter(function (field) { return field.parameter.loadBy === undefined; }); 209 | setUpFillSelectFields(selectFieldsIndependents, setUpSelectsDependents); 210 | } 211 | 212 | this.setUpInputsOnRow = function (callback) { 213 | fields.forEach(function (currentField) { 214 | switch (currentField.type.toLowerCase()) { 215 | case fieldType.text: create.textField(currentField); break; 216 | case fieldType.numeric: create.numericField(currentField); break; 217 | case fieldType.decimal: create.decimalField(currentField); break; 218 | case fieldType.bool: create.boolField(currentField); break; 219 | case fieldType.date: create.dateField(currentField); break; 220 | case fieldType.datetime: create.dateTimeField(currentField); break; 221 | case fieldType.select: create.selectField(currentField); break; 222 | default: break; 223 | } 224 | }); 225 | setUpSelects(setUpSelectsDependents); 226 | } 227 | this.setDownInputsOnRow = function (toOldValue) { 228 | fields.forEach(function (currentField) { 229 | switch (currentField.type.toLowerCase()) { 230 | case fieldType.text: revert.textField(currentField, toOldValue); break; 231 | case fieldType.numeric: revert.numericField(currentField, toOldValue); break; 232 | case fieldType.decimal: revert.decimalField(currentField, toOldValue); break; 233 | case fieldType.bool: revert.boolField(currentField, toOldValue); break; 234 | case fieldType.date: revert.dateField(currentField, toOldValue); break; 235 | case fieldType.datetime: revert.dateTimeField(currentField, toOldValue); break; 236 | case fieldType.select: revert.selectField(currentField, toOldValue); break; 237 | default: break; 238 | } 239 | }); 240 | } 241 | } 242 | 243 | var setUrl = function (urls, parameters) { 244 | if (urls.lastIndexOf("/") == urls.length - 1) 245 | return urls + "?" + parameters; 246 | else 247 | return urls + "/?" + parameters; 248 | } 249 | 250 | var inLineGridConfig = function (container, options) { 251 | var defaultOptions = { 252 | controller: { 253 | uriSave: "", 254 | uriUpdate: "", 255 | uriDelete: "", 256 | messageReturn: function () { } 257 | }, 258 | fields: [] 259 | } 260 | var fields = options.fields || defaultOptions.fields; 261 | 262 | var getFields = function () { 263 | if (fields === undefined) return null; 264 | return fields.filter(function (field) { return field.type != "buttons" && field.edit === true; }); 265 | } 266 | this.getFieldsButton = function () { 267 | if (fields === undefined) return null; 268 | return fields.filter(function (field) { return field.type == "buttons"; }); 269 | } 270 | 271 | var showCancelApplyChangesButtons = function (tr) { 272 | tr.find("." + divClassButtonsEditDelete).toggle(); 273 | tr.find("." + divClassButtonsApplyCancel).toggle(); 274 | } 275 | var getKeyAsUrlParameter = function (currentRow) { 276 | if (fields === undefined) return null; 277 | var keys = fields.filter(function (field) { return field.isKey === true }); 278 | var keyAsUrl = ""; 279 | var count = 0; 280 | keys.forEach(function (current) { 281 | var value = currentRow.find("td:nth-child(" + current.column + ")").html(); 282 | keyAsUrl += current.name + "=" + value; 283 | if (count > 0 && count < keys.length) 284 | keyAsUrl += "&"; 285 | count++; 286 | }); 287 | return keyAsUrl; 288 | }; 289 | 290 | this.addItemInGrid = function (event) { 291 | var currentRow = $(this).closest("tr"); 292 | }; 293 | this.editItemInGrid = function (event) { 294 | var currentRow = $(this).closest("tr"); 295 | showCancelApplyChangesButtons(currentRow); 296 | var editFields = getFields(); 297 | var fieldElement = new fieldElements(editFields, currentRow); 298 | fieldElement.setUpInputsOnRow(); 299 | }; 300 | this.removeItemFromGrid = function (event) { 301 | var currentRow = $(this).closest("tr"); 302 | var keysToDelete = getKeyAsUrlParameter(currentRow); 303 | var url = setUrl(options.controller.uriDelete.url, keysToDelete); 304 | $.ajax({ 305 | method: options.controller.uriDelete.verb, 306 | url: url, 307 | cache: false, 308 | error: function (XMLHttpRequest, textStatus, errorThrown) { 309 | options.controller.messageReturn(errorThrown); 310 | }, 311 | success: function (data) { 312 | if (data.result) { 313 | currentRow.remove(); 314 | } 315 | options.controller.messageReturn(data); 316 | } 317 | }); 318 | } 319 | this.cancelEditOrAddInGrid = function (event) { 320 | var currentRow = $(this).closest("tr"); 321 | var editFields = getFields(); 322 | var fieldElement = new fieldElements(editFields, currentRow); 323 | fieldElement.setDownInputsOnRow(true); 324 | showCancelApplyChangesButtons(currentRow); 325 | } 326 | }; 327 | 328 | this.setUpGrid = function () { 329 | var config = new inLineGridConfig(container, options); 330 | var buttons = config.getFieldsButton(); 331 | var addButtonOnGrid = function (buttons, tdIdx) { 332 | var rows = container.find("tbody tr"); 333 | rows.each(function (idx) { 334 | var td = $(this).find("td:nth-child(" + tdIdx + ")"); 335 | var divEditDelete = $("", { "class": divClassButtonsEditDelete }); 336 | buttons.editDelete.forEach(function (button) { 337 | divEditDelete.append($("", { name: button.name, "class": button.class, type: "button" }).append($("", { "class": button.icon }))); 338 | }); 339 | td.append(divEditDelete); 340 | 341 | var divCancelApply = $("", { "class": divClassButtonsApplyCancel, }); 342 | buttons.cancelApply.forEach(function (button) { 343 | divCancelApply.append($("", { name: button.name, "class": button.class, type: "button" }).append($("", { "class": button.icon }))); 344 | }); 345 | divCancelApply.hide(); 346 | td.append(divCancelApply); 347 | 348 | var divCustom = $("", { "class": divClassButtonsCustom, }); 349 | buttons.custom.forEach(function (button) { 350 | var btn = $("", { name: button.name, "class": button.class, type: "button" }); 351 | var icon = $("", { "class": button.icon }); 352 | btn.append(icon); 353 | btn.append(button.text); 354 | divCustom.append(btn); 355 | if (!button.showcheck) 356 | btn.addClass("hide"); 357 | }); 358 | td.append(divCustom); 359 | }); 360 | }; 361 | var setUpEventButtons = function (buttons) { 362 | var typeBtn = { 363 | apply: "apply", 364 | cancel: "cancel", 365 | edit: "edit", 366 | remove: "remove", 367 | custom: "custom", 368 | } 369 | buttons.forEach(function (currentBtn) { 370 | var actionButton = container.find("[name='" + currentBtn.name + "']"); 371 | switch (currentBtn.type) { 372 | case typeBtn.apply: actionButton.on("click", config.addItemInGrid); break; 373 | case typeBtn.cancel: actionButton.on("click", config.cancelEditOrAddInGrid); break; 374 | case typeBtn.edit: actionButton.on("click", config.editItemInGrid); break; 375 | case typeBtn.remove: actionButton.on("click", config.removeItemFromGrid); break; 376 | case typeBtn.custom: actionButton.on("click", currentBtn.onClick); break; 377 | default: break; 378 | } 379 | }); 380 | }; 381 | 382 | buttons.forEach(function (current) { 383 | addButtonOnGrid(current.buttons, current.column); 384 | setUpEventButtons(current.buttons.editDelete); 385 | setUpEventButtons(current.buttons.cancelApply); 386 | setUpEventButtons(current.buttons.custom); 387 | }); 388 | }; 389 | } 390 | $.fn.ilge = function (options) { 391 | var inlineGrigEditing = new inLineGridEditing(this, options); 392 | inlineGrigEditing.setUpGrid(); 393 | } 394 | })(jQuery); --------------------------------------------------------------------------------