2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 23:30:56 +00:00

refactor observable

This commit is contained in:
Rushabh Mehta 2018-02-13 17:24:57 +05:30
parent c7c115452a
commit bc781728aa
12 changed files with 99 additions and 116 deletions

View File

@ -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);
}
})
},

View File

@ -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);
}
}
}

View File

@ -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);
})
}
});

View File

@ -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);

View File

@ -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];

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -8,9 +8,6 @@ class ToDoMeta extends BaseMeta {
}
class ToDo extends BaseDocument {
setup() {
this.addHandler('validate');
}
validate() {
if (!this.status) {
this.status = 'Open';

View File

@ -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);

View File

@ -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);

View File

@ -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 = {};
}
}