mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
[refactor] extending forms and lists
This commit is contained in:
parent
1e9701d0be
commit
84d39be287
@ -23,11 +23,15 @@ class sqliteDatabase {
|
||||
}
|
||||
|
||||
async migrate() {
|
||||
for (let doctype in frappe.models.data.doctype) {
|
||||
if (await this.table_exists(doctype)) {
|
||||
await this.alter_table(doctype);
|
||||
} else {
|
||||
await this.create_table(doctype);
|
||||
for (let doctype in frappe.modules) {
|
||||
// check if controller module
|
||||
if (frappe.modules[doctype].Meta) {
|
||||
if (await this.table_exists(doctype)) {
|
||||
await this.alter_table(doctype);
|
||||
} else {
|
||||
await this.create_table(doctype);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
await this.commit();
|
||||
|
@ -2,8 +2,8 @@ const frappe = require('frappejs');
|
||||
const Search = require('./search');
|
||||
const Router = require('frappejs/common/router');
|
||||
const Page = require('frappejs/client/view/page');
|
||||
const List = require('frappejs/client/view/list');
|
||||
const Form = require('frappejs/client/view/form');
|
||||
const BaseList = require('frappejs/client/view/list');
|
||||
const BaseForm = require('frappejs/client/view/form');
|
||||
const Navbar = require('./navbar');
|
||||
|
||||
module.exports = class Desk {
|
||||
@ -58,8 +58,8 @@ module.exports = class Desk {
|
||||
|
||||
get_list_page(doctype) {
|
||||
if (!this.pages.lists[doctype]) {
|
||||
let page = new Page('List ' + doctype);
|
||||
page.list = new List({
|
||||
let page = new Page('List ' + frappe.get_meta(doctype).name);
|
||||
page.list = new (this.get_view_class(doctype, 'List', BaseList))({
|
||||
doctype: doctype,
|
||||
parent: page.body
|
||||
});
|
||||
@ -73,8 +73,8 @@ module.exports = class Desk {
|
||||
|
||||
get_form_page(doctype) {
|
||||
if (!this.pages.forms[doctype]) {
|
||||
let page = new Page('Edit ' + doctype);
|
||||
page.form = new Form({
|
||||
let page = new Page('Edit ' + frappe.get_meta(doctype).name);
|
||||
page.form = new (this.get_view_class(doctype, 'Form', BaseForm))({
|
||||
doctype: doctype,
|
||||
parent: page.body
|
||||
});
|
||||
@ -91,6 +91,20 @@ module.exports = class Desk {
|
||||
return this.pages.forms[doctype];
|
||||
}
|
||||
|
||||
get_view_class(doctype, class_name, default_class) {
|
||||
let client_module = this.get_client_module(doctype);
|
||||
if (client_module && client_module[class_name]) {
|
||||
return client_module[class_name];
|
||||
} else {
|
||||
return default_class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get_client_module(doctype) {
|
||||
return frappe.modules[`${doctype}_client`];
|
||||
}
|
||||
|
||||
add_sidebar_item(label, action) {
|
||||
let item = frappe.ui.add('a', '', frappe.ui.add('p', null, frappe.desk.sidebar));
|
||||
item.textContent = label;
|
||||
|
@ -97,6 +97,14 @@ class BaseControl {
|
||||
value = await this.validate(value);
|
||||
await this.doc.set(this.fieldname, value);
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.input.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.input.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseControl;
|
@ -1,7 +1,7 @@
|
||||
const frappe = require('frappejs');
|
||||
const controls = require('./controls');
|
||||
|
||||
module.exports = class Form {
|
||||
module.exports = class BaseForm {
|
||||
constructor({doctype, parent, submit_label='Submit'}) {
|
||||
this.parent = parent;
|
||||
this.doctype = doctype;
|
||||
@ -11,6 +11,9 @@ module.exports = class Form {
|
||||
this.controls_list = [];
|
||||
|
||||
this.meta = frappe.get_meta(this.doctype);
|
||||
if (this.setup) {
|
||||
this.setup();
|
||||
}
|
||||
this.make();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = class List {
|
||||
module.exports = class BaseList {
|
||||
constructor({doctype, parent, fields}) {
|
||||
this.doctype = doctype;
|
||||
this.parent = parent;
|
||||
@ -18,13 +18,8 @@ module.exports = class List {
|
||||
|
||||
async run() {
|
||||
this.make_body();
|
||||
this.set_filters();
|
||||
|
||||
let data = await this.meta.get_list({
|
||||
filters: this.filters,
|
||||
start:this.start,
|
||||
limit:this.page_length + 1
|
||||
});
|
||||
let data = await this.get_data();
|
||||
|
||||
for (let i=0; i< Math.min(this.page_length, data.length); i++) {
|
||||
this.render_row(this.start + i, data[i]);
|
||||
@ -40,16 +35,31 @@ module.exports = class List {
|
||||
this.update_more(data.length > this.page_length);
|
||||
}
|
||||
|
||||
async get_data() {
|
||||
return await frappe.db.get_all({
|
||||
doctype: this.doctype,
|
||||
fields: this.get_fields(),
|
||||
filters: this.get_filters(),
|
||||
start: this.start,
|
||||
limit: this.page_length + 1
|
||||
});
|
||||
}
|
||||
|
||||
get_fields() {
|
||||
return ['name'];
|
||||
}
|
||||
|
||||
async append() {
|
||||
this.start += this.page_length;
|
||||
await this.run();
|
||||
}
|
||||
|
||||
set_filters() {
|
||||
this.filters = {};
|
||||
get_filters() {
|
||||
let filters = {};
|
||||
if (this.search_input.value) {
|
||||
this.filters.keywords = ['like', '%' + this.search_input.value + '%'];
|
||||
filters.keywords = ['like', '%' + this.search_input.value + '%'];
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
make_body() {
|
||||
@ -104,10 +114,14 @@ module.exports = class List {
|
||||
|
||||
render_row(i, data) {
|
||||
let row = this.get_row(i);
|
||||
row.innerHTML = this.meta.get_row_html(data);
|
||||
row.innerHTML = this.get_row_html(data);
|
||||
row.style.display = 'block';
|
||||
}
|
||||
|
||||
get_row_html(data) {
|
||||
return `<a href="#edit/${this.doctype}/${data.name}>${data.name}</a>`;
|
||||
}
|
||||
|
||||
get_row(i) {
|
||||
if (!this.rows[i]) {
|
||||
this.rows[i] = frappe.ui.add('div', 'list-row py-2', this.body);
|
||||
|
@ -1,8 +1,7 @@
|
||||
const utils = require('../utils');
|
||||
const models = require('../model/models');
|
||||
const model = require('../model');
|
||||
const _document = require('../model/document');
|
||||
const meta = require('../model/meta');
|
||||
const BaseDocument = require('../model/document');
|
||||
const BaseMeta = require('../model/meta');
|
||||
const _session = require('../session');
|
||||
const errors = require('./errors');
|
||||
|
||||
@ -11,9 +10,8 @@ module.exports = {
|
||||
init_libs(frappe) {
|
||||
Object.assign(frappe, utils);
|
||||
frappe.model = model;
|
||||
frappe.models = new models.Models();
|
||||
frappe.document = _document;
|
||||
frappe.meta = meta;
|
||||
frappe.BaseDocument = BaseDocument;
|
||||
frappe.BaseMeta = BaseMeta;
|
||||
frappe._session = _session;
|
||||
frappe.errors = errors;
|
||||
}
|
||||
|
25
index.js
25
index.js
@ -15,6 +15,7 @@ module.exports = {
|
||||
|
||||
init_globals() {
|
||||
this.meta_cache = {};
|
||||
this.modules = {};
|
||||
this.docs = {};
|
||||
this.flags = {
|
||||
cache_docs: false
|
||||
@ -41,34 +42,46 @@ module.exports = {
|
||||
|
||||
get_meta(doctype) {
|
||||
if (!this.meta_cache[doctype]) {
|
||||
this.meta_cache[doctype] = new (this.models.get_meta_class(doctype))(this.models.get('DocType', doctype));
|
||||
this.meta_cache[doctype] = new (this.get_meta_class(doctype))();
|
||||
}
|
||||
return this.meta_cache[doctype];
|
||||
},
|
||||
|
||||
init_controller(doctype, module) {
|
||||
get_meta_class(doctype) {
|
||||
doctype = this.slug(doctype);
|
||||
this.models.controllers[doctype] = module[doctype];
|
||||
this.models.meta_classes[doctype] = module[doctype + '_meta'];
|
||||
if (this.modules[doctype] && this.modules[doctype].Meta) {
|
||||
return this.modules[doctype].Meta;
|
||||
} else {
|
||||
return this.BaseMeta;
|
||||
}
|
||||
},
|
||||
|
||||
async get_doc(data, name) {
|
||||
if (typeof data==='string' && typeof name==='string') {
|
||||
let doc = this.get_doc_from_cache(data, name);
|
||||
if (!doc) {
|
||||
let controller_class = this.models.get_controller(data);
|
||||
let controller_class = this.get_controller_class(data);
|
||||
doc = new controller_class({doctype:data, name: name});
|
||||
await doc.load();
|
||||
this.add_to_cache(doc);
|
||||
}
|
||||
return doc;
|
||||
} else {
|
||||
let controller_class = this.models.get_controller(data.doctype);
|
||||
let controller_class = this.get_controller_class(data.doctype);
|
||||
var doc = new controller_class(data);
|
||||
}
|
||||
return doc;
|
||||
},
|
||||
|
||||
get_controller_class(doctype) {
|
||||
doctype = this.slug(doctype);
|
||||
if (this.modules[doctype] && this.modules[doctype].Document) {
|
||||
return this.modules[doctype].Document;
|
||||
} else {
|
||||
return this.BaseDocument;
|
||||
}
|
||||
},
|
||||
|
||||
async get_new_doc(doctype) {
|
||||
let doc = await frappe.get_doc({doctype: doctype});
|
||||
doc.set_name();
|
||||
|
@ -1,6 +1,6 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class Document {
|
||||
module.exports = class BaseDocument {
|
||||
constructor(data) {
|
||||
this.handlers = {};
|
||||
this.setup();
|
||||
@ -143,6 +143,4 @@ class Document {
|
||||
await this.trigger('after_update', 'after_save');
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { Document: Document };
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
const Document = require('./document').Document;
|
||||
const BaseDocument = require('./document');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class Meta extends Document {
|
||||
module.exports = class BaseMeta extends BaseDocument {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
this.event_handlers = {};
|
||||
@ -100,22 +100,4 @@ class Meta extends Document {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collections
|
||||
async get_list({start, limit=20, filters}) {
|
||||
return await frappe.db.get_all({
|
||||
doctype: this.name,
|
||||
fields: this.list_options.fields,
|
||||
filters: filters,
|
||||
start: start,
|
||||
limit: limit
|
||||
});
|
||||
}
|
||||
|
||||
get_row_html(data) {
|
||||
return `<a href="/view/${this.name}/${data.name}">${data.name}</a>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { Meta: Meta }
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
const process = require('process');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class Models {
|
||||
constructor() {
|
||||
this.data = {doctype: {}};
|
||||
this.controllers = {};
|
||||
this.meta_classes = {};
|
||||
}
|
||||
|
||||
get(doctype, name) {
|
||||
return this.data[frappe.slug(doctype)][frappe.slug(name)];
|
||||
}
|
||||
|
||||
get_controller(doctype) {
|
||||
return this.controllers[frappe.slug(doctype)];
|
||||
}
|
||||
|
||||
get_meta_class(doctype) {
|
||||
return this.meta_classes[frappe.slug(doctype)] || frappe.meta.Meta;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { Models: Models }
|
@ -1,19 +1,13 @@
|
||||
const frappe = require('frappejs');
|
||||
const BaseMeta = require('frappejs/model/meta');
|
||||
const BaseDocument = require('frappejs/model/document');
|
||||
|
||||
class todo_meta extends frappe.meta.Meta {
|
||||
class ToDoMeta extends BaseMeta {
|
||||
setup_meta() {
|
||||
Object.assign(this, require('./todo.json'));
|
||||
this.list_options.fields = ['name', 'subject', 'status'];
|
||||
}
|
||||
|
||||
get_row_html(data) {
|
||||
const sign = data.status === 'Open' ? '' : '✔';
|
||||
return `<a href="#edit/todo/${data.name}">${sign} ${data.subject}</a>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class todo extends frappe.document.Document {
|
||||
class ToDo extends BaseDocument {
|
||||
setup() {
|
||||
this.add_handler('validate');
|
||||
}
|
||||
@ -24,4 +18,7 @@ class todo extends frappe.document.Document {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {todo:todo, todo_meta:todo_meta};
|
||||
module.exports = {
|
||||
Document: ToDo,
|
||||
Meta: ToDoMeta
|
||||
};
|
||||
|
16
models/doctype/todo/todo_client.js
Normal file
16
models/doctype/todo/todo_client.js
Normal file
@ -0,0 +1,16 @@
|
||||
const BaseList = require('frappejs/client/view/list');
|
||||
const BaseForm = require('frappejs/client/view/form');
|
||||
|
||||
class ToDoList extends BaseList {
|
||||
get_fields() {
|
||||
return ['name', 'subject', 'status'];
|
||||
}
|
||||
get_row_html(data) {
|
||||
return `<a href="#edit/todo/${data.name}">${data.subject}</a>`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Form: BaseForm,
|
||||
List: ToDoList
|
||||
}
|
@ -5,7 +5,7 @@ const express = require('express');
|
||||
const app = express();
|
||||
const frappe = require('frappejs');
|
||||
const rest_api = require('./rest_api')
|
||||
const models = require('frappejs/server/models');
|
||||
const init_models = require('frappejs/server/init_models');
|
||||
const common = require('frappejs/common');
|
||||
const bodyParser = require('body-parser');
|
||||
const path = require('path');
|
||||
@ -13,11 +13,11 @@ const path = require('path');
|
||||
module.exports = {
|
||||
async start({backend, connection_params, static, models_path}) {
|
||||
await this.init();
|
||||
models.init_models(path.resolve('node_modules', 'frappejs', 'models'));
|
||||
models.init_models(models_path);
|
||||
|
||||
await this.init_db({backend:backend, connection_params:connection_params});
|
||||
this.init_models(models_path);
|
||||
|
||||
// database
|
||||
await this.init_db({backend:backend, connection_params:connection_params});
|
||||
|
||||
// app
|
||||
app.use(bodyParser.json());
|
||||
@ -30,7 +30,14 @@ module.exports = {
|
||||
// listen
|
||||
frappe.app = app;
|
||||
frappe.server = app.listen(frappe.config.port);
|
||||
},
|
||||
|
||||
init_models(models_path) {
|
||||
// import frappe modules
|
||||
init_models(path.join(path.dirname(require.resolve('frappejs')), 'models'));
|
||||
|
||||
// import modules from the app
|
||||
init_models(models_path);
|
||||
},
|
||||
|
||||
async init() {
|
||||
|
23
server/init_models.js
Normal file
23
server/init_models.js
Normal file
@ -0,0 +1,23 @@
|
||||
const frappe = require('frappejs');
|
||||
const walk = require('walk');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function(models_path) {
|
||||
if (!models_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
walk.walkSync(models_path, {
|
||||
listeners: {
|
||||
file: (basepath, file_data, next) => {
|
||||
const doctype = path.basename(path.dirname(basepath));
|
||||
const name = path.basename(basepath);
|
||||
const file_path = path.resolve(basepath, file_data.name);
|
||||
if (doctype==='doctype' && file_data.name.endsWith('.js')) {
|
||||
frappe.modules[file_data.name.slice(0, -3)] = require(file_path);
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
const frappe = require('frappejs');
|
||||
const walk = require('walk');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
init_models(models_path) {
|
||||
const setup_model = (doctype, name, file_path) => {
|
||||
// add to frappe.models.data
|
||||
if (!frappe.models[doctype]) {
|
||||
frappe.models[doctype] = {};
|
||||
}
|
||||
frappe.models.data[doctype][name] = require(file_path);
|
||||
}
|
||||
|
||||
const setup_controller = (doctype, file_path) => {
|
||||
let _module = require(file_path);
|
||||
frappe.models.controllers[doctype] = _module[doctype];
|
||||
if (_module[doctype + '_meta']) {
|
||||
frappe.models.meta_classes[doctype] = _module[doctype + '_meta'];
|
||||
}
|
||||
}
|
||||
|
||||
walk.walkSync(models_path, {
|
||||
listeners: {
|
||||
file: (basepath, file_data, next) => {
|
||||
const doctype = path.basename(path.dirname(basepath));
|
||||
const name = path.basename(basepath);
|
||||
const file_path = path.resolve(basepath, file_data.name);
|
||||
if (file_data.name.endsWith('.json')) {
|
||||
setup_model(doctype, name, file_path)
|
||||
}
|
||||
if (doctype==='doctype' && file_data.name.endsWith('.js')) {
|
||||
setup_controller(path.basename(basepath), file_path);
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,6 @@ module.exports = {
|
||||
setup(app) {
|
||||
// get list
|
||||
app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
|
||||
let fields, filters;
|
||||
for (key of ['fields', 'filters']) {
|
||||
if (request.query[key]) {
|
||||
request.query[key] = JSON.parse(request.query[key]);
|
||||
|
@ -2,7 +2,8 @@ const server = require('frappejs/server');
|
||||
|
||||
module.exports = {
|
||||
async init_sqlite() {
|
||||
server.init()
|
||||
server.init();
|
||||
server.init_models();
|
||||
server.init_db({
|
||||
backend: 'sqlite',
|
||||
connection_params: {db_path: 'test.db'}
|
||||
|
@ -8,7 +8,7 @@ describe('Models', () => {
|
||||
});
|
||||
|
||||
it('should get todo json', () => {
|
||||
let todo = frappe.models.get('DocType', 'ToDo');
|
||||
let todo = frappe.get_meta('todo');
|
||||
assert.equal(todo.issingle, 0);
|
||||
});
|
||||
});
|
@ -1,5 +1,8 @@
|
||||
const server = require('frappejs/server');
|
||||
|
||||
if (require.main === module) {
|
||||
server.start({backend: 'sqlite', connection_params: {db_path: 'test.db'}});
|
||||
server.start({
|
||||
backend: 'sqlite',
|
||||
connection_params: {db_path: 'test.db'}
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user