mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
refactor observable
This commit is contained in:
parent
c7c115452a
commit
bc781728aa
@ -1,8 +1,8 @@
|
||||
module.exports = {
|
||||
bindKey(element, key, handler) {
|
||||
bindKey(element, key, listener) {
|
||||
element.addEventListener('keydown', (e) => {
|
||||
if (key === this.getKey(e)) {
|
||||
handler(e);
|
||||
listener(e);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -133,13 +133,18 @@ class BaseControl {
|
||||
async handleChange(event) {
|
||||
let value = await this.parse(this.getInputValue());
|
||||
value = await this.validate(value);
|
||||
await this.updateDocValue(value);
|
||||
}
|
||||
|
||||
async updateDocValue(value) {
|
||||
if (this.doc[this.fieldname] !== value) {
|
||||
if (this.doc.set) {
|
||||
await this.doc.set(this.fieldname, value);
|
||||
}
|
||||
if (this.parent_control) {
|
||||
if (this.parentControl) {
|
||||
// its a child
|
||||
this.doc[this.fieldname] = value;
|
||||
await this.parent_control.doc.set(this.fieldname, this.parent_control.getInputValue());
|
||||
await this.parentControl.doc.set(this.fieldname, this.parentControl.getInputValue());
|
||||
} else {
|
||||
// parent
|
||||
await this.doc.set(this.fieldname, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,7 @@ class LinkControl extends BaseControl {
|
||||
autoFirst: true,
|
||||
minChars: 0,
|
||||
maxItems: 99,
|
||||
filter: function() {
|
||||
return true;
|
||||
}
|
||||
filter: () => true,
|
||||
});
|
||||
|
||||
// rebuild the list on input
|
||||
@ -36,8 +34,8 @@ class LinkControl extends BaseControl {
|
||||
e.preventDefault();
|
||||
const newDoc = await frappe.getNewDoc(this.target);
|
||||
const formModal = frappe.desk.showFormModal(this.target, newDoc.name);
|
||||
formModal.form.once('submit', () => {
|
||||
this.form.doc.set(this.fieldname, formModal.form.doc.name);
|
||||
formModal.form.once('submit', async () => {
|
||||
await this.updateDocValue(formModal.form.doc.name);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
@ -17,30 +17,37 @@ class TableControl extends BaseControl {
|
||||
${frappe._("Remove")}</button>
|
||||
</div>`;
|
||||
|
||||
this.datatable = new DataTable(this.wrapper.querySelector('.datatable-wrapper'), {
|
||||
columns: this.getColumns(),
|
||||
data: this.getTableData(),
|
||||
takeAvailableSpace: true,
|
||||
addCheckboxColumn: true,
|
||||
editing: this.getTableInput.bind(this),
|
||||
});
|
||||
|
||||
this.wrapper.querySelector('.btn-add').addEventListener('click', async (event) => {
|
||||
this.doc[this.fieldname].push({});
|
||||
await this.doc.commit();
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.wrapper.querySelector('.btn-remove').addEventListener('click', async (event) => {
|
||||
let checked = this.datatable.rowmanager.getCheckedRows();
|
||||
this.doc[this.fieldname] = this.doc[this.fieldname].filter(d => !checked.includes(d.idx));
|
||||
await this.doc.commit();
|
||||
this.refresh();
|
||||
this.datatable.rowmanager.checkAll(false);
|
||||
});
|
||||
this.makeDatatable();
|
||||
this.setupToolbar();
|
||||
}
|
||||
}
|
||||
|
||||
makeDatatable() {
|
||||
this.datatable = new DataTable(this.wrapper.querySelector('.datatable-wrapper'), {
|
||||
columns: this.getColumns(),
|
||||
data: this.getTableData(),
|
||||
takeAvailableSpace: true,
|
||||
addCheckboxColumn: true,
|
||||
getEditor: this.getTableInput.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
setupToolbar() {
|
||||
this.wrapper.querySelector('.btn-add').addEventListener('click', async (event) => {
|
||||
this.doc[this.fieldname].push({});
|
||||
await this.doc.commit();
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.wrapper.querySelector('.btn-remove').addEventListener('click', async (event) => {
|
||||
let checked = this.datatable.rowmanager.getCheckedRows();
|
||||
this.doc[this.fieldname] = this.doc[this.fieldname].filter(d => !checked.includes(d.idx));
|
||||
await this.doc.commit();
|
||||
this.refresh();
|
||||
this.datatable.rowmanager.checkAll(false);
|
||||
});
|
||||
}
|
||||
|
||||
getInputValue() {
|
||||
return this.doc[this.fieldname];
|
||||
}
|
||||
@ -73,7 +80,7 @@ class TableControl extends BaseControl {
|
||||
return {
|
||||
initValue: (value, rowIndex, column) => {
|
||||
column.activeControl = control;
|
||||
control.parent_control = this;
|
||||
control.parentControl = this;
|
||||
control.doc = this.doc[this.fieldname][rowIndex];
|
||||
control.set_focus();
|
||||
return control.setInputValue(value);
|
||||
|
@ -80,8 +80,8 @@ module.exports = class BaseForm extends Observable {
|
||||
|
||||
async bindEvents(doc) {
|
||||
if (this.doc) {
|
||||
// clear handlers of outgoing doc
|
||||
this.doc.clearHandlers();
|
||||
// clear listeners of outgoing doc
|
||||
this.doc.clearListeners();
|
||||
}
|
||||
this.clearAlert();
|
||||
this.doc = doc;
|
||||
@ -89,13 +89,13 @@ module.exports = class BaseForm extends Observable {
|
||||
control.bind(this.doc);
|
||||
}
|
||||
|
||||
this.setupChangeHandler();
|
||||
this.setupChangeListener();
|
||||
this.trigger('use', {doc:doc});
|
||||
}
|
||||
|
||||
setupChangeHandler() {
|
||||
setupChangeListener() {
|
||||
// refresh value in control
|
||||
this.doc.addHandler('change', (params) => {
|
||||
this.doc.on('change', (params) => {
|
||||
if (params.fieldname) {
|
||||
// only single value changed
|
||||
let control = this.controls[params.fieldname];
|
||||
|
@ -1,26 +1,16 @@
|
||||
const frappe = require('frappejs');
|
||||
const Observable = require('frappejs/utils/observable');
|
||||
|
||||
module.exports = class BaseDocument {
|
||||
module.exports = class BaseDocument extends Observable {
|
||||
constructor(data) {
|
||||
this.handlers = {};
|
||||
super();
|
||||
this.fetchValues = {};
|
||||
this.setup();
|
||||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
setup() {
|
||||
// add handlers
|
||||
}
|
||||
|
||||
clearHandlers() {
|
||||
this.handlers = {};
|
||||
}
|
||||
|
||||
addHandler(key, method) {
|
||||
if (!this.handlers[key]) {
|
||||
this.handlers[key] = [];
|
||||
}
|
||||
this.handlers[key].push(method || key);
|
||||
// add listeners
|
||||
}
|
||||
|
||||
get meta() {
|
||||
@ -112,7 +102,7 @@ module.exports = class BaseDocument {
|
||||
async validateField(key, value) {
|
||||
let field = this.meta.getField(key);
|
||||
if (field && field.fieldtype == 'Select') {
|
||||
return this.meta.validate_select(field, value);
|
||||
return this.meta.validateSelect(field, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@ -208,7 +198,6 @@ module.exports = class BaseDocument {
|
||||
this.setChildIdx();
|
||||
await this.applyFormulae();
|
||||
await this.trigger('validate');
|
||||
await this.trigger('commit');
|
||||
}
|
||||
|
||||
async insert() {
|
||||
@ -237,16 +226,11 @@ module.exports = class BaseDocument {
|
||||
await this.trigger('after_delete');
|
||||
}
|
||||
|
||||
async trigger(key, params) {
|
||||
if (this.handlers[key]) {
|
||||
for (let method of this.handlers[key]) {
|
||||
if (typeof method === 'string') {
|
||||
await this[method](params);
|
||||
} else {
|
||||
await method(params);
|
||||
}
|
||||
}
|
||||
async trigger(event, params) {
|
||||
if (this[event]) {
|
||||
await this[event](params);
|
||||
}
|
||||
await super.trigger(event, params);
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
@ -4,7 +4,6 @@ const frappe = require('frappejs');
|
||||
module.exports = class BaseMeta extends BaseDocument {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
this.event_handlers = {};
|
||||
this.list_options = {
|
||||
fields: ['name', 'modified']
|
||||
};
|
||||
@ -54,13 +53,6 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
return this._hasFormulae;
|
||||
}
|
||||
|
||||
on(key, fn) {
|
||||
if (!this.event_handlers[key]) {
|
||||
this.event_handlers[key] = [];
|
||||
}
|
||||
this.event_handlers[key].push(fn);
|
||||
}
|
||||
|
||||
async set(fieldname, value) {
|
||||
this[fieldname] = value;
|
||||
await this.trigger(fieldname);
|
||||
@ -141,7 +133,7 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
return this._keywordFields;
|
||||
}
|
||||
|
||||
validate_select(field, value) {
|
||||
validateSelect(field, value) {
|
||||
let options = field.options;
|
||||
if (typeof options === 'string') {
|
||||
// values given as string
|
||||
@ -153,16 +145,12 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
return value;
|
||||
}
|
||||
|
||||
async trigger(key, event = {}) {
|
||||
Object.assign(event, {
|
||||
async trigger(event, params = {}) {
|
||||
Object.assign(params, {
|
||||
doc: this,
|
||||
name: key
|
||||
name: event
|
||||
});
|
||||
|
||||
if (this.event_handlers[key]) {
|
||||
for (var handler of this.event_handlers[key]) {
|
||||
await handler(event);
|
||||
}
|
||||
}
|
||||
await super.trigger(event, params);
|
||||
}
|
||||
}
|
@ -8,9 +8,6 @@ class NumberSeriesMeta extends BaseMeta {
|
||||
}
|
||||
|
||||
class NumberSeries extends BaseDocument {
|
||||
setup() {
|
||||
this.addHandler('validate');
|
||||
}
|
||||
validate() {
|
||||
if (this.current===null || this.current===undefined) {
|
||||
this.current = 0;
|
||||
|
@ -8,9 +8,6 @@ class ToDoMeta extends BaseMeta {
|
||||
}
|
||||
|
||||
class ToDo extends BaseDocument {
|
||||
setup() {
|
||||
this.addHandler('validate');
|
||||
}
|
||||
validate() {
|
||||
if (!this.status) {
|
||||
this.status = 'Open';
|
||||
|
@ -1,9 +1,18 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = {
|
||||
asyncHandler(fn) {
|
||||
return (req, res, next) => Promise.resolve(fn(req, res, next))
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
// handle error
|
||||
res.status(err.status_code || 500).send({error: err.message});
|
||||
});
|
||||
},
|
||||
|
||||
setup(app) {
|
||||
// get list
|
||||
app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
|
||||
app.get('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
||||
for (let key of ['fields', 'filters']) {
|
||||
if (request.query[key]) {
|
||||
request.query[key] = JSON.parse(request.query[key]);
|
||||
@ -24,7 +33,7 @@ module.exports = {
|
||||
}));
|
||||
|
||||
// create
|
||||
app.post('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
|
||||
app.post('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
||||
let data = request.body;
|
||||
data.doctype = request.params.doctype;
|
||||
let doc = frappe.newDoc(data);
|
||||
@ -34,7 +43,7 @@ module.exports = {
|
||||
}));
|
||||
|
||||
// update
|
||||
app.put('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
|
||||
app.put('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
||||
let data = request.body;
|
||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
||||
Object.assign(doc, data);
|
||||
@ -45,26 +54,26 @@ module.exports = {
|
||||
|
||||
|
||||
// get document
|
||||
app.get('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
|
||||
app.get('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
||||
return response.json(doc.getValidDict());
|
||||
}));
|
||||
|
||||
// get value
|
||||
app.get('/api/resource/:doctype/:name/:fieldname', frappe.async_handler(async function(request, response) {
|
||||
app.get('/api/resource/:doctype/:name/:fieldname', this.asyncHandler(async function(request, response) {
|
||||
let value = await frappe.db.getValue(request.params.doctype, request.params.name, request.params.fieldname);
|
||||
return response.json({value: value});
|
||||
}));
|
||||
|
||||
// delete
|
||||
app.delete('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
|
||||
app.delete('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name)
|
||||
await doc.delete();
|
||||
return response.json({});
|
||||
}));
|
||||
|
||||
// delete many
|
||||
app.delete('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
|
||||
app.delete('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
||||
let names = request.body;
|
||||
for (let name of names) {
|
||||
let doc = await frappe.getDoc(request.params.doctype, name);
|
||||
|
@ -19,15 +19,6 @@ module.exports = {
|
||||
return Math.random().toString(36).substr(3);
|
||||
},
|
||||
|
||||
async_handler(fn) {
|
||||
return (req, res, next) => Promise.resolve(fn(req, res, next))
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
// handle error
|
||||
res.status(err.status_code || 500).send({error: err.message});
|
||||
});
|
||||
},
|
||||
|
||||
async sleep(seconds) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, seconds * 1000);
|
||||
|
@ -1,35 +1,42 @@
|
||||
module.exports = class Observable {
|
||||
on(event, handler) {
|
||||
this._addHandler('_handlers', event, handler);
|
||||
on(event, listener) {
|
||||
this._addListener('_listeners', event, listener);
|
||||
}
|
||||
|
||||
once(event, handler) {
|
||||
this._addHandler('_onceHandlers', event, handler);
|
||||
once(event, listener) {
|
||||
this._addListener('_onceListeners', event, listener);
|
||||
}
|
||||
|
||||
async trigger(event, params) {
|
||||
await this._triggerHandler('_handlers', event, params);
|
||||
await this._triggerHandler('_onceHandlers', event, params);
|
||||
if (this._onceHandlers && this._onceHandlers[event]) {
|
||||
delete this._onceHandlers[event];
|
||||
await this._triggerEvent('_listeners', event, params);
|
||||
await this._triggerEvent('_onceListeners', event, params);
|
||||
|
||||
// clear once-listeners
|
||||
if (this._onceListeners && this._onceListeners[event]) {
|
||||
delete this._onceListeners[event];
|
||||
}
|
||||
}
|
||||
|
||||
_addHandler(name, event, handler) {
|
||||
_addListener(name, event, listener) {
|
||||
if (!this[name]) {
|
||||
this[name] = {};
|
||||
}
|
||||
if (!this[name][event]) {
|
||||
this[name][event] = [];
|
||||
}
|
||||
this[name][event].push(handler);
|
||||
this[name][event].push(listener);
|
||||
}
|
||||
|
||||
async _triggerHandler(name, event, params) {
|
||||
async _triggerEvent(name, event, params) {
|
||||
if (this[name] && this[name][event]) {
|
||||
for (let handler of this[name][event]) {
|
||||
await handler(params);
|
||||
for (let listener of this[name][event]) {
|
||||
await listener(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearListeners() {
|
||||
this._listeners = {};
|
||||
this._onceListeners = {};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user