Skip to content

Commit 02ad677

Browse files
author
App Generator
committed
Release v0.0.4 - Added Inline Edit
1 parent 5d9f431 commit 02ad677

File tree

8 files changed

+222
-8
lines changed

8 files changed

+222
-8
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change Log
22

3+
## [0.0.4] 2022-02-07
4+
### Improvements
5+
6+
- Added `inline edit` for rows
7+
38
## [0.0.3] 2022-02-07
49
### Improvements
510

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ Open-source sample provided by [AppSeed](https://appseed.us). The project implem
88
99
- DataTables managed by `Simple-DataTables` (Vanilla) JS
1010
- Stack: Flask, SqlAlchemy, Flask-Migrate, Flask-RestX
11-
- Data Tables Implementations
11+
- Data Tables Implementation(s):
1212
- Loaded from `Data` table by a controller (route)
1313
- Served by `/api/data` API node and consumed from JS
1414
- Loaded without any processing from a file:
15-
- `app/static/datatables/data.json`
15+
- `app/static/datatables/data.json`
16+
- Inline Edit / Delete
1617
- UI Kit: **Volt Dashboard** (Free Version) by **Themesberg**
1718
- Deployment scripts: Docker, Gunicorn/Nginx, HEROKU
1819
- Support via **Github** (issues tracker) and [Discord](https://discord.gg/fZC6hup).

api/routes.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
"type": fields.String(required=False, default='transaction'),
4848
})
4949

50+
# Used to validate input data for single field supdate
51+
update_field_model = rest_api.model('UpdateFieldModel', {"data_name" : fields.String(required=True, min_length=1, max_length=128),
52+
"data_value": fields.String(required=True, min_length=1, max_length=128)
53+
})
54+
5055
"""
5156
Return Files - Served by @APP object
5257
"""
@@ -221,4 +226,47 @@ def delete(self, id):
221226
mimetype='application/json'
222227
)
223228

224-
return response
229+
return response
230+
231+
@rest_api.route('/api/data/field/<int:id>')
232+
class FieldManager(Resource):
233+
234+
"""
235+
Return Item
236+
"""
237+
@rest_api.expect(update_field_model, validate=True)
238+
def put(self, id):
239+
240+
response_data = []
241+
status = 400
242+
243+
# Read ALL input
244+
req_data = request.get_json()
245+
246+
item = Data.get_by_id(id)
247+
248+
if item:
249+
250+
status = 200
251+
252+
data_name = req_data.get("data_name" )
253+
data_value = req_data.get("data_value" )
254+
255+
if data_name == 'code':
256+
item.code = data_value
257+
258+
if data_name == 'name':
259+
item.name = data_value
260+
261+
# Save the data
262+
item.save()
263+
264+
response_data = item.toDICT()
265+
266+
response = app.response_class(
267+
response=json.dumps( response_data ),
268+
status=status,
269+
mimetype='application/json'
270+
)
271+
272+
return response

app/static/datatables/app.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
3+
Copyright (c) 2019 - present AppSeed.us
4+
5+
*/
6+
7+
.hoverTable tr:hover {
8+
background-color: #ffff99;
9+
}
10+
11+
tr.editing {
12+
border: 1px solid green !important;
13+
font-weight: bold;
14+
}

