Category Archive Joget

Byphunsanit

JoGet: Bean Shell Create Json

วิธีดีที่สุดในการให้ข้อมูลจำนวนมากให้ javascript คือรูปแบบ json ไม่เว้นแม้แต่ใน JoGet เพราะถึงแม้จะสามารถดึงข้อมูลจาก input หรือ grid ได้ แต่มันจะถูกบันทึกลง database ไปด้วยแถมดูรก

วิธีที่ดีอีกวิธีคือให้ beanshell ใน section ดึงข้อมูลจาก query แล้วแสดงออกมาใน input ตัวเดียว

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.service.FormUtil;
import org.joget.commons.util.LogUtil;
import org.json.simple.JSONObject;

public FormRowSet load(Element element, String username, FormData formData) {

 FormRowSet rows = new FormRowSet();

 Connection con = null;
 try {
  DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
  con = ds.getConnection();

  sql = "SELECT appId, appVersion, name FROM jwdb.app_app WHERE published = 1;";
  //LogUtil.info("Bean Shell Create Json", "sql = " + sql);

  if (!con.isClosed()) {
   PreparedStatement stmt = con.prepareStatement(sql);
   ResultSet rs = stmt.executeQuery();

   /* create json */
   JSONObject json = new JSONObject();
   while (rs.next()) {
    JSONObject item = new JSONObject();

    item.put("appVersion", rs.getObject("appVersion"));
    item.put("name", rs.getObject("name"));

    json.put(rs.getObject("appId"), item);
   }

   /* add json to input */
   FormRow row = new FormRow();
   rows.add(row);
   row.put("json", json.toString());
  }

 } catch (Exception e) {
  LogUtil.error(getClassName(), e, "Bean Shell Create Json");
 } finally {
  try {
   if (con != null) {
    con.close();
   }
  } catch (SQLException e) {}
 }

 return rows;
}

return load(element, primaryKey, formData);

ตัวอย่างฟอร์ม

{
    "className": "org.joget.apps.form.model.Form",
    "properties": {
        "noPermissionMessage": "",
        "loadBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "name": "Bean Shell Create Json",
        "description": "",
        "postProcessorRunOn": "both",
        "permission": {
            "className": "",
            "properties": {}
        },
        "id": "BeanShellCreateJson",
        "postProcessor": {
            "className": "",
            "properties": {}
        },
        "storeBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "tableName": "prototypes"
    },
    "elements": [
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.TextArea",
                            "properties": {
                                "readonly": "",
                                "validator": {
                                    "className": "",
                                    "properties": {}
                                },
                                "workflowVariable": "",
                                "id": "json",
                                "label": "JSON",
                                "placeholder": "",
                                "rows": "10",
                                "value": "",
                                "cols": "100",
                                "readonlyLabel": ""
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "org.joget.apps.form.lib.BeanShellFormBinder",
                    "properties": {
                        "useAjax": "",
                        "cacheIdlePause": "120",
                        "cacheInterval": "",
                        "script": "import java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport javax.sql.DataSource;\nimport org.joget.apps.app.service.AppUtil;\nimport org.joget.apps.form.model.Element;\nimport org.joget.apps.form.model.FormData;\nimport org.joget.apps.form.model.FormRow;\nimport org.joget.apps.form.model.FormRowSet;\nimport org.joget.apps.form.service.FormUtil;\nimport org.joget.commons.util.LogUtil;\nimport org.json.simple.JSONObject;\n\npublic FormRowSet load(Element element, String username, FormData formData) {\n\n FormRowSet rows = new FormRowSet();\n\n Connection con = null;\n try {\n  DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean(\"setupDataSource\");\n  con = ds.getConnection();\n\n  sql = \"SELECT appId, appVersion, name FROM jwdb.app_app WHERE published = 1;\";\n  //LogUtil.info(\"Bean Shell Create Json\", \"sql = \" + sql);\n\n  if (!con.isClosed()) {\n   PreparedStatement stmt = con.prepareStatement(sql);\n   ResultSet rs = stmt.executeQuery();\n\n   /* create json */\n   JSONObject json = new JSONObject();\n   while (rs.next()) {\n    JSONObject item = new JSONObject();\n\n    item.put(\"appVersion\", rs.getObject(\"appVersion\"));\n    item.put(\"name\", rs.getObject(\"name\"));\n\n    json.put(rs.getObject(\"appId\"), item);\n   }\n\n   /* add json to input */\n   FormRow row = new FormRow();\n   rows.add(row);\n   row.put(\"json\", json.toString());\n  }\n\n } catch (Exception e) {\n  LogUtil.error(getClassName(), e, \"Bean Shell Create Json\");\n } finally {\n  try {\n   if (con != null) {\n    con.close();\n   }\n  } catch (SQLException e) {}\n }\n\n return rows;\n}\n\nreturn load(element, primaryKey, formData);"
                    }
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section1",
                "label": "Bean Shell Create Json",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        }
    ]
}

