Project

General

Profile

Form CRUD - Orders » History » Version 1

Lê Sĩ Quý, 08/29/2025 06:46 PM

1 1 Lê Sĩ Quý
# Form CRUD - Orders
2
3
{{TOC}}
4
5
## JSON Schema
6
7
``` javascript
8
{
9
  "display": "form",
10
  "components": [
11
    {
12
      "title": "<strong>New Order</strong>",
13
      "collapsible": false,
14
      "key": "NewEmployee",
15
      "type": "panel",
16
      "label": "Panel",
17
      "input": false,
18
      "tableView": false,
19
      "components": [
20
        {
21
          "label": "Order ID",
22
          "applyMaskOn": "change",
23
          "tableView": true,
24
          "customDefaultValue": "value = `ORD${moment().unix().toString().slice(-7)}`;",
25
          "allowCalculateOverride": true,
26
          "validate": {
27
            "required": true
28
          },
29
          "validateWhenHidden": false,
30
          "key": "OrderID",
31
          "type": "textfield",
32
          "input": true
33
        },
34
        {
35
          "label": "Customer",
36
          "widget": "choicesjs",
37
          "tableView": true,
38
          "dataSrc": "custom",
39
          "data": {
40
            "custom": "values = $ds.Customers;"
41
          },
42
          "idPath": "Col1",
43
          "template": "<span>{{ item['Company Name'] }}</span>",
44
          "clearOnRefresh": true,
45
          "clearOnHide": false,
46
          "validate": {
47
            "required": true
48
          },
49
          "validateWhenHidden": false,
50
          "key": "Customer",
51
          "type": "select",
52
          "input": true
53
        },
54
        {
55
          "label": "Employee",
56
          "widget": "choicesjs",
57
          "tableView": true,
58
          "dataSrc": "custom",
59
          "data": {
60
            "custom": "values = $ds.Employees;\n"
61
          },
62
          "idPath": "Col0",
63
          "template": "<span>{{ item.FirstName }} {{item.LastName }}</span>",
64
          "clearOnRefresh": true,
65
          "clearOnHide": false,
66
          "validate": {
67
            "required": true
68
          },
69
          "validateWhenHidden": false,
70
          "key": "Employee",
71
          "type": "select",
72
          "input": true
73
        },
74
        {
75
          "label": "Order Date",
76
          "format": "yyyy-MM-dd HH:mm:ss",
77
          "tableView": false,
78
          "datePicker": {
79
            "disableWeekends": false,
80
            "disableWeekdays": false
81
          },
82
          "timePicker": {
83
            "showMeridian": false
84
          },
85
          "defaultDate": "moment()",
86
          "validate": {
87
            "required": true
88
          },
89
          "enableMinDateInput": false,
90
          "enableMaxDateInput": false,
91
          "validateWhenHidden": false,
92
          "key": "OrderDate",
93
          "type": "datetime",
94
          "input": true,
95
          "widget": {
96
            "type": "calendar",
97
            "displayInTimezone": "viewer",
98
            "locale": "en",
99
            "useLocaleSettings": false,
100
            "allowInput": true,
101
            "mode": "single",
102
            "enableTime": true,
103
            "noCalendar": false,
104
            "format": "yyyy-MM-dd HH:mm:ss",
105
            "hourIncrement": 1,
106
            "minuteIncrement": 1,
107
            "time_24hr": true,
108
            "minDate": null,
109
            "disableWeekends": false,
110
            "disableWeekdays": false,
111
            "maxDate": null
112
          }
113
        },
114
        {
115
          "label": "Required Date",
116
          "format": "yyyy-MM-dd",
117
          "tableView": false,
118
          "datePicker": {
119
            "disableWeekends": false,
120
            "disableWeekdays": false
121
          },
122
          "enableTime": false,
123
          "enableMinDateInput": false,
124
          "enableMaxDateInput": false,
125
          "validateWhenHidden": false,
126
          "key": "RequiredDate",
127
          "type": "datetime",
128
          "input": true,
129
          "widget": {
130
            "type": "calendar",
131
            "displayInTimezone": "viewer",
132
            "locale": "en",
133
            "useLocaleSettings": false,
134
            "allowInput": true,
135
            "mode": "single",
136
            "enableTime": false,
137
            "noCalendar": false,
138
            "format": "yyyy-MM-dd",
139
            "hourIncrement": 1,
140
            "minuteIncrement": 1,
141
            "time_24hr": false,
142
            "minDate": null,
143
            "disableWeekends": false,
144
            "disableWeekdays": false,
145
            "maxDate": null
146
          }
147
        },
148
        {
149
          "label": "Shipped Date",
150
          "format": "yyyy-MM-dd",
151
          "tableView": false,
152
          "datePicker": {
153
            "disableWeekends": false,
154
            "disableWeekdays": false
155
          },
156
          "enableTime": false,
157
          "enableMinDateInput": false,
158
          "enableMaxDateInput": false,
159
          "validateWhenHidden": false,
160
          "key": "ShippedDate",
161
          "type": "datetime",
162
          "input": true,
163
          "widget": {
164
            "type": "calendar",
165
            "displayInTimezone": "viewer",
166
            "locale": "en",
167
            "useLocaleSettings": false,
168
            "allowInput": true,
169
            "mode": "single",
170
            "enableTime": false,
171
            "noCalendar": false,
172
            "format": "yyyy-MM-dd",
173
            "hourIncrement": 1,
174
            "minuteIncrement": 1,
175
            "time_24hr": false,
176
            "minDate": null,
177
            "disableWeekends": false,
178
            "disableWeekdays": false,
179
            "maxDate": null
180
          }
181
        },
182
        {
183
          "label": "Ship Via",
184
          "widget": "choicesjs",
185
          "tableView": true,
186
          "dataSrc": "custom",
187
          "data": {
188
            "custom": "values = $ds.Shippers;\n"
189
          },
190
          "idPath": "Col0",
191
          "template": "<span>{{ item['CompanyName'] }}</span>",
192
          "clearOnRefresh": true,
193
          "clearOnHide": false,
194
          "validate": {
195
            "required": true
196
          },
197
          "validateWhenHidden": false,
198
          "key": "ShipVia",
199
          "type": "select",
200
          "input": true
201
        },
202
        {
203
          "label": "Freight",
204
          "applyMaskOn": "change",
205
          "mask": false,
206
          "tableView": false,
207
          "delimiter": false,
208
          "requireDecimal": false,
209
          "inputFormat": "plain",
210
          "truncateMultipleSpaces": false,
211
          "validateWhenHidden": false,
212
          "key": "Freight",
213
          "type": "number",
214
          "input": true
215
        },
216
        {
217
          "title": "<strong>Shipment</strong>",
218
          "collapsible": false,
219
          "key": "shipment",
220
          "type": "panel",
221
          "label": "Panel",
222
          "input": false,
223
          "tableView": false,
224
          "components": [
225
            {
226
              "label": "Name",
227
              "applyMaskOn": "change",
228
              "tableView": true,
229
              "calculateValue": "var customer = _.find($ds.Customers, {'Customer ID': data.Customer['Customer ID']});\nvalue = customer ? customer['Contact Name'] : '';\n",
230
              "allowCalculateOverride": true,
231
              "validate": {
232
                "required": true
233
              },
234
              "validateWhenHidden": false,
235
              "key": "ShipName",
236
              "type": "textfield",
237
              "input": true
238
            },
239
            {
240
              "label": "Address",
241
              "applyMaskOn": "change",
242
              "tableView": true,
243
              "calculateValue": "var customer = _.find($ds.Customers, {'Customer ID': data.Customer['Customer ID']});\nvalue = customer ? customer['Address'] : '';\n",
244
              "allowCalculateOverride": true,
245
              "validate": {
246
                "required": true
247
              },
248
              "validateWhenHidden": false,
249
              "key": "ShipAddress",
250
              "type": "textfield",
251
              "input": true
252
            },
253
            {
254
              "label": "City",
255
              "applyMaskOn": "change",
256
              "tableView": true,
257
              "calculateValue": "var customer = _.find($ds.Customers, {'Customer ID': data.Customer['Customer ID']});\nvalue = customer ? customer['City'] : '';\n",
258
              "allowCalculateOverride": true,
259
              "validateWhenHidden": false,
260
              "key": "ShipCity",
261
              "type": "textfield",
262
              "input": true
263
            },
264
            {
265
              "label": "Region",
266
              "applyMaskOn": "change",
267
              "tableView": true,
268
              "calculateValue": "var customer = _.find($ds.Customers, {'Customer ID': data.Customer['Customer ID']});\nvalue = customer ? customer['Region'] : '';\n",
269
              "allowCalculateOverride": true,
270
              "validateWhenHidden": false,
271
              "key": "ShipRegion",
272
              "type": "textfield",
273
              "input": true
274
            },
275
            {
276
              "label": "Postal Code",
277
              "applyMaskOn": "change",
278
              "tableView": true,
279
              "validateWhenHidden": false,
280
              "key": "ShipPostalCode",
281
              "type": "textfield",
282
              "input": true
283
            },
284
            {
285
              "label": "Country",
286
              "widget": "choicesjs",
287
              "tableView": true,
288
              "dataSrc": "json",
289
              "data": {
290
                "json": [
291
                  {
292
                    "name": "Afghanistan",
293
                    "code": "AF"
294
                  },
295
                  {
296
                    "name": "Åland Islands",
297
                    "code": "AX"
298
                  },
299
                  {
300
                    "name": "Albania",
301
                    "code": "AL"
302
                  },
303
                  {
304
                    "name": "Algeria",
305
                    "code": "DZ"
306
                  },
307
                  {
308
                    "name": "American Samoa",
309
                    "code": "AS"
310
                  },
311
                  {
312
                    "name": "AndorrA",
313
                    "code": "AD"
314
                  },
315
                  {
316
                    "name": "Angola",
317
                    "code": "AO"
318
                  },
319
                  {
320
                    "name": "Anguilla",
321
                    "code": "AI"
322
                  },
323
                  {
324
                    "name": "Antarctica",
325
                    "code": "AQ"
326
                  },
327
                  {
328
                    "name": "Antigua and Barbuda",
329
                    "code": "AG"
330
                  },
331
                  {
332
                    "name": "Argentina",
333
                    "code": "AR"
334
                  },
335
                  {
336
                    "name": "Armenia",
337
                    "code": "AM"
338
                  },
339
                  {
340
                    "name": "Aruba",
341
                    "code": "AW"
342
                  },
343
                  {
344
                    "name": "Australia",
345
                    "code": "AU"
346
                  },
347
                  {
348
                    "name": "Austria",
349
                    "code": "AT"
350
                  },
351
                  {
352
                    "name": "Azerbaijan",
353
                    "code": "AZ"
354
                  },
355
                  {
356
                    "name": "Bahamas",
357
                    "code": "BS"
358
                  },
359
                  {
360
                    "name": "Bahrain",
361
                    "code": "BH"
362
                  },
363
                  {
364
                    "name": "Bangladesh",
365
                    "code": "BD"
366
                  },
367
                  {
368
                    "name": "Barbados",
369
                    "code": "BB"
370
                  },
371
                  {
372
                    "name": "Belarus",
373
                    "code": "BY"
374
                  },
375
                  {
376
                    "name": "Belgium",
377
                    "code": "BE"
378
                  },
379
                  {
380
                    "name": "Belize",
381
                    "code": "BZ"
382
                  },
383
                  {
384
                    "name": "Benin",
385
                    "code": "BJ"
386
                  },
387
                  {
388
                    "name": "Bermuda",
389
                    "code": "BM"
390
                  },
391
                  {
392
                    "name": "Bhutan",
393
                    "code": "BT"
394
                  },
395
                  {
396
                    "name": "Bolivia",
397
                    "code": "BO"
398
                  },
399
                  {
400
                    "name": "Bosnia and Herzegovina",
401
                    "code": "BA"
402
                  },
403
                  {
404
                    "name": "Botswana",
405
                    "code": "BW"
406
                  },
407
                  {
408
                    "name": "Bouvet Island",
409
                    "code": "BV"
410
                  },
411
                  {
412
                    "name": "Brazil",
413
                    "code": "BR"
414
                  },
415
                  {
416
                    "name": "British Indian Ocean Territory",
417
                    "code": "IO"
418
                  },
419
                  {
420
                    "name": "Brunei Darussalam",
421
                    "code": "BN"
422
                  },
423
                  {
424
                    "name": "Bulgaria",
425
                    "code": "BG"
426
                  },
427
                  {
428
                    "name": "Burkina Faso",
429
                    "code": "BF"
430
                  },
431
                  {
432
                    "name": "Burundi",
433
                    "code": "BI"
434
                  },
435
                  {
436
                    "name": "Cambodia",
437
                    "code": "KH"
438
                  },
439
                  {
440
                    "name": "Cameroon",
441
                    "code": "CM"
442
                  },
443
                  {
444
                    "name": "Canada",
445
                    "code": "CA"
446
                  },
447
                  {
448
                    "name": "Cape Verde",
449
                    "code": "CV"
450
                  },
451
                  {
452
                    "name": "Cayman Islands",
453
                    "code": "KY"
454
                  },
455
                  {
456
                    "name": "Central African Republic",
457
                    "code": "CF"
458
                  },
459
                  {
460
                    "name": "Chad",
461
                    "code": "TD"
462
                  },
463
                  {
464
                    "name": "Chile",
465
                    "code": "CL"
466
                  },
467
                  {
468
                    "name": "China",
469
                    "code": "CN"
470
                  },
471
                  {
472
                    "name": "Christmas Island",
473
                    "code": "CX"
474
                  },
475
                  {
476
                    "name": "Cocos (Keeling) Islands",
477
                    "code": "CC"
478
                  },
479
                  {
480
                    "name": "Colombia",
481
                    "code": "CO"
482
                  },
483
                  {
484
                    "name": "Comoros",
485
                    "code": "KM"
486
                  },
487
                  {
488
                    "name": "Congo",
489
                    "code": "CG"
490
                  },
491
                  {
492
                    "name": "Congo, The Democratic Republic of the",
493
                    "code": "CD"
494
                  },
495
                  {
496
                    "name": "Cook Islands",
497
                    "code": "CK"
498
                  },
499
                  {
500
                    "name": "Costa Rica",
501
                    "code": "CR"
502
                  },
503
                  {
504
                    "name": "Cote D\"Ivoire",
505
                    "code": "CI"
506
                  },
507
                  {
508
                    "name": "Croatia",
509
                    "code": "HR"
510
                  },
511
                  {
512
                    "name": "Cuba",
513
                    "code": "CU"
514
                  },
515
                  {
516
                    "name": "Cyprus",
517
                    "code": "CY"
518
                  },
519
                  {
520
                    "name": "Czech Republic",
521
                    "code": "CZ"
522
                  },
523
                  {
524
                    "name": "Denmark",
525
                    "code": "DK"
526
                  },
527
                  {
528
                    "name": "Djibouti",
529
                    "code": "DJ"
530
                  },
531
                  {
532
                    "name": "Dominica",
533
                    "code": "DM"
534
                  },
535
                  {
536
                    "name": "Dominican Republic",
537
                    "code": "DO"
538
                  },
539
                  {
540
                    "name": "Ecuador",
541
                    "code": "EC"
542
                  },
543
                  {
544
                    "name": "Egypt",
545
                    "code": "EG"
546
                  },
547
                  {
548
                    "name": "El Salvador",
549
                    "code": "SV"
550
                  },
551
                  {
552
                    "name": "Equatorial Guinea",
553
                    "code": "GQ"
554
                  },
555
                  {
556
                    "name": "Eritrea",
557
                    "code": "ER"
558
                  },
559
                  {
560
                    "name": "Estonia",
561
                    "code": "EE"
562
                  },
563
                  {
564
                    "name": "Ethiopia",
565
                    "code": "ET"
566
                  },
567
                  {
568
                    "name": "Falkland Islands (Malvinas)",
569
                    "code": "FK"
570
                  },
571
                  {
572
                    "name": "Faroe Islands",
573
                    "code": "FO"
574
                  },
575
                  {
576
                    "name": "Fiji",
577
                    "code": "FJ"
578
                  },
579
                  {
580
                    "name": "Finland",
581
                    "code": "FI"
582
                  },
583
                  {
584
                    "name": "France",
585
                    "code": "FR"
586
                  },
587
                  {
588
                    "name": "French Guiana",
589
                    "code": "GF"
590
                  },
591
                  {
592
                    "name": "French Polynesia",
593
                    "code": "PF"
594
                  },
595
                  {
596
                    "name": "French Southern Territories",
597
                    "code": "TF"
598
                  },
599
                  {
600
                    "name": "Gabon",
601
                    "code": "GA"
602
                  },
603
                  {
604
                    "name": "Gambia",
605
                    "code": "GM"
606
                  },
607
                  {
608
                    "name": "Georgia",
609
                    "code": "GE"
610
                  },
611
                  {
612
                    "name": "Germany",
613
                    "code": "DE"
614
                  },
615
                  {
616
                    "name": "Ghana",
617
                    "code": "GH"
618
                  },
619
                  {
620
                    "name": "Gibraltar",
621
                    "code": "GI"
622
                  },
623
                  {
624
                    "name": "Greece",
625
                    "code": "GR"
626
                  },
627
                  {
628
                    "name": "Greenland",
629
                    "code": "GL"
630
                  },
631
                  {
632
                    "name": "Grenada",
633
                    "code": "GD"
634
                  },
635
                  {
636
                    "name": "Guadeloupe",
637
                    "code": "GP"
638
                  },
639
                  {
640
                    "name": "Guam",
641
                    "code": "GU"
642
                  },
643
                  {
644
                    "name": "Guatemala",
645
                    "code": "GT"
646
                  },
647
                  {
648
                    "name": "Guernsey",
649
                    "code": "GG"
650
                  },
651
                  {
652
                    "name": "Guinea",
653
                    "code": "GN"
654
                  },
655
                  {
656
                    "name": "Guinea-Bissau",
657
                    "code": "GW"
658
                  },
659
                  {
660
                    "name": "Guyana",
661
                    "code": "GY"
662
                  },
663
                  {
664
                    "name": "Haiti",
665
                    "code": "HT"
666
                  },
667
                  {
668
                    "name": "Heard Island and Mcdonald Islands",
669
                    "code": "HM"
670
                  },
671
                  {
672
                    "name": "Holy See (Vatican City State)",
673
                    "code": "VA"
674
                  },
675
                  {
676
                    "name": "Honduras",
677
                    "code": "HN"
678
                  },
679
                  {
680
                    "name": "Hong Kong",
681
                    "code": "HK"
682
                  },
683
                  {
684
                    "name": "Hungary",
685
                    "code": "HU"
686
                  },
687
                  {
688
                    "name": "Iceland",
689
                    "code": "IS"
690
                  },
691
                  {
692
                    "name": "India",
693
                    "code": "IN"
694
                  },
695
                  {
696
                    "name": "Indonesia",
697
                    "code": "ID"
698
                  },
699
                  {
700
                    "name": "Iran, Islamic Republic Of",
701
                    "code": "IR"
702
                  },
703
                  {
704
                    "name": "Iraq",
705
                    "code": "IQ"
706
                  },
707
                  {
708
                    "name": "Ireland",
709
                    "code": "IE"
710
                  },
711
                  {
712
                    "name": "Isle of Man",
713
                    "code": "IM"
714
                  },
715
                  {
716
                    "name": "Israel",
717
                    "code": "IL"
718
                  },
719
                  {
720
                    "name": "Italy",
721
                    "code": "IT"
722
                  },
723
                  {
724
                    "name": "Jamaica",
725
                    "code": "JM"
726
                  },
727
                  {
728
                    "name": "Japan",
729
                    "code": "JP"
730
                  },
731
                  {
732
                    "name": "Jersey",
733
                    "code": "JE"
734
                  },
735
                  {
736
                    "name": "Jordan",
737
                    "code": "JO"
738
                  },
739
                  {
740
                    "name": "Kazakhstan",
741
                    "code": "KZ"
742
                  },
743
                  {
744
                    "name": "Kenya",
745
                    "code": "KE"
746
                  },
747
                  {
748
                    "name": "Kiribati",
749
                    "code": "KI"
750
                  },
751
                  {
752
                    "name": "Korea, Democratic People\"S Republic of",
753
                    "code": "KP"
754
                  },
755
                  {
756
                    "name": "Korea, Republic of",
757
                    "code": "KR"
758
                  },
759
                  {
760
                    "name": "Kuwait",
761
                    "code": "KW"
762
                  },
763
                  {
764
                    "name": "Kyrgyzstan",
765
                    "code": "KG"
766
                  },
767
                  {
768
                    "name": "Lao People\"S Democratic Republic",
769
                    "code": "LA"
770
                  },
771
                  {
772
                    "name": "Latvia",
773
                    "code": "LV"
774
                  },
775
                  {
776
                    "name": "Lebanon",
777
                    "code": "LB"
778
                  },
779
                  {
780
                    "name": "Lesotho",
781
                    "code": "LS"
782
                  },
783
                  {
784
                    "name": "Liberia",
785
                    "code": "LR"
786
                  },
787
                  {
788
                    "name": "Libyan Arab Jamahiriya",
789
                    "code": "LY"
790
                  },
791
                  {
792
                    "name": "Liechtenstein",
793
                    "code": "LI"
794
                  },
795
                  {
796
                    "name": "Lithuania",
797
                    "code": "LT"
798
                  },
799
                  {
800
                    "name": "Luxembourg",
801
                    "code": "LU"
802
                  },
803
                  {
804
                    "name": "Macao",
805
                    "code": "MO"
806
                  },
807
                  {
808
                    "name": "Macedonia, The Former Yugoslav Republic of",
809
                    "code": "MK"
810
                  },
811
                  {
812
                    "name": "Madagascar",
813
                    "code": "MG"
814
                  },
815
                  {
816
                    "name": "Malawi",
817
                    "code": "MW"
818
                  },
819
                  {
820
                    "name": "Malaysia",
821
                    "code": "MY"
822
                  },
823
                  {
824
                    "name": "Maldives",
825
                    "code": "MV"
826
                  },
827
                  {
828
                    "name": "Mali",
829
                    "code": "ML"
830
                  },
831
                  {
832
                    "name": "Malta",
833
                    "code": "MT"
834
                  },
835
                  {
836
                    "name": "Marshall Islands",
837
                    "code": "MH"
838
                  },
839
                  {
840
                    "name": "Martinique",
841
                    "code": "MQ"
842
                  },
843
                  {
844
                    "name": "Mauritania",
845
                    "code": "MR"
846
                  },
847
                  {
848
                    "name": "Mauritius",
849
                    "code": "MU"
850
                  },
851
                  {
852
                    "name": "Mayotte",
853
                    "code": "YT"
854
                  },
855
                  {
856
                    "name": "Mexico",
857
                    "code": "MX"
858
                  },
859
                  {
860
                    "name": "Micronesia, Federated States of",
861
                    "code": "FM"
862
                  },
863
                  {
864
                    "name": "Moldova, Republic of",
865
                    "code": "MD"
866
                  },
867
                  {
868
                    "name": "Monaco",
869
                    "code": "MC"
870
                  },
871
                  {
872
                    "name": "Mongolia",
873
                    "code": "MN"
874
                  },
875
                  {
876
                    "name": "Montserrat",
877
                    "code": "MS"
878
                  },
879
                  {
880
                    "name": "Morocco",
881
                    "code": "MA"
882
                  },
883
                  {
884
                    "name": "Mozambique",
885
                    "code": "MZ"
886
                  },
887
                  {
888
                    "name": "Myanmar",
889
                    "code": "MM"
890
                  },
891
                  {
892
                    "name": "Namibia",
893
                    "code": "NA"
894
                  },
895
                  {
896
                    "name": "Nauru",
897
                    "code": "NR"
898
                  },
899
                  {
900
                    "name": "Nepal",
901
                    "code": "NP"
902
                  },
903
                  {
904
                    "name": "Netherlands",
905
                    "code": "NL"
906
                  },
907
                  {
908
                    "name": "Netherlands Antilles",
909
                    "code": "AN"
910
                  },
911
                  {
912
                    "name": "New Caledonia",
913
                    "code": "NC"
914
                  },
915
                  {
916
                    "name": "New Zealand",
917
                    "code": "NZ"
918
                  },
919
                  {
920
                    "name": "Nicaragua",
921
                    "code": "NI"
922
                  },
923
                  {
924
                    "name": "Niger",
925
                    "code": "NE"
926
                  },
927
                  {
928
                    "name": "Nigeria",
929
                    "code": "NG"
930
                  },
931
                  {
932
                    "name": "Niue",
933
                    "code": "NU"
934
                  },
935
                  {
936
                    "name": "Norfolk Island",
937
                    "code": "NF"
938
                  },
939
                  {
940
                    "name": "Northern Mariana Islands",
941
                    "code": "MP"
942
                  },
943
                  {
944
                    "name": "Norway",
945
                    "code": "NO"
946
                  },
947
                  {
948
                    "name": "Oman",
949
                    "code": "OM"
950
                  },
951
                  {
952
                    "name": "Pakistan",
953
                    "code": "PK"
954
                  },
955
                  {
956
                    "name": "Palau",
957
                    "code": "PW"
958
                  },
959
                  {
960
                    "name": "Palestinian Territory, Occupied",
961
                    "code": "PS"
962
                  },
963
                  {
964
                    "name": "Panama",
965
                    "code": "PA"
966
                  },
967
                  {
968
                    "name": "Papua New Guinea",
969
                    "code": "PG"
970
                  },
971
                  {
972
                    "name": "Paraguay",
973
                    "code": "PY"
974
                  },
975
                  {
976
                    "name": "Peru",
977
                    "code": "PE"
978
                  },
979
                  {
980
                    "name": "Philippines",
981
                    "code": "PH"
982
                  },
983
                  {
984
                    "name": "Pitcairn",
985
                    "code": "PN"
986
                  },
987
                  {
988
                    "name": "Poland",
989
                    "code": "PL"
990
                  },
991
                  {
992
                    "name": "Portugal",
993
                    "code": "PT"
994
                  },
995
                  {
996
                    "name": "Puerto Rico",
997
                    "code": "PR"
998
                  },
999
                  {
1000
                    "name": "Qatar",
1001
                    "code": "QA"
1002
                  },
1003
                  {
1004
                    "name": "Reunion",
1005
                    "code": "RE"
1006
                  },
1007
                  {
1008
                    "name": "Romania",
1009
                    "code": "RO"
1010
                  },
1011
                  {
1012
                    "name": "Russian Federation",
1013
                    "code": "RU"
1014
                  },
1015
                  {
1016
                    "name": "RWANDA",
1017
                    "code": "RW"
1018
                  },
1019
                  {
1020
                    "name": "Saint Helena",
1021
                    "code": "SH"
1022
                  },
1023
                  {
1024
                    "name": "Saint Kitts and Nevis",
1025
                    "code": "KN"
1026
                  },
1027
                  {
1028
                    "name": "Saint Lucia",
1029
                    "code": "LC"
1030
                  },
1031
                  {
1032
                    "name": "Saint Pierre and Miquelon",
1033
                    "code": "PM"
1034
                  },
1035
                  {
1036
                    "name": "Saint Vincent and the Grenadines",
1037
                    "code": "VC"
1038
                  },
1039
                  {
1040
                    "name": "Samoa",
1041
                    "code": "WS"
1042
                  },
1043
                  {
1044
                    "name": "San Marino",
1045
                    "code": "SM"
1046
                  },
1047
                  {
1048
                    "name": "Sao Tome and Principe",
1049
                    "code": "ST"
1050
                  },
1051
                  {
1052
                    "name": "Saudi Arabia",
1053
                    "code": "SA"
1054
                  },
1055
                  {
1056
                    "name": "Senegal",
1057
                    "code": "SN"
1058
                  },
1059
                  {
1060
                    "name": "Serbia and Montenegro",
1061
                    "code": "CS"
1062
                  },
1063
                  {
1064
                    "name": "Seychelles",
1065
                    "code": "SC"
1066
                  },
1067
                  {
1068
                    "name": "Sierra Leone",
1069
                    "code": "SL"
1070
                  },
1071
                  {
1072
                    "name": "Singapore",
1073
                    "code": "SG"
1074
                  },
1075
                  {
1076
                    "name": "Slovakia",
1077
                    "code": "SK"
1078
                  },
1079
                  {
1080
                    "name": "Slovenia",
1081
                    "code": "SI"
1082
                  },
1083
                  {
1084
                    "name": "Solomon Islands",
1085
                    "code": "SB"
1086
                  },
1087
                  {
1088
                    "name": "Somalia",
1089
                    "code": "SO"
1090
                  },
1091
                  {
1092
                    "name": "South Africa",
1093
                    "code": "ZA"
1094
                  },
1095
                  {
1096
                    "name": "South Georgia and the South Sandwich Islands",
1097
                    "code": "GS"
1098
                  },
1099
                  {
1100
                    "name": "Spain",
1101
                    "code": "ES"
1102
                  },
1103
                  {
1104
                    "name": "Sri Lanka",
1105
                    "code": "LK"
1106
                  },
1107
                  {
1108
                    "name": "Sudan",
1109
                    "code": "SD"
1110
                  },
1111
                  {
1112
                    "name": "Suriname",
1113
                    "code": "SR"
1114
                  },
1115
                  {
1116
                    "name": "Svalbard and Jan Mayen",
1117
                    "code": "SJ"
1118
                  },
1119
                  {
1120
                    "name": "Swaziland",
1121
                    "code": "SZ"
1122
                  },
1123
                  {
1124
                    "name": "Sweden",
1125
                    "code": "SE"
1126
                  },
1127
                  {
1128
                    "name": "Switzerland",
1129
                    "code": "CH"
1130
                  },
1131
                  {
1132
                    "name": "Syrian Arab Republic",
1133
                    "code": "SY"
1134
                  },
1135
                  {
1136
                    "name": "Taiwan",
1137
                    "code": "TW"
1138
                  },
1139
                  {
1140
                    "name": "Tajikistan",
1141
                    "code": "TJ"
1142
                  },
1143
                  {
1144
                    "name": "Tanzania, United Republic of",
1145
                    "code": "TZ"
1146
                  },
1147
                  {
1148
                    "name": "Thailand",
1149
                    "code": "TH"
1150
                  },
1151
                  {
1152
                    "name": "Timor-Leste",
1153
                    "code": "TL"
1154
                  },
1155
                  {
1156
                    "name": "Togo",
1157
                    "code": "TG"
1158
                  },
1159
                  {
1160
                    "name": "Tokelau",
1161
                    "code": "TK"
1162
                  },
1163
                  {
1164
                    "name": "Tonga",
1165
                    "code": "TO"
1166
                  },
1167
                  {
1168
                    "name": "Trinidad and Tobago",
1169
                    "code": "TT"
1170
                  },
1171
                  {
1172
                    "name": "Tunisia",
1173
                    "code": "TN"
1174
                  },
1175
                  {
1176
                    "name": "Turkey",
1177
                    "code": "TR"
1178
                  },
1179
                  {
1180
                    "name": "Turkmenistan",
1181
                    "code": "TM"
1182
                  },
1183
                  {
1184
                    "name": "Turks and Caicos Islands",
1185
                    "code": "TC"
1186
                  },
1187
                  {
1188
                    "name": "Tuvalu",
1189
                    "code": "TV"
1190
                  },
1191
                  {
1192
                    "name": "Uganda",
1193
                    "code": "UG"
1194
                  },
1195
                  {
1196
                    "name": "Ukraine",
1197
                    "code": "UA"
1198
                  },
1199
                  {
1200
                    "name": "United Arab Emirates",
1201
                    "code": "AE"
1202
                  },
1203
                  {
1204
                    "name": "United Kingdom",
1205
                    "code": "GB"
1206
                  },
1207
                  {
1208
                    "name": "United States",
1209
                    "code": "US"
1210
                  },
1211
                  {
1212
                    "name": "United States Minor Outlying Islands",
1213
                    "code": "UM"
1214
                  },
1215
                  {
1216
                    "name": "Uruguay",
1217
                    "code": "UY"
1218
                  },
1219
                  {
1220
                    "name": "Uzbekistan",
1221
                    "code": "UZ"
1222
                  },
1223
                  {
1224
                    "name": "Vanuatu",
1225
                    "code": "VU"
1226
                  },
1227
                  {
1228
                    "name": "Venezuela",
1229
                    "code": "VE"
1230
                  },
1231
                  {
1232
                    "name": "Viet Nam",
1233
                    "code": "VN"
1234
                  },
1235
                  {
1236
                    "name": "Virgin Islands, British",
1237
                    "code": "VG"
1238
                  },
1239
                  {
1240
                    "name": "Virgin Islands, U.S.",
1241
                    "code": "VI"
1242
                  },
1243
                  {
1244
                    "name": "Wallis and Futuna",
1245
                    "code": "WF"
1246
                  },
1247
                  {
1248
                    "name": "Western Sahara",
1249
                    "code": "EH"
1250
                  },
1251
                  {
1252
                    "name": "Yemen",
1253
                    "code": "YE"
1254
                  },
1255
                  {
1256
                    "name": "Zambia",
1257
                    "code": "ZM"
1258
                  },
1259
                  {
1260
                    "name": "Zimbabwe",
1261
                    "code": "ZW"
1262
                  }
1263
                ]
1264
              },
1265
              "valueProperty": "name",
1266
              "template": "<span>{{ item.name }}</span>",
1267
              "calculateValue": "var customer = _.find($ds.Customers, {'Customer ID': data.Customer['Customer ID']});\nvalue = customer ? instance.setValue(customer['Country']): '';",
1268
              "allowCalculateOverride": true,
1269
              "validate": {
1270
                "required": true
1271
              },
1272
              "validateWhenHidden": false,
1273
              "key": "ShipCountry",
1274
              "type": "select",
1275
              "input": true
1276
            }
1277
          ]
1278
        },
1279
        {
1280
          "label": "<strong>Orders</strong>",
1281
          "tableView": false,
1282
          "templates": {
1283
            "footer": "<div class=\"row\">\n  <div class=\"col-sm-6\"></div>\n  <div class=\"col-sm-2\"><strong>TOTAL</strong></div>\n  <div class=\"col-sm-2\"><strong>{{ _.sumBy(data.Orders, 'Discount') }}</strong></div>\n  <div class=\"col-sm-2\"><strong>{{ _.sumBy(data.Orders, 'Subtotal') }}</strong></div>\n</div>"
1284
          },
1285
          "validateWhenHidden": false,
1286
          "rowDrafts": false,
1287
          "key": "Orders",
1288
          "type": "editgrid",
1289
          "displayAsTable": false,
1290
          "input": true,
1291
          "components": [
1292
            {
1293
              "label": "Product",
1294
              "widget": "choicesjs",
1295
              "tableView": true,
1296
              "dataSrc": "custom",
1297
              "data": {
1298
                "custom": "values = $ds.Products;"
1299
              },
1300
              "idPath": "ProductID",
1301
              "template": "<span>{{ item.ProductName }}</span>",
1302
              "validate": {
1303
                "required": true
1304
              },
1305
              "validateWhenHidden": false,
1306
              "key": "Product",
1307
              "type": "select",
1308
              "input": true
1309
            },
1310
            {
1311
              "label": "Supplier",
1312
              "applyMaskOn": "change",
1313
              "disabled": true,
1314
              "tableView": true,
1315
              "calculateValue": "var product = _.find($ds.Products, {ProductID: row.Product.ProductID});\nvalue = product ? product.Supplier : '';",
1316
              "validateWhenHidden": false,
1317
              "key": "Supplier",
1318
              "type": "textfield",
1319
              "input": true
1320
            },
1321
            {
1322
              "label": "Category",
1323
              "applyMaskOn": "change",
1324
              "disabled": true,
1325
              "tableView": false,
1326
              "calculateValue": "var product = _.find($ds.Products, {ProductID: row.Product.ProductID});\nvalue = product ? product.Category : '';\n",
1327
              "validateWhenHidden": false,
1328
              "key": "Category",
1329
              "type": "textfield",
1330
              "input": true
1331
            },
1332
            {
1333
              "label": "Unit Price ($)",
1334
              "applyMaskOn": "change",
1335
              "disabled": true,
1336
              "tableView": true,
1337
              "calculateValue": "var product = _.find($ds.Products, {ProductID: row.Product.ProductID});\nvalue = product ? product.UnitPrice : '';",
1338
              "validateWhenHidden": false,
1339
              "key": "UnitPrice",
1340
              "type": "textfield",
1341
              "input": true
1342
            },
1343
            {
1344
              "label": "Quantity",
1345
              "applyMaskOn": "change",
1346
              "mask": false,
1347
              "tableView": true,
1348
              "delimiter": false,
1349
              "requireDecimal": false,
1350
              "inputFormat": "plain",
1351
              "truncateMultipleSpaces": false,
1352
              "validate": {
1353
                "required": true
1354
              },
1355
              "validateWhenHidden": false,
1356
              "key": "Quantity",
1357
              "type": "number",
1358
              "input": true
1359
            },
1360
            {
1361
              "label": "Discount ($)",
1362
              "applyMaskOn": "change",
1363
              "mask": false,
1364
              "tableView": true,
1365
              "defaultValue": 0,
1366
              "delimiter": false,
1367
              "requireDecimal": false,
1368
              "inputFormat": "plain",
1369
              "truncateMultipleSpaces": false,
1370
              "validateWhenHidden": false,
1371
              "key": "Discount",
1372
              "type": "number",
1373
              "input": true
1374
            },
1375
            {
1376
              "label": "Subtotal ($)",
1377
              "applyMaskOn": "change",
1378
              "mask": false,
1379
              "disabled": true,
1380
              "tableView": true,
1381
              "delimiter": false,
1382
              "requireDecimal": false,
1383
              "inputFormat": "plain",
1384
              "truncateMultipleSpaces": false,
1385
              "calculateValue": "value = (row.UnitPrice - row.Discount) * row.Quantity",
1386
              "validateWhenHidden": false,
1387
              "key": "Subtotal",
1388
              "type": "number",
1389
              "input": true
1390
            }
1391
          ]
1392
        }
1393
      ]
1394
    },
1395
    {
1396
      "label": "Save",
1397
      "showValidations": false,
1398
      "disableOnInvalid": true,
1399
      "tableView": false,
1400
      "key": "submit",
1401
      "type": "button",
1402
      "saveOnEnter": false,
1403
      "input": true
1404
    }
1405
  ]
1406
}
1407
```
1408
1409
## HTML Template
1410
1411
``` html
1412
<!DOCTYPE html>
1413
<html lang="en">
1414
1415
<head>
1416
    <meta name="viewport"
1417
        content="user-scalable=no, initial-scale=1.0001, maximum-scale=1.0001, width=device-width, minimal-ui shrink-to-fit=no">
1418
    <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0" />
1419
    <title>Northwind</title>
1420
1421
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
1422
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
1423
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css">
1424
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/awesome-notifications/3.1.0/style.min.css">
1425
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/w2ui@2.0.0/w2ui-2.0.min.css">
1426
    <script src="https://cdnjs.cloudflare.com/ajax/libs/awesome-notifications/3.1.0/modern.var.min.js"></script>
1427
    <script src="https://cdn.jsdelivr.net/npm/localstorage-slim"></script>
1428
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
1429
    <script src="https://cdn.form.io/js/formio.embed.js"></script>
1430
</head>
1431
1432
<body>
1433
    <style>
1434
        button[type="submit"] {
1435
            background-color: #007bff;
1436
            color: white;
1437
            padding: 10px 15px;
1438
            border: none;
1439
            border-radius: 4px;
1440
            cursor: pointer;
1441
            font-size: 1em;
1442
            width: 100%;
1443
        }
1444
    </style>
1445
    <div id="toolbar"></div>
1446
    <div id="formio"></div>
1447
    <script type="module">
1448
        import { w2toolbar } from "https://cdn.jsdelivr.net/npm/w2ui@2.0.0/w2ui-2.0.es6.min.js";
1449
        var $ds = {};
1450
        var email = ls.get("email");
1451
        var notifier = new AWN({ option: "top-right" });
1452
        var sid = ls.get("sid");
1453
1454
        $(function () {
1455
            if (sid) {
1456
                google.script.run
1457
                    .withSuccessHandler(onCheckSidSuccess)
1458
                    .withFailureHandler(onInvalidSid)
1459
                    .checkSid(sid);
1460
            }
1461
            else {
1462
                onInvalidSid();
1463
            }
1464
        });
1465
1466
        function onInvalidSid(err) {
1467
            if (err) {
1468
                notifier.warning(err.message);
1469
            }
1470
            else {
1471
                let url = "<?!= base ?>?url=/form/login&callback=<?!= url ?>";
1472
                notifier.modal(`<b>You need to login to access this page.</b><br><a href="${url}">👉 <b>Login</b></a>`)
1473
            }
1474
        }
1475
1476
        function onCheckSidSuccess(valid) {
1477
            new w2toolbar({
1478
                box: "#toolbar",
1479
                name: "toolbar",
1480
                items: [
1481
                    {
1482
                        type: "menu", id: "sales", icon: "w2ui-icon-info", text: "Sales",
1483
                        items: [
1484
                            { id: "customers", text: "Customers", icon: "fa fa-user" },
1485
                            { id: "orders", text: "Orders", icon: "fa fa-file-text" },
1486
                        ]
1487
                    },
1488
                    { type: "break" },
1489
                    {
1490
                        type: "menu", id: "operations", icon: "w2ui-icon-settings", text: "Operations",
1491
                        items: [
1492
                            { id: "employees", text: "Employees", icon: "fa fa-address-card" },
1493
                            { id: "products", text: "Products", icon: "fa fa-archive" },
1494
                            { id: "categories", text: "Product Categories", icon: "fa fa-sitemap" },
1495
                            { id: "suppliers", text: "Suppliers", icon: "fa fa-user" },
1496
                            { id: "shippers", text: "Shippers", icon: "fa fa-shopping-cart" }
1497
                        ]
1498
                    },
1499
                    { type: "spacer" },
1500
                    {
1501
                        type: "menu", id: "auth", text: email,
1502
                        items: [
1503
                            { text: "Logout", id: "logout", icon: "fa fa-sign-out" }
1504
                        ]
1505
                    }
1506
                ],
1507
                onClick(event) {
1508
                    switch (event.target) {
1509
                        case "sales:customers": {
1510
                            window.open("<?= base ?>?url=/form/customers", "_top");
1511
                            break;
1512
                        }
1513
                        case "sales:orders": {
1514
                            window.open("<?= base ?>?url=/form/orders", "_top");
1515
                            break;
1516
                        }
1517
                        case "operations:employees": {
1518
                            window.open("<?= base ?>?url=/form/employees", "_top");
1519
                            break;
1520
                        }
1521
                        case "operations:products": {
1522
                            window.open("<?= base ?>?url=/form/products", "_top");
1523
                            break;
1524
                        }
1525
                        case "operations:categories": {
1526
                            window.open("<?= base ?>?url=/form/categories", "_top");
1527
                            break;
1528
                        }
1529
                        case "operations:suppliers": {
1530
                            window.open("<?= base ?>?url=/form/suppliers", "_top");
1531
                            break;
1532
                        }
1533
                        case "operations:shippers": {
1534
                            window.open("<?= base ?>?url=/form/shippers", "_top");
1535
                            break;
1536
                        }
1537
                        case "auth:logout": {
1538
                            ls.remove("sid");
1539
                            ls.remove("email");
1540
                            window.open("<?= base ?>?url=/form/login&callback=<?!= url ?>", "_top");
1541
                            break;
1542
                        }
1543
                    }
1544
                }
1545
            });
1546
1547
            if (valid) {
1548
                google.script.run.withSuccessHandler(showForms)
1549
                    .loadDatasources("<?!= datasource ?>");
1550
            } else {
1551
                onInvalidSid();
1552
            }
1553
        }
1554
1555
        function showForms(ds) {
1556
            $ds = ds;
1557
            let rec = <?!= (data) ?>;
1558
            Formio.createForm(document.getElementById("formio"), <?!= template ?>)
1559
                .then(function (form) {
1560
                    // Prevent the submission from going to the form.io server.
1561
                    form.noAlerts = true;
1562
                    form.nosubmit = true;
1563
                    form.submission = rec.Document ? { data: JSON.parse(rec.Document) } : {};
1564
1565
                    // Triggered when they click the submit button.
1566
                    form.on("submit", function (submission) {
1567
                        google.script.run
1568
                            .withFailureHandler((err) => {
1569
                                notifier.warning("Successfully saved.");
1570
                                form.emit("submitError");
1571
                            })
1572
                            .withSuccessHandler((id) => {
1573
                                if (!id) {
1574
                                    form.emit("submitError");
1575
                                    notifier.warning("An error occurred while saving the entry.");
1576
                                }
1577
                                else {
1578
                                    form.emit("submitDone");
1579
                                    <? if (!id) { ?>
1580
                                    form.submission = {};
1581
                                    form.refresh();
1582
                                    <? } ?>
1583
                                    notifier.success("Successfully saved.");
1584
                                }
1585
                            })
1586
                            <? if (id) { ?>
1587
                        .update("<?!= id ?>", "<?!= form.Id ?>", { Document: JSON.stringify(submission.data) }, null, sid)
1588
                                <? } else { ?>
1589
                        .create("<?!= form.Id ?>", { Document: JSON.stringify(submission.data) }, sid);
1590
                        <? } ?>
1591
                    });
1592
                });
1593
        }
1594
    </script>
1595
</body>
1596
1597
</html>
1598
```
1599
1600
## Giải thích Code
1601
1602
Form List Customers sử dụng cácthư viện sau:
1603
- [jQuery](https://jquery.com/) để khởi tạo grid sau khi page load xong.
1604
- [localstorage-slim](https://github.com/digitalfortress-tech/localstorage-slim) để đọc sid & email để bỏ qua bước login nếu trước đó đã login thành công. Trường hợp login không thành công thì sẽ thông báo và điều hướng đến trang login.
1605
- [Notyf](https://carlosroso.com/notyf/) hiển thị thông báo.
1606
- [w2ui](https://w2ui.com/web/home) cung cấp các thành phần UI cơ bản và nâng cao, ví dụ: toolbar và grid, dialog...
1607
- Các thư viện đều được load từ CDN để tối ưu tốc độ, đơn giản hóa HTML Templated. Các bạn cần có kiến thức trong phần yêu cầu để hiểu logic của code JS + templated nhúng trong page. 
1608
- Trong page này gọi đến
1609
  - Hàm backend **checkSid** sid lưu trữ có khớp với giá trị trên server, nếu đúng thì sẽ sử dụng email trả về, còn sai sẽ điều hướng đến trang login
1610
  - Hàm backend **loadDatasources** để tải các dữ liệu tham chiếu phía client, ví dụ danh sách Products
1611
  - Hàm backend **create** để lưu form submission dưới dạng document trong database NoSQL
1612
  - Hàm backend **update** để cập nhật record đã tồn tại với record id được truyền vào từ url theo định dạng **form-id/record-id**. Trong trường hợp này record-id chính là column Id của document.
1613
- Form CRUD Order tham chiếu đến các datasource **Customers**, **Employees**, **Shippers**, **Products** Các datasource sẽ phục làm data các thành phần FormIO UI.
1614
1615
## Demo
1616
- Link: https://script.google.com/macros/s/AKfycbwIjB-hULVZdfCtsXFPg4Af_8WoKx2AFf85KMVwnsO_WkeAXW3zarT6vZNFVfwccz1_sA/exec?url=/form/crud-order
1617
- Account: user@northwind.com
1618
- Password: user