Category Archive java

เซ็ต JAVA_HOME

ตัวอย่างการตั้งค่า system variables สำหรับการชี้ Java Development Kit (JDK) ที่ติดตั้งในเครื่อง

  1. ในช่องค้นหาพิมย์ว่า edit environment variables for your account
  2. คลิกรายการแรกที่เจอ
  3. ด้านล่าง System variables คลิก New..
  4. ช่อง Variable name: ใส่ JAVA_HOME
  5. ช่อง Variable value: ใส่ path jdk ที่ติดตั้งไว้ เช่น C:\Program Files\Java\jdk1.7.0_80
    Note: อย่าใส่ \bin ลงไปในขั้นตอนนี้
  6. คลิก OK
  7. คลิก Variable ที่ชื่อ Path เพิ่ม %JAVA_HOME%\bin ลงไป ถ้าเป็น windows เก่ากว่า windows 10 ใช้ ; คั่นกับรายการอื่นๆ
    Note: ตัวแปรนี้จะทำให้สามมารถใช้ java commands ได้จากทุกที่
  8. คลิก OK
  9. คลิก OK
  10. ทดสอบโดยการเปิด command ขึ้นมาใหม่ พิมย์
    java -version
    จะเห็นข้อความประมาณ
    java version "1.7.0_80"
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

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": ""
            }
        }
    ]
}

JAVA: Create Json

การสร้าง json ใน java ทั้งแบบ array [“aaa”, “bbb”, “ccc”] และ object {“aaa”:”bbb”, “ccc”:”ddd”, “eee”:”fff”} ทำได้ง่ายๆ

สร้าง json array

import org.json.simple.JSONArray;

JSONArray json = new JSONArray();
while (rs.next()) {
JSONObject item = new JSONArray();

item.add(rs.getObject("appId"));
item.add(rs.getObject("appVersion"));
item.add(rs.getObject("name"));

json.add(item);
}

รวมร่าง

json.add( { JSONArray ที่ต้องการรวม } );

สร้าง json object

import org.json.simple.JSONObject;

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);
}

รวมร่าง

json.put( {index ที่ต้องการ}, { JSONArray ที่ต้องการรวม } );

แปลงเป็น string เพื่อใช้อย่างอื่น

json.toString()

อ่านเพิ่มเติม JoGet: Bean Shell Create Json

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": ""
            }
        }
    ]
}

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()

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 ได้อีกด้วย

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>

joGet: AJAX Subform reload

ตัวอย่างการสร้าง ajax subform ในเรื่อง joGet: AJAX Subform มีโจทย์มาว่า input ที่ผูกกับซับฟอร์มถูกเปลี่ยนค่าจาก javascript ก็คิดว่าค่าใน subform จะเปลี่ยนตามไปด้วย แต่มันกลับไม่เปลี่ยนตาม เพราะ input ค่าเปลี่ยนก็จะทำให้มันโหลดข้อมูลมาใหม่ได้ไม่ยาก แต่มันไม่ทำงาน ลองศึกษาไฟล์ /jw/plugin/org.joget.plugin.enterprise.AjaxSubForm/js/jquery.ajaxsubform.js ดูก็พบว่ามันมีการสั่ง .off เอา event เก่าออกไปก่อน ทำให้แค่เปลี่ยนค่าเฉยๆ มันจะไม่ทำงาน

การสั่ง reload / refresh ajax subform โดยใช้ javascript ทำได้โดย

<button class="btn btn-info" id="refreshBtn" type="button">Reload form</button>
<script>
$(document).ready(function() {

$('#refreshBtn').on('click', function() {

let field1 = FormUtil.getField('field1');
field1.val('72817b0c-ac1430db-671b8000-37df4c8d');
field1.trigger('change');

});

});
</script>

แบบนี้เมื่อนเปลี่ยนค่า field1 subform จะถูกเปลี่ยนค่าตามไปด้วยแล้ว ลองดัดแปลงให้เหมาะกับงานดูนะครับ

joGet: AJAX Subform

AJAX Subform เป็น Form Element ที่ joget เตรียมไว้ให้สามารถใช้ ajax ดึงข้อมูลฟอร์มที่มีมาแสดงได้แบบไม่จำเป็นต้องโหลดทั้งหน้าใหม่ เหมือนระบบที่เราเขียนด้วยตัวเอง

การใช้งานทำได้ง่ายๆ โดยการ

    1. ในหน้า Form Builder ลาก AJAX Subform มาจากด้านซ้ายมือ
    2. คลิก Edit ไอค่อน
    3. กรอกข้อมูล
      ID
      id ของ ajax subform เช่น ajaxSubform
      Form
      เลือกฟอร์มที่ต้องการนำมาแทรกไว้ในหน้านี้
    4. คลิก Next >
    5. ติ๊ก
      Reload Subform when Parent Field value change?
      ติ๊กเพื่อจะให้ ajax ทำงานโหลด ฟอร์มใหม่เมื่อ input ที่ผูกไว้ เปลี่ยนค่า
    6. ผูกฟอร์มทั้งสองไว้ด้วยกัน
      Parent Field to keep Subform ID
      id input ในฟอร์มหลัก ที่จะใช้ลิงค์ให้ sub form ถูกดึงมาแทนที่โดยใช้ ajax
      Subform Field to keep Parent ID
      เป็น input ที่อยู่ใน subform ถ้าไม่มี การ load subform มาจาก server ก็ยังทำงานได้ตามปกติ