Byphunsanit

javascript: string to JSON.parse()

หลังจากใช้ MySQL: return json format กันเป็นแล้วเรามาลองใช้กับ joget แทนที่จะต้องไปเขียน beanshell ให้ยุ่งยาก (พลาดง่ายๆ อีกตะหาก) ดูจริงๆ แล้วสามารถใช้กับหน้าเว็บทั้งหมดไม่ใช่แค่ joGet เพราะมันคือ javascript พื้นฐาน

เปิดฟอร์มที่ต้องการมา

  1. สร้าง section ขึ้นมา โดยกำหนด Load Binder เป็น JDBC Binder และ SQL SELECT Query ใส่ query ที่ต้องการ เช่น
    SELECT
        CONCAT('[',
                GROUP_CONCAT(JSON_OBJECT('label', name, 'value', appId)),
                ']') AS optionsList
    FROM
        jwdb.app_app
    ORDER BY name ASC
  2. ลาก Hidden Field มาตั้ง id เป็น optionsList ใน section ที่สร้างไว้
  3. เขียน javascript ดึงค่ามาจาก string json ที่เก็บไว้ใน input ชื่อ optionsList โดยใช้ JSON.parse() เผื่อความปลอดภัย ควรใช้ JSON.parse() ใน try catch เพราะว่าถ้า json ที่ได้ผิดปกติจะสามารถควบคุมได้ ลาก Custom HTML มาแล้วใส่ code
    <table class="table table-striped" id="tableA">
        <thead>
            <tr>
                <th scope="col" style="width:20px;">#</th>
                <th scope="col">Name</th>
                <th scope="col">Value</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
    <script>
        $(document).ready(function () {
    
            let html = new Array();
            let no = 0;
            let optionsList = $('#optionsList');
            try {
                let temp = JSON.parse(optionsList.val());
    
                if (temp.length > 0) {
                    $.each(temp, function (index, value) {
                        no++;
                        html.push('<tr><th scope="row">' + no + '</th><td>' + value.label + '</td><td>' + value.value + '</td><tr>');
                    });
                }
                if (html.length == 0) {
                    html = '<tr><th scope="row">-</th><td>-</td><td>-</td><tr>';
                } else {
                    html = html.join("\n");
                }
            } catch (e) {
                console.log(e.message);
                html = '<h1>Error!!</h1>';
            }
            $('#tableA > tbody').html(html);
    
        });
    </script>

สามารถ copy form ตัวอย่างได้จาก json

