Hi,
Ok so the default way of viewing linked documents is via connections. So lets say for instance I want to view the tasks list for a particular project, either i can go to the tasks list view and add a filter or access them via the connections tab in the project form(which more or less does the same as the former). Instead i wanted to add a separate tab in the project form containing a html field which show a paginated table of all tasks for a particular project. So I added an html field to the project doctype called “tasks_list” and added a client side script.
Here’s the script:
frappe.ui.form.on("Project", {
refresh: function(frm) {
console.log("Inside refresh function");
if (frm.fields_dict['tasks_list']) {
var cur_field = frm.fields_dict['tasks_list'];
console.log("cur_field found:", cur_field);
// Function to fetch tasks
function fetch_tasks(start, limit) {
console.log("Fetching tasks from start:", start, "with limit:", limit);
frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Task",
filters: [["project", "=", frm.doc.name]],
fields: ["name", "status", "description"],
limit_start: start,
limit_page_length: limit,
},
callback: function(response) {
console.log("Response received:", response);
if (!response.message || response.message.length === 0) {
cur_field.html("No linked tasks found for this project.");
return;
}
try {
var task_list_html = get_task_list_html(response.message);
cur_field.$wrapper.html(task_list_html); // Set the HTML
add_pagination_controls(cur_field.$wrapper, start, limit, response.message.length);
} catch (error) {
console.error("Error generating HTML:", error);
cur_field.$wrapper.html("Failed to display linked tasks.");
}
},
error: function(error) {
console.error("Error in frappe call:", error);
cur_field.$wrapper.html("Failed to fetch linked tasks.");
}
});
}
// Initial fetch with page 1
fetch_tasks(0, 10);
} else {
console.error("Custom field 'tasks_list' not found.");
}
},
});
// Function to generate HTML
function get_task_list_html(data) {
try {
var table_html = '<div class="table-responsive"><table class="table table-bordered">';
table_html += "<thead><tr>";
table_html += "<th>Name</th><th>Status</th><th>Description</th>"; // Add table headers
table_html += "</tr></thead><tbody>";
// Iterate through fetched tasks and build table rows
data.forEach(task => {
table_html += "<tr>";
table_html += `<td>${task.name || ""}</td>`;
table_html += `<td>${task.status || ""}</td>`;
table_html += `<td>${task.description || ""}</td>`;
table_html += "</tr>";
});
table_html += "</tbody></table></div>";
return table_html;
} catch (error) {
console.error("Error in get_task_list_html function:", error);
throw error;
}
}
// Function to add pagination controls
function add_pagination_controls(container, start, limit, fetched_count) {
try {
container = $(container); // Ensure container is a jQuery object
var pagination_html = '<div class="pagination-controls">';
if (start > 0) {
pagination_html += `<button class="btn btn-secondary" onclick="fetch_tasks(${start - limit}, ${limit})">Previous</button>`;
}
if (fetched_count === limit) {
pagination_html += `<button class="btn btn-secondary" onclick="fetch_tasks(${start + limit}, ${limit})">Next</button>`;
}
pagination_html += '</div>';
container.append(pagination_html);
} catch (error) {
console.error("Error in add_pagination_controls function:", error);
throw error;
}
}
// Make fetch_tasks globally accessible
window.fetch_tasks = function(start, limit) {
var frm = cur_frm; // Use the global cur_frm object
if (frm.fields_dict['tasks_list']) {
var cur_field = frm.fields_dict['tasks_list'];
console.log("Fetching tasks for pagination from start:", start, "with limit:", limit);
frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Task",
filters: [["project", "=", frm.doc.name]],
fields: ["name", "status", "description"],
limit_start: start,
limit_page_length: limit,
},
callback: function(response) {
console.log("Pagination response received:", response);
if (!response.message || response.message.length === 0) {
cur_field.$wrapper.html("No linked tasks found for this project.");
return;
}
try {
var task_list_html = get_task_list_html(response.message);
cur_field.$wrapper.html(task_list_html); // Set the HTML
add_pagination_controls(cur_field.$wrapper, start, limit, response.message.length);
} catch (error) {
console.error("Error generating HTML for pagination:", error);
cur_field.$wrapper.html("Failed to display linked tasks.");
}
},
error: function(error) {
console.error("Error in frappe call for pagination:", error);
cur_field.$wrapper.html("Failed to fetch linked tasks.");
}
});
} else {
console.error("Custom field 'tasks_list' not found.");
}
};
Here’s a screenshot of the result:
This lets you view all linked doctypes for another doctype within the form in a list view.
You can change the fields as per your wishes and dont forget to remove all the console error handlers.
Cheerio!