Using DataTable in custom desk pages

Hello,

I have manged to build a Custom App Page. But I have a few queries for adding features.

  1. I want to set a column to have a Hyper link so that when users clicks on it I want to run a custom Client Script. How to do this?
  2. As per the sample code provided here a button shows up in the table but when user clicks on the button I want to run a complex script. The current sample has a very simple script to show Alert. How to go about adding a complex script to the button in Table?

TIA

Yogi Yang

Hi there,

In the example here, I’ve used a DataTable’s cell formatter to create a button with an onclick event handler. The handler just runs a simple alert function, but you could just as easily call any other JavaScript function you define.

Client Scripts aren’t really designed to be run in this context, so making it work would be a bit of a hack. That said, you could probably do it with a combination of get_doc calls, string parsing, and eval() statements. Still, it’d be a lot easier to just write the code in the page definition if you can.

1 Like

Hello,

Thank you for the technical explanation. I am finding it very hard to call Client Script.

I have added a function in the page definition. But for some reason the code is not getting executed when user clicks on the button. The browser console throws message Uncaught ReferenceError: btn_view is not defined.

Here is my code. The code is very long so I am just pasting the code in question

//Append a Div
    $("<div class='datatable'></div>").appendTo('.layout-main-section');
        
    //--set up our empty datatable
	// let el = document.querySelector('.layout-main-section')
    let el = document.querySelector('.datatable');

	//let button_formatter = (value) => `<button onclick="alert('This is ${value}')">Action!</button>`;
    let button_formatter = (value) => `<button onclick="btn_view()">Action!</button>`
    
	let columns = [
        {name:'Date', editable:false, focusable:false},
            {name:'Supplier Name', editable:false, focusable:false},
            {name:'PO', editable:false, focusable:false},
            {name:'Item Name', editable:false, focusable:false},
            {name:'Received Qty', editable:false, focusable:false},
            {name:'Reject Qty', editable:false, focusable:false},
            {name:'Batch #', editable:false, focusable:false},
            {name:'Coil Heat #', editable:false, focusable:false},
            {name:'Coil Qty', editable:false, focusable:false},
            {name:'Coil Wt.', editable:false, focusable:false},
            {name:'QC', editable:false, focusable:false},
            {name: "Action Button", focusable: false, format: button_formatter }
        ]
	let datatable = new frappe.DataTable(el, { columns: columns, data: [], layout: "fluid", inlineFilters: "true", serialNoColumn: "false" });

	//use regular ajax api methods to fetch document data, then refresh
    frappe.call({
        method: "mymfg.api.get_purchase_receipt_view_all"
    }).done((r) => {
        console.log(r)
        console.log(r.message)
        let data = []
        datatable.freeze
        $.each(r.message, function(_i, e){
            let rr = {
                'Date':e.posting_date, 
                'Supplier Name':e.supplier_name, 
                'PO':e.purchase_order,
                'Item Name':e.item_name,
                'Received Qty':e.received_qty,
                'Reject Qty':e.rejected_qty,
                'Batch #':e.batch_no,
                'Coil Heat #':e.coil_heat_no,
                'Coil Qty':e.coil_qty,
                'Coil Wt.':e.coil_wt,
                'QC':e.quality_inspection
            }
            data.push(rr)
        })
        //let data = r.map(Object.values)
        datatable.refresh(data, columns)
    })

    function btn_view(){
        alert("Hello");
    }

@peterg, I am also not able to grasp as to how to pass the row to a function. Please guide me regarding this also, because when a user clicks on the button I want to show a Modal in which I want to load complete data of the record clicked and allow user to edit it and then update the data.

TIA

Yogi Yang

I always get a bit muddled about javascripts precedence rules, but it looks here like you’re calling the function before you’re defining it. If you move the function definition to the top of the script, does it work there?

For this, you have to pass a row identifier to the function you’re calling. With DataTable, I’ve done this by passing a variable to the cell formatter ($(variable)). If you’re using DataTable, you could do something similar. Alternately, full frameworks like Vue have more sophisticated ways of handling row ids.

Hello,

I tried it that way also. But it does not work.

I tried as per your suggestion but it returns Undefined rather then a value. What mistake am I making here that the variable is Undefined.

Here is the code snippet.

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

TIA

Yogi Yang

Try to define at global block (outside function)
ex:

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

// here define your function
function btn_view(val){
        alert("Hello");
}

I have no background about Vue, but I guess you are able to do it this way in javascript.

let button_formatter = (value) => `<button onclick="btn_view(value)">Action!</button>`;

or

let button_formatter = (value) => `<button onclick="alert('This is ' + value)">Action!</button>`;
2 Likes

Good catch, @amadhaji. I hadn’t noticed that YogiYang’s function was being defined in the on_page_load function. Placing it outside the block into global space should work.

The way you’re passing the value to the function should work too.

Hello,

Thanks this worked!

But there is some mistake in my code. I am not able to pass it any value.

I amusing the following code.

let button_formatter = (value) => `<button onclick="btn_view(${value})">Action!</button>`

It seems I am making come mistake but cannot catch it.

If I use the code as provided by @peterg then the alert will display the value but as soon as I replace the data with my data it does becomes Undefined.

Why?

TIA

Yogi Yang

what is the data witch you pass?
Is it defined?
Maybe you will need to define it in global space.

You should be able to just look at the page source to see what’s getting printed as the onclick handler. That would be a good troubleshooting step.

Hello,

I check onclick handler and there it is undefined. I don’t understand as to why.

I am not using let data = r.map(Object.values) as that feature is not available so I am using this code to add rows to table.

frappe.call({
        method: "mymfg.api.get_purchase_receipt_view_all"
    }).done((r) => {
        console.log(r)
        console.log(r.message)
        let data = []
        datatable.freeze
        $.each(r.message, function(_i, e){
            let rr = {
                'Date':e.posting_date, 
                'Supplier Name':e.supplier_name, 
                'PO':e.purchase_order,
                'Item Name':e.item_name,
                'Received Qty':e.received_qty,
                'Reject Qty':e.rejected_qty,
                'Batch #':e.batch_no,
                'Coil Heat #':e.coil_heat_no,
                'Coil Qty':e.coil_qty,
                'Coil Wt.':e.coil_wt,
                'QC':e.quality_inspection,
                'Action Button':e.name2
            }
            data.push(rr)
        })
        datatable.refresh(data, columns)
    })

TIA

Yogi Yang

Does the table show defined values if you remove the button formatter from the “Action Button” column?

1 Like

Hello,

Thank I managed to solve the problem.

But when I click on the button browser throws this error Uncaught SyntaxError: '' string literal contains an unescaped line break.

Without format: button_formatter the values that are coming up are like these:

3db957fdd6
a53513ab03
.
.
.
etc.

TIA

Yogi Yang

@YogiYang, would you have any objections if I split this discussion off into a separate thread? I’m happy to help you troubleshoot here, but we’re really talking about javascript/DataTable and not custom desk pages.

Either way, please post your complete page js file and I’ll see if I can reproduce your error.

1 Like

Hello,

Yes, please splitting the discussion.

And thanks also for helping me out.

I managed to solve the problem by enclosing the value in quotes.

Here is what I did.

let button_formatter = (value) => `<button onclick="btn_view('${value}')">Action!</button>`

Once gain thank you very much. I managed to solve this problem!

TIA

Yogi Yang

1 Like

Great! Yeah, the quotes are necessary, because otherwise the values are interpreted as variable names. I can’t believe I missed that in your code, but I’m glad you caught it. :slight_smile:

1 Like