{
    "className": "org.joget.apps.form.model.Form",
    "properties": {
        "noPermissionMessage": "",
        "loadBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "name": "JSON Parse",
        "description": "",
        "postProcessorRunOn": "both",
        "permission": {
            "className": "",
            "properties": {}
        },
        "id": "JSONParse",
        "postProcessor": {
            "className": "",
            "properties": {}
        },
        "storeBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "tableName": "prototypes"
    },
    "elements": [
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.HiddenField",
                            "properties": {
                                "useDefaultWhenEmpty": "",
                                "workflowVariable": "",
                                "id": "optionsList",
                                "value": ""
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "org.joget.plugin.enterprise.JdbcLoadBinder",
                    "properties": {
                        "jdbcDatasource": "default",
                        "sql": "SELECT \n    CONCAT('[',\n            GROUP_CONCAT(JSON_OBJECT('label', name, 'value', appId)),\n            ']') AS optionsList\nFROM\n    jwdb.app_app\nORDER BY name ASC"
                    }
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section1",
                "label": "",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        },
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.CustomHTML",
                            "properties": {
                                "autoPopulate": "",
                                "id": "field2",
                                "label": "",
                                "value": "<table class=\"table table-striped\" id=\"tableA\">\n    <thead>\n        <tr>\n            <th scope=\"col\" style=\"width:20px;\">#</th>\n            <th scope=\"col\">Name</th>\n            <th scope=\"col\">Value</th>\n        </tr>\n    </thead>\n    <tbody>\n    </tbody>\n</table>\n<script>\n    $(document).ready(function () {\n\n        let html = new Array();\n        let no = 0;\n        let optionsList = $('#optionsList');\n        try {\n            let temp = JSON.parse(optionsList.val());\n\n            if (temp.length > 0) {\n                $.each(temp, function (index, value) {\n                    no++;\n                    html.push('<tr><th scope=\"row\">' + no + '</th><td>' + value.label + '</td><td>' + value.value + '</td><tr>');\n                });\n            }\n            if (html.length == 0) {\n                html = '<tr><th scope=\"row\">-</th><td>-</td><td>-</td><tr>';\n            } else {\n                html = html.join(\"\\n\");\n            }\n        } catch (e) {\n            console.log(e.message);\n            html = '<h1>Error!!</h1>';\n        }\n        $('#tableA > tbody').html(html);\n\n    });\n</script>"
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "",
                    "properties": {}
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section2",
                "label": "Options List",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        }
    ]
}

Byphunsanit

MySQL: return json format

mysql ก็ support json เพราะว่าเดี๋ยวนี้อะไรๆ ก็แลกเปลี่ยนข้อมูลกันโดยใช้ json ถ้าสามารถ query จาก mysql ให้ result ออกมาเป็น json เลย ก็สะดวกที่จะไม่ต้องใช้ java หรือ beanshell ใน joget แปลงผลลัพธ์ให้ยุ่งยาก แค่มี input ซักตัวก็สามารถนำมันไปใช้ได้เลย

เริ่มจากการที่เขียน query ตามปกติเพื่อเลือกข้อมูลที่ต้องการ เช่น

SELECT
    *
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

จะเห็นว่ามี column PROVINCE_ID, PROVINCE_CODE, PROVINCE_NAME และ GEO_ID ทีนี้จะแปลง ให้เป็นรูปแบบ json array โดยใช้ JSON_OBJECT วิธีการใช้คือ

SELECT JSON_OBJECT('name1', value1, 'name2', value2, 'name3', value3);

คือ

SELECT
    JSON_OBJECT('PROVINCE_ID',
            PROVINCE_ID,
            'PROVINCE_CODE',
            PROVINCE_CODE,
            'PROVINCE_NAME',
            PROVINCE_NAME,
            'GEO_ID',
            GEO_ID) AS json
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

ค่าค่อยดูเป็น json ขึ้นมาหน่อย แต่มันยังอยู่คนละแถว ไม่เป็นไรมีตัวช่วย

SELECT
    CONCAT('[',
            GROUP_CONCAT(JSON_OBJECT('PROVINCE_ID',
                        PROVINCE_ID,
                        'PROVINCE_CODE',
                        PROVINCE_CODE,
                        'PROVINCE_NAME',
                        PROVINCE_NAME,
                        'GEO_ID',
                        GEO_ID)),
            ']') AS json
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

แค่นี้ก็ได้ result เป็น json สมใจ อ่านตัวอย่างการใช้ได้ที่ javascript: string to JSON.parse()

Byphunsanit

joGet: Pending Process Activity and Assignees

ต้องการสร้าง List ใน joGet เพิ่อที่จะได้ดูสถานะและเข้าไปดู form ใน process ตามขั้นตอนต่างๆ ใน workflow

ทำได้โดยใช้ความรู้ 2 เรื่องคือ

การใช้ query เพิ่อดู process activity

process ที่กำลังอยู่ระหว่างสถานะต่างๆ จะถูกบันทึกลงใน database สามารถ query มาดูได้โดยใช้ตัวอย่าง เช่น

SELECT
a.*,
sact.Name AS activityName,
GROUP_CONCAT(DISTINCT sass.ResourceId
SEPARATOR ', ') AS assignee
FROM
app_fd_hr_expense_claim a
JOIN
SHKActivities sact ON a.id = sact.ProcessId
JOIN
SHKActivityStates ssta ON ssta.oid = sact.State
INNER JOIN
SHKAssignmentsTable sass ON sact.Id = sass.ActivityId
WHERE
ssta.KeyValue = 'open.not_running.not_started'
GROUP BY a.id

