Add button in list view item

you need to check

list_view.js
but its generic if you change here it will reflect to all list view of the forms

but i can’t add the new button, i modificate the item_list.js but don’t show the button

Any way/suggestion to do the same using custom script ??

1 Like

Bumping to prevent making another post about this.

Is there anyway to do this without editing list_view.js or base_list.js?

This is the hack I was able to come up with:

// this will help prevent duplicate calls
let added = false;

// This function is called any time the page is updated
$(document).bind('DOMSubtreeModified', function () {
    if ('List/Sales Invoice/List' in frappe.pages && frappe.pages['List/Sales Invoice/List'].page && !added) {
        added = true;
        frappe.pages['List/Sales Invoice/List'].page.add_action_item('Export to Quickbooks', function(event) {
            // Convert list of UI checks to list of IDs
            let selected = [];
            for (let check of event.view.cur_list.$checks) {
                selected.push(check.dataset.name);
            }
            // Do action
        });
        
    }
});

This adds an Action Item button to the Sales Invoice List page only; but with the .page object, you can do anything needed with the Page API (add a normal menu item, change page title, etc).

To the Frappe team, is there a better way to do this?

The good practice is to do it by this way :slight_smile:

2 Likes

Thank you for your input, but this did not fit my specific use case.

I needed to add to the Sales Invoice List view (sales_invoices_list.js) from an external app and not modify the original version.

@kevinpthorne Could you tell me where and how you will add your code?

image

I created a small JS file to be included as part of the front-end of my app. The build.json will build the compiled file…

image

…and then included here in hooks.py.

This is by far a perfect solution though. DOMSubtreeModified is called ANY time the page is changed, meaning it is called VERY often. Frappe pages are also session-persistent; the ugly if-statement helps scope exactly when the Action item should be added.

// this will help prevent duplicate calls
let added = false;

// This function is called any time the page is updated
$(document).bind('DOMSubtreeModified', function () {
    if ('List/Sales Invoice/List' in frappe.pages && frappe.pages['List/Sales Invoice/List'].page && !added) {
        added = true;
        frappe.pages['List/Sales Invoice/List'].page.add_action_item('Export to Quickbooks', function(event) {
            // Convert list of UI checks to list of IDs
            let selected = [];
            for (let check of event.view.cur_list.$checks) {
                selected.push(check.dataset.name);
            }
            // Do action
        });
        
    }
});

Unfortunately, this snippet cannot go into a Custom Script since Custom Scripts only load on Forms of a certain DocType.

1 Like

Thank you, this worked well. Any idea how to have it as a separate button

On version 13 you can load custom script on list view

frappe.listview_settings[‘Invoice’] = {
button: {
show: function(doc) {
return true;
},
get_label: function() {
return __(‘PDF’);
},
get_description: function(doc) {
return (‘Print {0}’, [doc.name])
},
action: function(doc) {
//frappe.set_route(“/app/print/Invoice/” + doc.name);
var objWindowOpenResult = window.open(frappe.urllib.get_full_url(“/api/method/frappe.utils.print_format.download_pdf?”
+ “doctype=” + encodeURIComponent(“Invoice”)
+ “&name=” + encodeURIComponent(doc.name)
+ “&trigger_print=0”
+ “&format=invoice print format”
+ “&no_letterhead=0”
+ “&_lang=en”
));
if(!objWindowOpenResult) {
msgprint(
(“Please set permission for pop-up windows in your browser!”)); return;
}
}
}
}

Check this blog for reference: How to add Button link in Doctype list View ? - Frappe Framework - Aadhil PM

3 Likes

Thanks @Aadhil_P_M

I meant as a context level button next to List View like this

frappe.ui.form.on(“Doctype Name”, “refresh”, function(frm){
frm.add_custom_button(“Button Description”, function(){
var myWin = window.open(‘https://example.com/’);
});
});

Please replace doctype name, button Description and example.com

Can you please specify action on button so I can assist you better.

https://frappeframework.com/docs/user/en/guides/app-development/adding-custom-button-to-form

Please check above link for reference

1 Like

Sorry if I wasnt clear enought, I mean adding an independent button to the list view not the form

I was able to create a custom button on the list view of a specific Doctype by adding a secondary action. Alternatively, you could also add an action to the Action menu.

frappe.listview_settings['Enrollment'] = {

    onload(listview) {
        // triggers once before the list is loaded
        console.log("loaded", listview);
        listview.page.add_action_item('My custom Action', () => my_action_handler());
        listview.page.set_secondary_action('Refresh', () => refresh(), 'octicon octicon-sync');
  }

See Page API for details.

4 Likes

got any solution ??

@Aadhil_P_M , button status changed from paid to submitted after script applied.

The above script is to add custom button. Is it possible to post the script you added or screenshot of the script here.

1 Like
frappe.listview_settings['Sales Invoice'] = {
    button: {
        show: function(doc) {
            return true;
            },
        get_label: function() {
            return __('PDF');
            },
        get_description: function(doc) {
            return ('Print {0}', [doc.name]);
            },
        action: function(doc) {
            var objWindowOpenResult = window.open(frappe.urllib.get_full_url("/api/method/frappe.utils.print_format.download_pdf?"
                + "doctype=" + encodeURIComponent("Sales Invoice")
                + "&name=" + encodeURIComponent(doc.name)
                + "&trigger_print=0"
                + "&format=SINV - A"
                + "&no_letterhead=LetterHead1"
                + "&_lang=en"
            ));
        if(!objWindowOpenResult) {
            msgprint("Please set permission for pop-up windows in your browser!"); 
            return;
        }
    }
}};

Please find the screenshot. After applied Status changed.
image

2 Likes

In version 14, the onload function sometimes fails to trigger every time the list loads. Eg. If the user navigates back to different page and returns to this list again, the onload event is not triggered. To solve this, you can use before_render. To get the list_view reference inside this function, use cur_list variable.

Example implementation:

before_render: function() {
      console.log("before render", cur_list)
      let me = cur_list;

      me.page.add_inner_button("Click Me", function() {
          console.log("clicked inside before render");
      }
}

This will add a new button on the list view.