Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kintone Technical Training - Custom View

Kintone Technical Training - Custom View

Here is the Custom View study content of Kintone Technical Training.
If you want to access the reference link in it directly, please download this PDF file.
We hope you enjoy Kintone technical training journey!

[ Video ]
https://www.youtube.com/watch?v=tmnb-zZmJNI

[ Programs & App Template ]
https://github.com/Cybozu-GTA/kintone-technical-training-materials

Tweet

More Decks by kintone-technical-training

Other Decks in Programming

Transcript

  1. 2 Notice • This material was created using the March

    2021 version of Kintone. • We may change our contents without prior notice.
  2. 3 Prerequisites • You have a Kintone Account with Kintone

    System Administrator's privilege. • You understand the basics of JavaScript. • You have Google Chrome and a code editor installed. • You have cloned our GitHub Repository to local. • You have created apps with App Template (Custom View/Custom_View_AppTemplate.zip) in our GitHub Repository. • You can also use the apps that you created in Kintone Technical Training – JavaScript API.
  3. 4 Useful Resources • Kintone Developer Program – API Docs

    • https://kintone.dev/en/docs/ • Kintone Help Site • https://get.kintone.help/k/en/ • Kintone Technical Training – Development Overview • https://speakerdeck.com/cybozugta/kintone-technical-training-development-overview • Kintone Technical Training YouTube Channel • https://www.youtube.com/playlist?list=PLJOThIyQA7oOINO73ah34ZdHy13Sd6331 • Google Chrome DevTools • https://developers.google.com/web/tools/chrome-devtools • https://developers.google.com/web/tools/chrome-devtools/javascript • MDN Web docs • https://developer.mozilla.org/en-US/
  4. 7 What We’ll Build – Sales Actual/Forecast vs. Budget The

    scenario in this training assumes that: • Users store actual/forecast sales revenue in the Sales Deals app. • Users input their monthly budget in the Budget app. We are going to build customization so that users can check actual/forecast vs. budget at a glance.
  5. 9 What We’ll Build – Sales Actual/Forecast vs. Budget Sales

    Deals Budget User/Department Information Retrieve records Retrieve records Retrieve user list Create/Aggregate dataset to render Render data in Sales Deals View Dataset (Structure) Data Source
  6. 10 How to Design/Build Sales Deals Budget User/Department Information Retrieve

    records Retrieve records Retrieve user list Create/Aggregate dataset to render Render data in Sales Deals Design from the goal in the opposite direction of the data flow Data flow
  7. 11 How to Design Sales Deals Budget User/Department Information Retrieve

    records Retrieve records Retrieve user list Create/Aggregate dataset to render Render data in Sales Deals 1. What kind of view to render • Just view or some interactions • Find appropriate libraries if necessary 2. Design a dataset that is easy to render • Data structure to render easily 3. Collect the data that is needed for the dataset • App records • App settings • User/Department Design from the goal in the opposite direction of the data flow Data flow
  8. 13 What Kind of View to Render Fiscal year selection

    Aggregated data table 1. Initial table rendering for default fiscal year 2. Re-render table by selecting fiscal year
  9. 14 <tbody> <thead> <table> <tr> <tr> <td> <th> <table> <thead>

    <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> What Kind of View to Render
  10. 15 Design a Dataset That is Easy to View [{

    user1: { forecasts: Array(12), targets: Array(12) } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] <table> <thead> <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] View Dataset (Structure)
  11. 16 Collect the Data That is Needed for the Dataset

    [{ user1: { forecasts: Array(12), targets: Array(12) } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] Sales Deals User/Department Information Budget Data Source Dataset (Structure)
  12. 17 Coding Flow [{ user1: { forecasts: Array(12), targets: Array(12)

    } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] Sales Deals User/Department Information Budget Data Source Dataset (Structure) <table> <thead> <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> View Start coding in the same direction of the data flow Data flow
  13. 18 Coding Flow [{ user1: { forecasts: Array(12), targets: Array(12)

    } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] Sales Deals User/Department Information Budget Data Source Dataset (Structure) <table> <thead> <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> View Start coding in the same direction of the data flow 1. Retrieve data from data sources • Conditional data retrieval (range, filtering) 2. Create dataset • Data structure to render easily 3. Render data • HTML rendering • Event attachment Data flow
  14. Name Field Code Type Default Value Date Date Date Rep

    Rep User Selection Logged-in user Target Target Number 20 Preparation – Create Budget App Quickly Don’t forget to Update App
  15. 21 Preparation – Set up Custom View on Sales Deals

    app 8. Don’t forget to Update App 1. Input Sales Actual/Forecast vs. Budget 2. Choose Custom view 6. Input <div id="container"></div> 4. Remember View ID 3. Choose Desktop View only 5. Uncheck Enable pagenation 7. Press Save
  16. 22 Preparation – Set up JavaScript & CSS Files on

    Sales Deals app 5. Don’t forget to Update App 1. Copy and paste the URLs from Custom View/common/Dependencies.txt 2. Upload Custom View/Hands-on #1/customize.js 3. Upload Custom View/51-modern-default.css and Custom View/common/customize.css 4. Press Save
  17. 23 Preparation – Add records on Sales Deals app and

    Budget app Sales Deals app Budget app Add some records in both apps so that we can check the customize behaviors later. * The fields marked up by should be filled in with some value.
  18. 24 Preparation – Use Kintone-like Stylesheet (table tag) <table class="kintoneplugin-table">

    <thead> <tr> <th class="kintoneplugin-table-th"><span class="title">Row Title 1</span></th> <th class="kintoneplugin-table-th-blankspace"></th> </tr> </thead> <tbody> <tr> <td> <div class="kintoneplugin-table-td-control"> <div class="kintoneplugin-table-td-control-value"> <div class="kintoneplugin-input-outer"> <input class="kintoneplugin-input-text" type="text" /> </div> </div> </div> </td> <td class="kintoneplugin-table-td-operation"> <button type="button" class="kintoneplugin-button-add-row-image" title="Add row"></button> <button type="button" class="kintoneplugin-button-remove-row-image" title="Delete this row"></button> </td> </tr> </tbody> </table> 51-modern-default.css contains styles for Kintone plug-in development. For table tag You’ll use two of them in this hands-on. GitHub Code
  19. 25 Preparation – Use Kintone-like Stylesheet (select tag) 51-modern-default.css contains

    styles for Kintone plug-in development. For select tag <div class="kintoneplugin-select-outer"> <div class="kintoneplugin-select"> <select> <option>Choice 1</option> <option>Choice 2</option> <option>Choice 3</option> </select> </div> </div> GitHub Code
  20. 26 Preparation – Replace Parameters in customize.js // constant values

    for app and custom view var BUDGET_APP_ID =your_budget_app_id; var CUSTOM_VIEW_ID =your_sales_deals_app_custom_view_id; // constant values for this customization var MAX_READ_LIMIT = 100; var DateTime = luxon.DateTime; var client = new KintoneRestAPIClient({}); /** * Get all users via User API * @param {Object} params - object argument * - {Number} [limit] - size * - {Number} [offset=0] - offset * - {Array} [ids=[]] - ids * - {Array} [codes=[]] - code * @return {Object} object return * - {Array} users - users */ var getUsers = function(params) { // }; Tips: The easiest way to check the app ID See the URL when you open Budget app list view. You can also check it in App Management page. Replace with your view ID you set up previously Replace with your app ID of Budget app Custom View/Hands-on #1/customize.js GitHub Code
  21. 28 Functions and Events // Build aggregation data buildData //

    Create HTML for table with HTML template engine createTableHTML // Render table renderTable // Create HTML for dropdown with HTML template engine createDropdownHTML // Render dropdown renderDropdown // Event handler for app.record.index.show indexHandler // Attach app.record.index.show event kintone.events.on('app.record.index.show', indexHandler); Retrieve data & Create dataset Render table Render drop-down Define event handler Attach event (listener) Hands-on #1 Hands-on #2 Hands-on #3 Custom View/Hands-on #3/customize.js
  22. 30 Hands-on #1 Data Retrieval and Dataset Creation [{ user1:

    { forecasts: Array(12), targets: Array(12) } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] Sales Deals User/Department Information Budget Data Source Dataset (Structure)
  23. 31 var buildData = function(params) { var year = params.year;

    var start = DateTime.local().set({year: year}).startOf('year').toISODate(); var end = DateTime.local().set({year: year}).endOf('year').toISODate(); var salesCondition = 'Expected_Close_Date >= "' + start + '" and Expected_Close_Date <= "' + end + '"'; var targetCondition = 'Date >= "' + start + '" and Date <= "' + end + '"'; return kintone.Promise.all([ getUsers(), client.record.getAllRecordsWithId({ app: kintone.app.getId(), condition: salesCondition }), client.record.getAllRecordsWithId({ app: BUDGET_APP_ID, condition: targetCondition }) ]).then(function(response) { console.log(response); var users = response[0]; var salesRecords = response[1]; var targetRecords = response[2]; // create dataset after retrieving from sources here return data; }); }; Hands-on #1 buildData – Data Retrieval Part and Dataset Creation Part GitHub Code
  24. var buildData = function(params) { var year = params.year; var

    start = DateTime.local().set({year: year}).startOf('year').toISODate(); var end = DateTime.local().set({year: year}).endOf('year').toISODate(); var salesCondition = 'Expected_Close_Date >= "' + start + '" and Expected_Close_Date <= "' + end + '"'; var targetCondition = 'Date >= "' + start + '" and Date <= "' + end + '"'; return kintone.Promise.all([ getUsers(), client.record.getAllRecordsWithId({ app: kintone.app.getId(), condition: salesCondition }), client.record.getAllRecordsWithId({ app: BUDGET_APP_ID, condition: targetCondition }) ]).then(function(response) { console.log(response); var users = response[0]; var salesRecords = response[1]; var targetRecords = response[2]; // create dataset after retrieving from sources here return data; }); }; 32 Dataset creation part Data retrieval part GitHub Code Hands-on #1 buildData – Data Retrieval Part and Dataset Creation Part
  25. 33 Hands-on #1 buildData – Data Retrieval Part var year

    = params.year; var start = DateTime.local().set({year: year}).startOf('year').toISODate(); var end = DateTime.local().set({year: year}).endOf('year').toISODate(); var salesCondition = 'Expected_Close_Date >= "' + start + '" and Expected_Close_Date <= "' + end + '"'; var targetCondition = 'Date >= "' + start + '" and Date <= "' + end + '"'; Create a query condition from the year value set in the dropdown For example, if the year is 2020, the variables, start and end should be 2020-01-01 and 2020-12-31. You can use DateTime object from Luxon to create ISO 8601 date format easily when Luxon is loaded in advance. https://moment.github.io/luxon/api-docs/index.html#datetime https://kintone.dev/en/docs/kintone/rest-api/records/get-records/ Create query conditions by referring to API Doc. If the year is 2020, targetCondition becomes Date >= "2020-01-01" and Date <= "2020-12-31". Luxon is a successor to Moment that is famous with handling date format. GitHub Code GitHub Code
  26. 34 Hands-on #1 buildData – Data Retrieval Part var salesCondition

    = 'Expected_Close_Date >= "' + start + '" and Expected_Close_Date <= "' + end + '"'; var targetCondition = 'Date >= "' + start + '" and Date <= "' + end + '"'; return kintone.Promise.all([ getUsers(), client.record.getAllRecordsWithId({ app: kintone.app.getId(), condition: salesCondition }), client.record.getAllRecordsWithId({ app: BUDGET_APP_ID, condition: targetCondition }) ]) Call API requests to collect data Promise object to get user information Promise object to get records from Sales Deals Promise object to get records from Budget Promise.all() returns a single Promise object that fulfills when all of the promises passed as an iterable have been fulfilled. That means Promise.all() itself is a Promise object and can be chained with .then(). client from KintoneRestAPIClient is a REST API Client for Kintone. You can use getAllRecordsWithId() here to get all records that match the query conditions. https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client GitHub Code
  27. 35 Hands-on #1 buildData – Dataset creation part return kintone.Promise.all([

    getUsers(), client.record.getAllRecordsWithId({ app: kintone.app.getId(), condition: salesCondition }), client.record.getAllRecordsWithId({ app: BUDGET_APP_ID, condition: targetCondition }) ]).then(function(response) { console.log(response); var users = response[0]; var salesRecords = response[1]; var targetRecords = response[2]; // create dataset after retrieving from sources here return data; }); The response for Promise.all() includes the array of the result of each Promise object. In this case, the argument of kintone.Promise.all() is the array that has 3 Promise object. So, the response has the array that has 3 elements. Create dataset and return data in this block Finally, kintone.Promise.all().then(function(){ }) becomes a Promise object that returns dataset to render. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all GitHub Code
  28. 36 Hands-on #1 buildData – Dataset creation part ]).then(function(response) {

    console.log(response); var users = response[0]; var salesRecords = response[1]; var targetRecords = response[2]; var data = {}; users.forEach(function(user) { user.forecasts = Array(12).fill(0); user.targets = Array(12).fill(0); data[user.code] = user; }); salesRecords.forEach(function(record) { var user = record.Rep.value[0].code; var month = record.Expected_Close_Date.value.split('-')[1]; month = Number(month); data[user].forecasts[month - 1] += Number(record.Forecast_Value.value) || 0; }); targetRecords.forEach(function(record) { var user = record.Rep.value[0].code; var month = record.Date.value.split('-')[1]; month = Number(month); data[user].targets[month - 1] += Number(record.Target.value) || 0; }); return data; }); Initialize dataset template Set sales data into dataset based on user and month Set target budget data into dataset based on user and month GitHub Code
  29. var indexHandler = function(event) { if (event.viewId !== CUSTOMIZE_VIEW_ID) {

    return event; } return buildData({year: 2020}).then(function(response) { console.log(response); return event; }).catch(function(error) { alert('Error Occurred.'); console.log(error); return event; }); }; kintone.events.on('app.record.index.show', indexHandler); 37 Hands-on #1 indexHandler and kintone.events.on The event object should be always returned in Kintone’s event handler. Main logic for this event Return event without any logics if table should not be displayed Define Kintone event Define events with buildData GitHub Code
  30. 38 Hands-on #1 Testing Make sure you see the following

    output in the Console tab: • User information, sales deals, and budget data • Created dataset which includes data above
  31. 40 Hands-on #2 – Set up JavaScript & CSS Files

    on Sales Deals app 3. Don’t forget to Update App 1. Upload Custom View/Hands-on #2/customize.js 2. Press Save
  32. 41 Hands-on #2 – Replace Parameters in customize.js // constant

    values for app and custom view var BUDGET_APP_ID =your_budget_app_id; var CUSTOM_VIEW_ID =your_sales_deals_app_custom_view_id; // constant values for this customization var MAX_READ_LIMIT = 100; var DateTime = luxon.DateTime; var client = new KintoneRestAPIClient({}); /** * Get all users via User API * @param {Object} params - object argument * - {Number} [limit] - size * - {Number} [offset=0] - offset * - {Array} [ids=[]] - ids * - {Array} [codes=[]] - code * @return {Object} object return * - {Array} users - users */ var getUsers = function(params) { // }; Tips: The easiest way to check the app ID See the URL when you open Budget app list view. You can also check it in App Management page. Replace with your view ID you set up previously Replace with your app ID of Budget app Custom View/Hands-on #2/customize.js GitHub Code
  33. 42 <tbody> <thead> <table> <tr> <tr> <td> <th> <table> <thead>

    <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> Hands-on #2 Table Created by createTableHTML and renderTable
  34. 43 Hands-on #2 Operation in createTableHTML [{ user1: { forecasts:

    Array(12), targets: Array(12) } },{ user2: { forecasts: Array(12), targets: Array(12) } },{ user3: { forecasts: Array(12), targets: Array(12) } },...] <table> <thead> <tr> <th></th><th></th>...<th></th> </tr> </thead> <tbody> <tr> <td></td><td></td>...<td></td> </tr> <tr> <td></td><td></td>...<td></td> </tr> ... </tbody> </table> ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] HTML Dataset (Structure)
  35. <table> <thead> <tr> <th>Name</th> <% titles.forEach(function(title) { %> <th><%- title

    %></th> <% }); %> </tr> </thead> <tbody> <% Object.keys(data).forEach(function(user) { %> <tr> <td><%- user %></td> <% data[user].forEach(function(value) { %> <td><%- value %></td> <% }); %> </tr> <% }); %> </tbody> </table> 44 Ref. Template Engine Example(1/2) - Lodash Template HTML string & JavaScript description JavaScript description should be in <% %> JavaScript value should be in <%- %> https://lodash.com/docs/4.17.15#template
  36. var titles = ['title1', 'title2', 'title3']; var data = {

    sadaharu: [1, 2, 3], gauss: [4, 5, 6] }; <table <thead> <tr> <th>Name</th><th>title1</th><th>title2</th><th>title3</th> </tr> </thead> <tbody> <tr> <td>sadaharu</td><td>1</td><td>2</td><td>3</td> </tr> <tr> <td>gauss</td><td>4</td><td>5</td><td>6</td> </tr> </tbody> </table> 45 Ref. Template Engine Example(2/2) - Lodash Data Output JSON https://lodash.com/docs/4.17.15#template HTML Template engine allows to add dynamic logic to static HTML securely
  37. 46 Hands-on #2 createTableHTML Create HTML with a template engine

    var createTableHTML = function(params) { var data = params.data; var colTitles = []; for (var i = 0; i < 12; i++) { colTitles.push(luxon.Info.months('short')[i]); } var templateString = '<table class="kintoneplugin-table">' + ... '</table>'; return _.template(templateString)({data: data, colTitles: colTitles}); }; Template string JSON data GitHub Code
  38. 47 Hands-on #2 renderTable var renderTable = function(params) { var

    year = params.year; var containerElement = document.querySelector('#container’); return buildData({ year: year }).then(function(data) { containerElement.innerHTML = createTableHTML({data: data}); return data; }); }; Get the element div#container to insert the table HTML Insert the HTML after fetching data Render table HTML GitHub Code
  39. var indexHandler = function(event) { if (event.viewId !== CUSTOM_VIEW_ID) {

    return event; } return renderTable({year: 2020}).then(function(response) { console.log(response); return event; }).catch(function(error) { alert('Error Occurred.'); console.log(error); return event; }); }; kintone.events.on('app.record.index.show', indexHandler); 48 Hands-on #2 indexHandler and kintone.events.on The event object should be always returned in Kintone’s event handler. Main logic for this event Return event without any logics if table should not be displayed Define Kintone event Define events with renderTable GitHub Code
  40. 49 Hands-on #2 Testing Make sure you see the following

    output: • Created dataset which includes data above in the Console tab • Rendered table in the Custom View
  41. 51 Hands-on #3 – Set up JavaScript & CSS Files

    on Sales Deals app 3. Don’t forget to Update App 1. Upload Custom View/Hands-on #3/customize.js 2. Press Save
  42. 52 Hands-on #3 – Replace Parameters in customize.js // constant

    values for app and custom view var BUDGET_APP_ID =your_budget_app_id; var CUSTOM_VIEW_ID =your_sales_deals_app_custom_view_id; // constant values for this customization var MAX_READ_LIMIT = 100; var DateTime = luxon.DateTime; var client = new KintoneRestAPIClient({}); /** * Get all users via User API * @param {Object} params - object argument * - {Number} [limit] - size * - {Number} [offset=0] - offset * - {Array} [ids=[]] - ids * - {Array} [codes=[]] - code * @return {Object} object return * - {Array} users - users */ var getUsers = function(params) { // }; Tips: The easiest way to check the app ID See the URL when you open Budget app list view. You can also check it in App Management page. Replace with your view ID you set up previously Replace with your app ID of Budget app Custom View/Hands-on #3/customize.js GitHub Code
  43. 54 Hands-on #3 createDropdownHTML var createDropdownHTML = function() { var

    templateString = '<div class="kintoneplugin-select-outer">' + ' <div class="kintoneplugin-select">' + ' <select id="fy">' + ' <% options.forEach(function(option) { %>' + ' <option value="<%- option.year %>" <%- option.selected %>>FY<%- option.year %></option>' + ' <% }) %>' + ' </select>' + ' </div>' + '</div>'; var now = DateTime.local(); var options = []; for (var i = -2; i <= 1; i++) { // 4 years choices options.push({ selected: i === 0 ? 'selected' : '', // The value of selected is “selected” when i equals 0 year: now.year + i // The value of year is “this year” (e.g. 2020) when i equals 0 }); } return _.template(templateString)({options: options}); }; Create HTML with a template engine Template String JSON Data GitHub Code
  44. 55 Ref. Template Engine Example - Lodash <select id="fy"> <%

    options.forEach(function(option) { %> <option value="<%- option.year %>" <%- option.selected %>>FY<%- option.year %></option> <% }); %> </select> Template Data Output <select id="fy"> <option value="2018">FY2018</option> <option value="2019">FY2019</option> <option value="2020" selected="">FY2020</option> <option value="2021">FY2021</option> </select> var options = [{ year: 2018 selected: '' },{ year: 2019 selected: '' },{ year: 2020 selected: 'selected' },{ year: 2021 selected: '' }]; Output HTML string & JavaScript description JavaScript description should be in <% %> JSON Template engine allows to add dynamic logic to static HTML securely https://lodash.com/docs/4.17.15#template HTML JavaScript value should be in <%- %>
  45. var renderDropdown = function() { var divElement = document.createElement('div'); kintone.app.getHeaderMenuSpaceElement().appendChild(divElement);

    divElement.innerHTML = createDropdownHTML(); var selectElement = document.querySelector('#fy'); var year; selectElement.addEventListener('change', function(event) { year = event.target.value; renderTable({ year: year }).catch(function(error) { console.log(error); alert('Error Occurred.’); }); }, false); year = selectElement.value; return renderTable({ year: year }); }; 56 Hands-on #3 renderDropdown Put select element in a header menu Define change event to select#fy Initial rendering Get the element select#fy GitHub Code
  46. 57 Hands-on #3 indexHandler and kintone.events.on var indexHandler = function(event)

    { if (event.viewId !== CUSTOMIZE_VIEW_ID) { return event; } return renderDropdown().then(function(response) { console.log(response); return event; }).catch(function(error) { alert('Error Occurred.'); console.log(error); return event; }); }; kintone.events.on('app.record.index.show', indexHandler); The event object should be always returned in Kintone’s event handler. Main logic for this event Define Kintone event Define events with rednerDropdown GitHub Code Return event without any logics if table should not be displayed
  47. 58 Hands-on #3 Testing Make sure you see the following

    output: • Dropdown next to the graph icon in the header * You can emit the output in the Console tab by removing console.log(response).