[DevTip] Building custom desk pages

On a few different threads, people have been asking about how to create custom desk pages for displaying data and executing workflows. The Page API has a bit of documentation, but there’s not much really showing how to get started. Here’s a very simple example that will hopefully help with that.

As administrator, create a new Page document with settings that look like this:


The module field should link a module in your custom app.

In your module’s pages folder on your server, you’ll find a file called demo.js (or whatever you called your page). It comes pre-filled with boilerplate code that invokes the make_app_page method:

frappe.pages['demo'].on_page_load = function(wrapper) {
	var page = frappe.ui.make_app_page({
		parent: wrapper,
		title: 'Demo',
		single_column: true
	});

}

From here, your options are limitless really. The make_app_page method generates a bit of html at the wrapper location, and you can hook into that html using whatever frontend tools you prefer.

Here is a very simple illustration using Frappe’s DataTable library. (You could just as easily use Vue or whatever else you like.)

frappe.pages['demo'].on_page_load = function(wrapper) {
	var page = frappe.ui.make_app_page({
		parent: wrapper,
		title: 'Demo',
		single_column: true
	});

	//set up our empty datatable
	let el = document.querySelector('.layout-main-section')
	let button_formatter = (value) => `<button onclick="alert('This is ${value}')">Action!</button>`
	let columns = ['Name', 'Territory', 'Account Manager', 
		{name: "Action Button", focusable: false, format: button_formatter }]
	let datatable = new frappe.DataTable(el, { columns: columns, data: [], layout: "fluid" });

	//use regular ajax api methods to fetch document data, then refresh
	frappe.db.get_list("Customer", 
		{fields: ['customer_name', 'territory', 'account_manager', 'name']}
	).then((r) => { 
		let data = r.map(Object.values)
		datatable.refresh(data, columns) 
	})
}

That gives me a page that looks like this:


We’re pulling data from the database and creating a button for each Customer that triggers arbitrary javascript. Nothing fancy, but pretty much anything is possible on the same principles.

14 Likes

@peterg,

The line

let button_formatter = (value) => return `<button onclick="alert('This is ${value}')">Action!</button>`

Is giving error. The error message is

Expected Expression got Keyword

Regards,

Remove the return keyword. It isn’t allowed on JS arrow functions without a block.

2 Likes

Thanks for the tip. It worked but now I am getting this error.

console.trace() ReferenceError: DataTable is not defined
  on_page_load production_view.js:42
  trigger_page_event pageview.js:44
  <anonymous> pageview.js:28
  show factory.js:26
  callback factory.js:5
  success request.js:83
  200 request.js:127
  call request.js:271
  jQuery 6
  call request.js:253
  call request.js:103
  with_page breadcrumbs.js:195
  show factory.js:17
  with_doctype model.js:133
  show factory.js:13
  render_page router.js:212
  render router.js:186
  route router.js:107
  set_route desk.js:109
  startup desk.js:12
  init sort_selector.js:193
  start_app sort_selector.js:193
  Rollup sort_selector.js:193
  jQuery 8
request.js:275:12
  call request.js:275
  jQuery 6
  call request.js:253
  call request.js:103
  with_page breadcrumbs.js:195
  show factory.js:17
  with_doctype model.js:133
  show factory.js:13
  render_page router.js:212
  render router.js:186
  route router.js:107
  set_route desk.js:109
  startup desk.js:12
  init sort_selector.js:193
  start_app sort_selector.js:193
  Rollup sort_selector.js:193

It seems to say that DataTable is not defined.

Regards,

@peterg,

Can you please tell me as to how I can add support for DataTable to the page?

Regards,

Where are you seeing this error?

I am seeing the error message in Browser console.

Here is the exact code that I am using

frappe.pages['production-view'].on_page_load = function(wrapper) {

 var page = frappe.ui.make_app_page({
  parent: wrapper,
  title: 'Production View',
  single_column: true
});

    page.set_title("Manage Production")

    //Testing Code to Add Field to Page
    let MyField = page.add_field({
        label: 'Status',
        fieldtype: 'Select',
        fieldname: 'status',
        options:[
            'Open',
            'Close',
            'Cancelled'
        ],
        change(){
            frappe.msgprint(MyField.get_value());
        }
    });

    //--set up our empty datatable
	let el = document.querySelector('.layout-main-section')
	let button_formatter = (value) => `<button onclick="alert('This is ${value}')">Action!</button>`
	let columns = ['Name', 'Territory', 'Account Manager', 
  {name: "Action Button", focusable: false, format: button_formatter }]
	let datatable = new DataTable(el, { columns: columns, data: [], layout: "fluid" });

	//use regular ajax api methods to fetch document data, then refresh
	frappe.db.get_list("Customer", 
	 {fields: ['customer_name', 'territory', 'account_manager', 'name']}
	).then((r) => { 
	let data = r.map(Object.values)
	datatable.refresh(data, columns) 
	});
}

Regards,

Hi @yogeshvachhani, My apologies for the slow response. I’m out of the office for a few days, but I’ll take a look as soon as I can. Hopefully somebody else might be able to help in the meantime.

1 Like

use frappe.DataTable insted of DataTable

change

to

let datatable = new frappe.DataTable(el, { columns: columns, data: [], layout: "fluid" });
2 Likes

Thanks @amadhaji.

It is working now!

And it is removing all the controls/elements that I have placed before the table.

Here is the view that is generated without DataTable.

image
Here is view generated with DataTable.

Any ideas as to how can I preserve all the content above DataTable.

In fact after a bit of testing I found that anything that we add before and after the table does not show up on the page.

Regards,

you can add div element inside '.layout-main-section' and use instead of it.
ex:

$("<div class='datatable'></div>").appendTo('.layout-main-section');
let el = document.querySelector('.datatable');
1 Like

@amadhaji,

You suggestion worked for me.

Thanks for the solution.

Regards,

15 posts were split to a new topic: Using DataTable in custom desk pages