ทีนี้มาศึกษาการทำงานกันดูบ้าง

      1. joGet จะแทรกไฟล์ /jw/plugin/org.joget.plugin.enterprise.AjaxSubForm/js/jquery.ajaxsubform.js เพิ่มเข้ามาจากปกติ
      2. จะถูกเรียกใช้โดย javascript ที่เพิ่มเข้ามา เช่น
        <script type="text/javascript">
        $(document).ready(function() {
        $("#ajaxSubform_ajaxsubform_928").ajaxSubForm({
        contextPath: "/jw",
        id: "ajaxSubform",
        label: "",
        formDefId: "sendEmail",
        readOnly: "",
        readOnlyLabel: "",
        noframe: "",
        ajax: "true",
        parentSubFormId: "field1",
        prefix: "",
        hideEmpty: "",
        appId: "prototypes",
        appVersion: "1",
        processId: "",
        nonce: "%EF%BF%BD%00%3B%7D%28%EF%BF%BD%EF%BF%BD%EF%BF%BD",
        collapsible: "",
        collapsibleLabelExpanded: "Hide Details",
        collapsibleLabelCollapsed: "View Details",
        collapsibleExpanded: "true",
        collapsibleNoLoad: ""
        });
        });
        </script>

เมื่อไล่ดู code แล้วมันคือ plugin ที่ extended มาจาก jQuery ajax ตามปกติ ส่ง request ไปยัง /jw/web/json/plugin/org.joget.plugin.enterprise.AjaxSubForm/service โดยส่ง formDefId มีค่าเป็น form id ของ subform, parentSubFormId คือ field ในฟอร์มหลักที่ใช้ผูกกับซับฟอร์ม และจะคืนค่ากลับมาเป็น html ของฟอร์ม ในอนาคตคงได้ใช้เรียก ajax ไปโดยไม่ต้องสร้าง subform ก่อน

อ่านเพิ่มเติม

JoGet: Save PDF From Jasper Report

ตัวอย่างการ save pdf file ที่สร้างมาจาก JasperReports ก่อนที่จะส่งให้ user ทางอีเมล์ เพื่อที่ฝ่ายที่เกี่ยวข้องจะได้นำไฟล์มาตรวจสอบความถูกต้องในภายหลังว่ารีพอร์ตที่เป็น pdf ไฟล์ที่ส่งไปมีเนื่้อหายังไงจากไฟล์ที่บันทึกเอาไว้

เราสามารถทำได้โดยใช้ plugin Bean Shell Tool ตามตัวอย่าง

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import org.joget.apps.app.service.AppUtil;

public Object execute() {

/* get parameters from outside activity tools box */
String id = "#variable.id#";

String documentRoot = "C:\\Joget-v5-Enterprise\\apache-tomcat-8.0.20\\webapps\\jw\\";

/* connection to default datasource */
DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
con = ds.getConnection();

/* load JasperReports template */
String sourceFileName = documentRoot + "assets\\JasperReports\\app.jasper";

Map parameters = new HashMap();

/* send parameters to jasper report */
parameters.put("id", id);

/* genrate pdf file and save to local directory */
printFileName = JasperFillManager.fillReportToFile(sourceFileName, parameters, con);
if (printFileName != null) {
JasperExportManager.exportReportToPdfFile(printFileName, documentRoot + "datas\\policies\\" + id + ".pdf");
}

return null;
}

//call execute method
return execute();

อธิบายการทำงาน จะเรียกใช้ beanshell เข้าไปเรียก class เดียวกับที่ joget ใช้ จึงมั่นใจได้ว่ารีพอร์ตที่ออกมาจะเหมือนกับที่ joget ใช้จริงๆ โดยจะอ่านไฟล์ .jasper มาจาก “C:\Joget-v5-Enterprise\apache-tomcat-8.0.20\webapps\jw\assets\JasperReports\app.jasper” จากนั้นจะสรุปออกมาเป็นไฟล์ .jrprint เอาไว้ใน “C:\Joget-v5-Enterprise\apache-tomcat-8.0.20\webapps\jw\assets\JasperReports\” เหมือนกัน แต่ชื่อไฟล์อาจจะต่างกันโดยจะตั้งตาม atrbut name ที่อยู่ใน tag jasperReport และสร้างไฟล์ pdf เก็บไว้ที่ “C:\Joget-v5-Enterprise\apache-tomcat-8.0.20\webapps\jw\datas\policies” ตาม ที่กำหนดไว้ที่ใน JasperExportManager.exportReportToPdfFile

เพราะว่าไฟล์ใน “C:\Joget-v5-Enterprise\apache-tomcat-8.0.20\webapps\jw” สามารถเรียกดูได้จาก url จึงควรระวังเรื่อง Information leakage เอาไว้ด้วย