mirror of
https://github.com/frappe/books.git
synced 2024-11-14 01:14:03 +00:00
fixes and added observable base class
This commit is contained in:
parent
d6888a8bb3
commit
a6c98470ba
21
cli.js
21
cli.js
@ -1,30 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const utils = require('frappejs/utils');
|
||||
const process = require('process');
|
||||
const boilerplate = require('frappejs/models/boilerplate');
|
||||
|
||||
program.command('new-model <name>')
|
||||
.description('Create a new model in the `models/doctype` folder')
|
||||
.action((name) => {
|
||||
fs.mkdirSync(`./models/doctype/${utils.slug(name)}`);
|
||||
fs.writeFileSync(`./models/doctype/${utils.slug(name)}/${utils.slug(name)}`, `{
|
||||
"name": "${name}",
|
||||
"doctype": "DocType",
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"keyword_fields": [
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"label": "Name",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1
|
||||
}
|
||||
]
|
||||
}`);
|
||||
boilerplate.make_model_files(name);
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
@ -1,5 +1,6 @@
|
||||
const Page = require('frappejs/client/view/page');
|
||||
const view = require('frappejs/client/view');
|
||||
const frappejs = require('frappejs');
|
||||
|
||||
module.exports = class FormPage extends Page {
|
||||
constructor(doctype) {
|
||||
@ -15,6 +16,19 @@ module.exports = class FormPage extends Page {
|
||||
this.on('show', async (params) => {
|
||||
await this.show_doc(params.doctype, params.name);
|
||||
});
|
||||
|
||||
// if name is different after saving, change the route
|
||||
this.form.on('submit', async (params) => {
|
||||
let route = frappe.router.get_route();
|
||||
if (!(route && route[2] === this.form.doc.name)) {
|
||||
await frappe.router.set_route('edit', this.form.doc.doctype, this.form.doc.name);
|
||||
this.form.show_alert('Added', 'success');
|
||||
}
|
||||
});
|
||||
|
||||
this.form.on('delete', async (params) => {
|
||||
await frappe.router.set_route('list', this.form.doctype);
|
||||
});
|
||||
}
|
||||
|
||||
async show_doc(doctype, name) {
|
||||
|
@ -1,8 +1,10 @@
|
||||
const frappe = require('frappejs');
|
||||
const controls = require('./controls');
|
||||
const Observable = require('frappejs/utils/observable');
|
||||
|
||||
module.exports = class BaseForm {
|
||||
module.exports = class BaseForm extends Observable {
|
||||
constructor({doctype, parent, submit_label='Submit'}) {
|
||||
super();
|
||||
this.parent = parent;
|
||||
this.doctype = doctype;
|
||||
this.submit_label = submit_label;
|
||||
@ -52,23 +54,10 @@ module.exports = class BaseForm {
|
||||
this.btn_delete.addEventListener('click', async () => {
|
||||
await this.doc.delete();
|
||||
this.show_alert('Deleted', 'success');
|
||||
this.trigger('delete');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
show_alert(message, type) {
|
||||
this.clear_alert();
|
||||
this.alert = frappe.ui.add('div', `alert alert-${type}`, this.body);
|
||||
this.alert.textContent = message;
|
||||
}
|
||||
|
||||
clear_alert() {
|
||||
if (this.alert) {
|
||||
frappe.ui.remove(this.alert);
|
||||
this.alert = null;
|
||||
}
|
||||
}
|
||||
|
||||
async use(doc, is_new = false) {
|
||||
if (this.doc) {
|
||||
// clear handlers of outgoing doc
|
||||
@ -88,6 +77,8 @@ module.exports = class BaseForm {
|
||||
control.set_doc_value();
|
||||
}
|
||||
});
|
||||
|
||||
this.trigger('use', {doc:doc});
|
||||
}
|
||||
|
||||
async submit() {
|
||||
@ -102,6 +93,7 @@ module.exports = class BaseForm {
|
||||
} catch (e) {
|
||||
this.show_alert('Failed', 'danger');
|
||||
}
|
||||
await this.trigger('submit');
|
||||
}
|
||||
|
||||
refresh() {
|
||||
@ -110,4 +102,20 @@ module.exports = class BaseForm {
|
||||
}
|
||||
}
|
||||
|
||||
show_alert(message, type, clear_after = 5) {
|
||||
this.clear_alert();
|
||||
this.alert = frappe.ui.add('div', `alert alert-${type}`, this.body);
|
||||
this.alert.textContent = message;
|
||||
setTimeout(() => {
|
||||
this.clear_alert();
|
||||
}, clear_after * 1000);
|
||||
}
|
||||
|
||||
clear_alert() {
|
||||
if (this.alert) {
|
||||
frappe.ui.remove(this.alert);
|
||||
this.alert = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
const frappe = require('frappejs');
|
||||
const Observable = require('frappejs/utils/observable');
|
||||
|
||||
module.exports = class Page {
|
||||
module.exports = class Page extends Observable {
|
||||
constructor(title) {
|
||||
this.handlers = {};
|
||||
super();
|
||||
this.title = title;
|
||||
this.make();
|
||||
}
|
||||
@ -42,17 +43,4 @@ module.exports = class Page {
|
||||
this.page_error.classList.remove('hide');
|
||||
this.page_error.innerHTML = `<h3 class="text-extra-muted">${title ? title : ""}</h3><p class="text-muted">${message ? message : ""}</p>`;
|
||||
}
|
||||
|
||||
on(event, fn) {
|
||||
if (!this.handlers[event]) this.handlers[event] = [];
|
||||
this.handlers[event].push(fn);
|
||||
}
|
||||
|
||||
async trigger(event, params) {
|
||||
if (this.handlers[event]) {
|
||||
for (let handler of this.handlers[event]) {
|
||||
await handler(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ const frappe = require('frappejs');
|
||||
|
||||
module.exports = class Router {
|
||||
constructor() {
|
||||
this.last_route = null;
|
||||
this.current_page = null;
|
||||
this.static_routes = [];
|
||||
this.dynamic_routes = [];
|
||||
@ -52,13 +53,33 @@ module.exports = class Router {
|
||||
|
||||
listen() {
|
||||
window.addEventListener('hashchange', (event) => {
|
||||
this.show(window.location.hash);
|
||||
let route = this.get_route_string();
|
||||
if (this.last_route !== route) {
|
||||
this.show(route);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
set_route(...parts) {
|
||||
// split and get routes
|
||||
get_route() {
|
||||
let route = this.get_route_string();
|
||||
if (route) {
|
||||
return route.split('/');
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async set_route(...parts) {
|
||||
const route = parts.join('/');
|
||||
|
||||
// setting this first, does not trigger show via hashchange,
|
||||
// since we want to this with async/await, we need to trigger
|
||||
// the show method
|
||||
this.last_route = route;
|
||||
|
||||
window.location.hash = route;
|
||||
await this.show(route);
|
||||
}
|
||||
|
||||
async show(route) {
|
||||
@ -66,6 +87,8 @@ module.exports = class Router {
|
||||
route = route.substr(1);
|
||||
}
|
||||
|
||||
this.last_route = route;
|
||||
|
||||
if (!route) {
|
||||
route = this.default;
|
||||
}
|
||||
@ -103,4 +126,12 @@ module.exports = class Router {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_route_string() {
|
||||
let route = window.location.hash;
|
||||
if (route && route[0]==='#') {
|
||||
route = route.substr(1);
|
||||
}
|
||||
return route;
|
||||
}
|
||||
}
|
11
docs/cli.md
Normal file
11
docs/cli.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Command Line Tools
|
||||
|
||||
## Adding a new model / DocType
|
||||
|
||||
You can add a new model template with the following command
|
||||
|
||||
```sh
|
||||
node_modules/frappejs/cli.js new-model [modelname]
|
||||
```
|
||||
|
||||
This will add a new `.json` file for the model (DocType) definition and a controller
|
@ -2,7 +2,7 @@
|
||||
|
||||
The Frappe.js client comes in with a built in router, that is handles via hashing (example `#route`)
|
||||
|
||||
## Add a new route
|
||||
## Adding new route handlers
|
||||
|
||||
You can add a new route by calling `frappe.router.add`
|
||||
|
||||
@ -43,6 +43,25 @@ frappe.router.add('new/todo', async (params) => {
|
||||
});
|
||||
```
|
||||
|
||||
## Setting route
|
||||
|
||||
You can change route with
|
||||
|
||||
```js
|
||||
await frappe.router.set_route('list', 'todo');
|
||||
```
|
||||
|
||||
## Getting current route
|
||||
|
||||
`frappe.router.get_route()` will return the current route as a list.
|
||||
|
||||
```js
|
||||
await frappe.router.set_route('list', 'todo');
|
||||
|
||||
// returns ['list', 'todo'];
|
||||
route = frappe.router.get_route();
|
||||
```
|
||||
|
||||
## Show a route
|
||||
|
||||
To set a route, you can call `frappe.router.show(route_name)`
|
||||
|
@ -24,3 +24,4 @@ Frappe.js is a meta-data driven framework that enables rapid application develop
|
||||
- [Backends](backends.md)
|
||||
- [Errors](errors.md)
|
||||
- [Testing](testing.md)
|
||||
- [Command Line Tools](cli.md)
|
46
model/boilerplate.js
Normal file
46
model/boilerplate.js
Normal file
@ -0,0 +1,46 @@
|
||||
const fs = require('fs');
|
||||
const utils = require('frappejs/utils');
|
||||
|
||||
module.exports = {
|
||||
make_model_files(name) {
|
||||
|
||||
// [doctype].json
|
||||
fs.mkdirSync(`./models/doctype/${utils.slug(name)}`);
|
||||
fs.writeFileSync(`./models/doctype/${utils.slug(name)}/${utils.slug(name)}.json`, `{
|
||||
"name": "${name}",
|
||||
"doctype": "DocType",
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"keyword_fields": [
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"label": "Name",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1
|
||||
}
|
||||
]
|
||||
}`);
|
||||
|
||||
// [doctype].js
|
||||
let thinname = name.replace(/ /g, '');
|
||||
fs.writeFileSync(`./models/doctype/${utils.slug(name)}/${utils.slug(name)}.js`, `const BaseMeta = require('frappejs/model/meta');
|
||||
const BaseDocument = require('frappejs/model/document');
|
||||
|
||||
class ${thinname}Meta extends BaseMeta {
|
||||
setup_meta() {
|
||||
Object.assign(this, require('./${utils.slug(name)}.json'));
|
||||
}
|
||||
}
|
||||
|
||||
class ${thinname} extends BaseDocument {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Document: ${thinname},
|
||||
Meta: ${thinname}Meta
|
||||
};`);
|
||||
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ module.exports = class BaseDocument {
|
||||
|
||||
async validate_field (key, value) {
|
||||
let df = this.meta.get_field(key);
|
||||
if (df.fieldtype=='Select') {
|
||||
if (df && df.fieldtype=='Select') {
|
||||
return this.meta.validate_select(df, value);
|
||||
}
|
||||
return value;
|
||||
|
20
utils/observable.js
Normal file
20
utils/observable.js
Normal file
@ -0,0 +1,20 @@
|
||||
module.exports = class Observable {
|
||||
constructor() {
|
||||
this._handlers = {};
|
||||
}
|
||||
|
||||
on(event, fn) {
|
||||
if (!this._handlers[event]) {
|
||||
this._handlers[event] = [];
|
||||
}
|
||||
this._handlers[event].push(fn);
|
||||
}
|
||||
|
||||
async trigger(event, params) {
|
||||
if (this._handlers[event]) {
|
||||
for (let handler of this._handlers[event]) {
|
||||
await handler(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user