app/static/datatables/app.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
3+
Copyright (c) 2019 - present AppSeed.us
4+
5+
*/
6+
7+
"use strict";
8+
const doc = document;
9+
doc.addEventListener("DOMContentLoaded", function(event) {
10+
11+
var xhr1 = new XMLHttpRequest();
12+
13+
document.addEventListener('keydown', event => {
14+
15+
// Catch 'ENTER' event
16+
if (event.key === 'Enter') {
17+
18+
// If applies on a TD
19+
if (event.target.matches('td.editable')) {
20+
21+
// Drop the default event
22+
event.preventDefault();
23+
24+
// Log the clicked element in the console
25+
// console.log(event.target);
26+
console.log( 'EVENT -> Cell Save' );
27+
28+
let td = event.target;
29+
let tr = event.target.closest('tr.editable');
30+
31+
let data_id = tr.dataset.id;
32+
let data_name = td.dataset.name;
33+
let data_value = td.textContent;
34+
35+
console.log( ' >> Save Data [' + data_id + '] ' + data_name + ' = [' + data_value + ']' );
36+
37+
xhr1.open("PUT", "/api/data/field/" + data_id);
38+
xhr1.setRequestHeader("Content-Type", "application/json");
39+
40+
// Check Status
41+
xhr1.onreadystatechange = function() {
42+
43+
if (this.status == 200 && this.readyState == 4) {
44+
console.log( ' >>> Editing OK' );
45+
}
46+
47+
if (this.status != 200) {
48+
console.log( ' >>> Editing ERR ' + this.status );
49+
}
50+
51+
};//end onreadystate
52+
53+
xhr1.send(JSON.stringify( { data_name: data_name, data_value: data_value } ));
54+
55+
// Disable 'EDITABLE' property
56+
td.setAttribute("contenteditable", "false");
57+
58+
} // END if (event.target.matches('td.editable')) {
59+
60+
} // END if (event.key === 'Enter') {
61+
62+
});
63+
64+
// handle Clicks
65+
document.addEventListener('click', function (event) {
66+
67+
// Cell Edit event
68+
if (event.target.matches('td.editable')) {
69+
70+
// Drop the default event
71+
event.preventDefault();
72+
73+
// Log the clicked element in the console
74+
// console.log(event.target);
75+
console.log( 'EVENT -> Cell Edit' );
76+
77+
let td = event.target;
78+
let tr = event.target.closest('tr.editable');
79+
80+
let data_id = tr.dataset.id;
81+
let data_name = td.dataset.name;
82+
83+
console.log( ' >> Edit Data ' + data_id + ' -> ' + data_name );
84+
85+
// Enable 'EDITABLE' property
86+
td.setAttribute("contenteditable", "true");
87+
}
88+
89+
// ROW Delete event
90+
if (event.target.matches('.row-delete')) {
91+
92+
// Drop the default event
93+
event.preventDefault();
94+
95+
// console.log(event.target);
96+
console.log( 'EVENT -> ROW Delete' );
97+
98+
let tr = event.target.closest('tr.editable');
99+
let data_id = tr.dataset.id;
100+
101+
console.log( ' >>> DELETE ID = ' + data_id );
102+
103+
xhr1.open('DELETE', "/api/data/" + data_id, true);
104+
105+
// Check Status
106+
xhr1.onreadystatechange = function() {
107+
108+
if (this.status == 200 && this.readyState == 4) {
109+
console.log( ' >>> Deletion OK' );
110+
tr.remove();
111+
}
112+
113+
if (this.status != 200) {
114+
console.log( ' >>> Deletion ERR ' + this.status );
115+
}
116+
117+
};//end onreadystate
118+
119+
xhr1.send();
120+
121+
}
122+
123+
}, false);
124+
125+
});

app/templates/datatables/datatables.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@latest/dist/style.css" rel="stylesheet" type="text/css">
99

10+
<!-- APP Style -->
11+
<link type="text/css" href="/static/datatables/app.css" rel="stylesheet">
12+
1013
{% endblock stylesheets %}
1114

1215
{% block content %}
@@ -58,6 +61,9 @@ <h1 class="h4">Simple DataTables</h1>
5861
<!-- CDN -->
5962
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest"></script>
6063

64+
<!-- APP Actions -->
65+
<script src="/static/datatables/app.js"></script>
66+
6167
<!-- Custom Code -->
6268
<script>
6369
const table = new simpleDatatables.DataTable("table")

app/views.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def datatables():
5858
'''
5959

6060
table = ''
61-
table += '<table class="table">'
61+
table += '<table class="table hoverTable">'
6262
table += ' <thead>'
6363
table += ' <tr>'
6464
table += ' <th>ID</th>'
@@ -67,6 +67,7 @@ def datatables():
6767
table += ' <th>Price</th>'
6868
table += ' <th>Currency</th>'
6969
table += ' <th>Timestamp</th>'
70+
table += ' <th>&nbsp;</th>'
7071
table += ' </tr>'
7172
table += ' </thead>'
7273
table += ' <tbody>'
@@ -78,11 +79,25 @@ def datatables():
7879

7980
# Format the date
8081
ts = datetime.utcfromtimestamp(item.ts).strftime('%Y-%m-%d')
81-
table += ' <tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td></tr>'.format( item.id, item.code, item.name, item.value, item.currency, ts )
82+
#table += ' <tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td></tr>'.format( item.id, item.code, item.name, item.value, item.currency, ts )
83+
84+
# Open ROW
85+
table += '<tr data-id="' + str( item.id ) + '" class="editable">'
86+
87+
table += '<td data-name="id">' + str( item.id ) + '</td>' # ID
88+
table += '<td class="editable" data-name="code">' + item.code + '</td>' # ID
89+
table += '<td class="editable" data-name="name">' + item.name + '</td>' # ID
90+
table += '<td class="editable" data-name="value">' + str( item.value ) + '</td>' # ID
91+
table += '<td class="editable" data-name="currency">' + item.currency + '</td>' # ID
92+
table += '<td data-name="ts">' + ts + '</td>' # ID
93+
94+
table += '<td><a href="#" class="row-delete text-danger me-3">delete</a></td>' # ID
95+
96+
# Close ROW
97+
table += '</tr>'
8298

8399
table += ' </tbody>'
84-
table += '</table>'
85-
100+
table += '</table>'
86101

87102
return render_template( 'datatables/datatables.html', segment='datatables', table=table )
88103

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "flask-volt-datatables",
33
"mastertemplate": "flask-volt-datatables",
4-
"version": "0.0.3",
4+
"version": "0.0.4",
55
"description": "Template project - Flask/Jinja2 Theme",
66
"scripts": {},
77
"repository": {

0 commit comments

Comments
 (0)