ตามตัวอย่างใน List Pending Activity and Assignees อย่าลืมเปลี่ยนตาราง a เป็นตารางที่ใช้ใน process จริงๆ

การใช้ link ไปยังฟอร์มตาม activity
ลิงค์ของ joget จะอยู่ในรูปแบบ /jw/web/userview/ {app id} / {userview id} /_/ {process_id} ?_action=assignmentView

ตัวอย่าง list ที่เสร็จแล้ว

{"id":"activity_and_assignees","name":"Activity And Assignees","pageSize":"0","order":"","orderBy":"","showPageSizeSelector":"true","pageSizeSelectorOptions":"10,20,30,40,50,100","buttonPosition":"bottomLeft","checkboxPosition":"left","useSession":"false","hidePageSize":"true","description":"","rowActions":[{"name":"Data List Hyperlink Action","className":"org.joget.apps.datalist.lib.HyperlinkDataListAction","label":"Hyperlink","type":"text","id":"rowAction_0","properties":{"href":"\/jw\/web\/userview\/vehicle\/carpark\/_\/car_park_registration?_action=assignmentView","target":"_self","hrefParam":"activityId","hrefColumn":"activityId","label":"Run Process","confirmation":"","visible":"","rules":[]}}],"actions":[],"filters":[],"binder":{"className":"org.joget.plugin.enterprise.JdbcDataListBinder","properties":{"jdbcDatasource":"default","sql":"SELECT \n    a.*,\n    sass.activityId,\n    sact.Name AS activityName,\n    GROUP_CONCAT(DISTINCT sass.ResourceId\n        SEPARATOR ', ') AS assignee\nFROM\n    app_fd_cars AS a\n        JOIN\n    SHKActivities AS sact ON a.id = sact.ProcessId\n        JOIN\n    SHKActivityStates AS ssta ON ssta.oid = sact.State\n        INNER JOIN\n    SHKAssignmentsTable AS sass ON sact.Id = sass.ActivityId\nWHERE\n    ssta.KeyValue = 'open.not_running.not_started'\nGROUP BY a.id","primaryKey":"id"}},"columns":[{"id":"column_0","label":"id","displayLabel":"id","name":"id"},{"id":"column_1","label":"c_licensePlate","displayLabel":"c_licensePlate","name":"c_licensePlate"},{"id":"column_2","label":"c_brand","displayLabel":"c_brand","name":"c_brand"},{"id":"column_3","label":"c_approve","displayLabel":"c_approve","name":"c_approve"},{"id":"column_4","label":"activityName","displayLabel":"activityName","name":"activityName"},{"id":"column_5","label":"assignee","displayLabel":"assignee","name":"assignee"}]}

นอกจากนี้ยังสามารถหาตัวอย่างได้จาก app Process Monitor Utility ได้อีกด้วย

Byphunsanit

joGet: editable input

ต้องการให้ user ต้องคลิก Click to Edit เพื่อที่จะแก้ไขข้อมูล ที่เป็นข้อมูลที่สำคัญๆ เพื่อป้องกันการบันทึกข้อมูลโดยไม่ตั้งใจ

เพื่อที่จะสามารถ reused code นี้ไปใช้ในฟอร์มอื่นๆ จึงเขียนไว้ในไฟล์กลาง

/*
pitt phunsanit
editable input
version 1
*/
function clickToEdit(inputs) {
$.each(inputs, function(key, value) {
let id = value + "EditFlag";
let input = FormUtil.getField(value);

input
.attr("readonly", true)
.closest(".form-cell")
.append(
'<label style="display: inline;"><input id="' +
id +
'" name="' +
id +
'" type="checkbox" value="Y"> Click to Edit</label>'
);
$("#" + id).on("change", function() {
if ($(this).prop("checked") == true) {
input.prop("readonly", false);
} else {
input.prop("readonly", true);
}
});
});
}

การใช้งานเพียงแค่เรียกใช้ function ถ้าต้องการให้คลิกเพื่อแก้ไข input id firstname, surname, middlename ก็เขียน JavaScript ตามตัวอย่าง

<script src="/jw/assets/scripts.js"></script>
<script>
$(document).ready(function () {

clickToEdit(['firstname', 'surname', 'middlename']);

});
</script>