2
0
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:
Rushabh Mehta 2018-01-31 15:43:33 +05:30
parent d6888a8bb3
commit a6c98470ba
11 changed files with 175 additions and 54 deletions

21
cli.js
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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