I have manged to build a Custom App Page. But I have a few queries for adding features.
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?
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?
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.
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.
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.
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>`;
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.
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.
@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.
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.