@memberjunction/ng-base-forms 1.8.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,10 @@ export declare class MJFormField extends BaseRecordComponent implements AfterVie
33
33
  * valid when the FieldName field is a foreign key field to another entity.
34
34
  */
35
35
  LinkType: 'Email' | 'URL' | 'Record' | 'None';
36
+ /**
37
+ * Optional, when the field type is a linked field, you can specify if a search box is shown or a dropdown component is shown. The default is 'search'.
38
+ */
39
+ LinkComponentType: 'Search' | 'Dropdown';
36
40
  /**
37
41
  * If set to false, the label for the field will not be shown. The default is true.
38
42
  */
@@ -65,6 +69,6 @@ export declare class MJFormField extends BaseRecordComponent implements AfterVie
65
69
  constructor(renderer: Renderer2, cdr: ChangeDetectorRef);
66
70
  ngAfterViewInit(): void;
67
71
  static ɵfac: i0.ɵɵFactoryDeclaration<MJFormField, never>;
68
- static ɵcmp: i0.ɵɵComponentDeclaration<MJFormField, "mj-form-field", never, { "record": { "alias": "record"; "required": false; }; "EditMode": { "alias": "EditMode"; "required": false; }; "FieldName": { "alias": "FieldName"; "required": false; }; "Type": { "alias": "Type"; "required": false; }; "LinkType": { "alias": "LinkType"; "required": false; }; "ShowLabel": { "alias": "ShowLabel"; "required": false; }; "DisplayName": { "alias": "DisplayName"; "required": false; }; "PossibleValues": { "alias": "PossibleValues"; "required": false; }; "Value": { "alias": "Value"; "required": false; }; }, { "ValueChange": "ValueChange"; }, never, never, false, never>;
72
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJFormField, "mj-form-field", never, { "record": { "alias": "record"; "required": false; }; "EditMode": { "alias": "EditMode"; "required": false; }; "FieldName": { "alias": "FieldName"; "required": false; }; "Type": { "alias": "Type"; "required": false; }; "LinkType": { "alias": "LinkType"; "required": false; }; "LinkComponentType": { "alias": "LinkComponentType"; "required": false; }; "ShowLabel": { "alias": "ShowLabel"; "required": false; }; "DisplayName": { "alias": "DisplayName"; "required": false; }; "PossibleValues": { "alias": "PossibleValues"; "required": false; }; "Value": { "alias": "Value"; "required": false; }; }, { "ValueChange": "ValueChange"; }, never, never, false, never>;
69
73
  }
70
74
  //# sourceMappingURL=base-field-component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-field-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-field-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAyB,aAAa,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3I,OAAO,EAAE,UAAU,EAAE,eAAe,EAAqB,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;;AAGjD;;;;;GAKG;AACH,qBAKa,WAAY,SAAQ,mBAAoB,YAAW,aAAa;IA8GzE,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,GAAG;IA9Gb;;OAEG;IACM,MAAM,EAAG,UAAU,CAAC;IAC7B;;OAEG;IACM,QAAQ,EAAE,OAAO,CAAS;IACnC;;OAEG;IACM,SAAS,EAAE,MAAM,CAAM;IAChC;;OAEG;IACM,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,gBAAgB,GAAG,YAAY,GAAG,UAAU,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,CAAa;IACxI;;;OAGG;IACM,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAU;IAEhE;;OAEG;IACM,SAAS,EAAE,OAAO,CAAQ;IAEnC,SAAS,uDAAa;IAEtB,OAAO,CAAC,YAAY,CAAuB;IAC3C;;OAEG;IACH,IACW,WAAW,IAAI,MAAM,CAO/B;IAED,IAAW,WAAW,CAAC,QAAQ,EAAE,MAAM,EAEtC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,OAAO,CAAC,eAAe,CAAyB;IAChD;;;OAGG;IACH,IAAa,cAAc,IAAI,MAAM,EAAE,CAOtC;IAED,IAAI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAEpC;IAED,IACW,KAAK,IAAI,GAAG,CAUtB;IACD,IAAW,KAAK,CAAC,QAAQ,EAAE,GAAG,EAM7B;IACS,WAAW,oBAA2B;IAEhD;;OAEG;IACH,IAAW,eAAe,IAAI,OAAO,CAIpC;IAED,IAAW,SAAS,IAAI,eAAe,CAItC;IAEyC,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAClF,OAAO,CAAC,QAAQ,CAA+B;gBAGrC,QAAQ,EAAE,SAAS,EACnB,GAAG,EAAE,iBAAiB;IAKhC,eAAe,IAAI,IAAI;yCApHZ,WAAW;2CAAX,WAAW;CA2LvB"}
1
+ {"version":3,"file":"base-field-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-field-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAyB,aAAa,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3I,OAAO,EAAE,UAAU,EAAE,eAAe,EAAqB,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;;AAGjD;;;;;GAKG;AACH,qBAKa,WAAY,SAAQ,mBAAoB,YAAW,aAAa;IAmHzE,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,GAAG;IAnHb;;OAEG;IACM,MAAM,EAAG,UAAU,CAAC;IAC7B;;OAEG;IACM,QAAQ,EAAE,OAAO,CAAS;IACnC;;OAEG;IACM,SAAS,EAAE,MAAM,CAAM;IAChC;;OAEG;IACM,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,gBAAgB,GAAG,YAAY,GAAG,UAAU,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,CAAa;IACxI;;;OAGG;IACM,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAU;IAEhE;;OAEG;IACM,iBAAiB,EAAE,QAAQ,GAAG,UAAU,CAAc;IAE/D;;OAEG;IACM,SAAS,EAAE,OAAO,CAAQ;IAEnC,SAAS,uDAAa;IAEtB,OAAO,CAAC,YAAY,CAAuB;IAC3C;;OAEG;IACH,IACW,WAAW,IAAI,MAAM,CAO/B;IAED,IAAW,WAAW,CAAC,QAAQ,EAAE,MAAM,EAEtC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,OAAO,CAAC,eAAe,CAAyB;IAChD;;;OAGG;IACH,IAAa,cAAc,IAAI,MAAM,EAAE,CAOtC;IAED,IAAI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAEpC;IAED,IACW,KAAK,IAAI,GAAG,CAUtB;IACD,IAAW,KAAK,CAAC,QAAQ,EAAE,GAAG,EAM7B;IACS,WAAW,oBAA2B;IAEhD;;OAEG;IACH,IAAW,eAAe,IAAI,OAAO,CAIpC;IAED,IAAW,SAAS,IAAI,eAAe,CAItC;IAEyC,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAClF,OAAO,CAAC,QAAQ,CAA+B;gBAGrC,QAAQ,EAAE,SAAS,EACnB,GAAG,EAAE,iBAAiB;IAKhC,eAAe,IAAI,IAAI;yCAzHZ,WAAW;2CAAX,WAAW;CAgMvB"}
@@ -105,7 +105,7 @@ function MJFormField_Conditional_0_div_1_Conditional_2_Template(rf, ctx) { if (r
105
105
  i0.ɵɵelement(0, "mj-link-field", 9);
106
106
  } if (rf & 2) {
107
107
  const ctx_r0 = i0.ɵɵnextContext(3);
108
- i0.ɵɵproperty("record", ctx_r0.record)("FieldName", ctx_r0.FieldName);
108
+ i0.ɵɵproperty("record", ctx_r0.record)("FieldName", ctx_r0.FieldName)("LinkComponentType", ctx_r0.LinkComponentType);
109
109
  } }
110
110
  function MJFormField_Conditional_0_div_1_Conditional_3_Case_0_Template(rf, ctx) { if (rf & 1) {
111
111
  const _r2 = i0.ɵɵgetCurrentView();
@@ -192,7 +192,7 @@ function MJFormField_Conditional_0_div_1_Conditional_3_Template(rf, ctx) { if (r
192
192
  } }
193
193
  function MJFormField_Conditional_0_div_1_Template(rf, ctx) { if (rf & 1) {
194
194
  i0.ɵɵelementStart(0, "div", 2);
195
- i0.ɵɵtemplate(1, MJFormField_Conditional_0_div_1_Conditional_1_Template, 2, 1, "label")(2, MJFormField_Conditional_0_div_1_Conditional_2_Template, 1, 2, "mj-link-field", 9)(3, MJFormField_Conditional_0_div_1_Conditional_3_Template, 8, 1);
195
+ i0.ɵɵtemplate(1, MJFormField_Conditional_0_div_1_Conditional_1_Template, 2, 1, "label")(2, MJFormField_Conditional_0_div_1_Conditional_2_Template, 1, 3, "mj-link-field", 9)(3, MJFormField_Conditional_0_div_1_Conditional_3_Template, 8, 1);
196
196
  i0.ɵɵelementEnd();
197
197
  } if (rf & 2) {
198
198
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -313,6 +313,10 @@ export class MJFormField extends BaseRecordComponent {
313
313
  * valid when the FieldName field is a foreign key field to another entity.
314
314
  */
315
315
  this.LinkType = 'None';
316
+ /**
317
+ * Optional, when the field type is a linked field, you can specify if a search box is shown or a dropdown component is shown. The default is 'search'.
318
+ */
319
+ this.LinkComponentType = 'Dropdown';
316
320
  /**
317
321
  * If set to false, the label for the field will not be shown. The default is true.
318
322
  */
@@ -353,14 +357,14 @@ MJFormField.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJFormField, se
353
357
  } if (rf & 2) {
354
358
  let _t;
355
359
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.markdown = _t.first);
356
- } }, inputs: { record: "record", EditMode: "EditMode", FieldName: "FieldName", Type: "Type", LinkType: "LinkType", ShowLabel: "ShowLabel", DisplayName: "DisplayName", PossibleValues: "PossibleValues", Value: "Value" }, outputs: { ValueChange: "ValueChange" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["markdown", ""], ["class", "record-form-row", 4, "ngIf"], [1, "record-form-row"], [4, "ngIf"], ["mjEmailLink", "", 3, "field"], ["mjWebLink", "", 3, "field"], ["mjFieldLink", "", 3, "record", "fieldName"], [3, "value", "readonly", "languages", "language"], [3, "data"], [3, "record", "FieldName"], [3, "ngModel", "languages", "language"], [3, "ngModel"], ["mjFillContainer", "", 3, "ngModel", "fillWidth", "fillHeight"], [3, "value"], ["type", "checkbox", "kendoCheckBox", "", 3, "ngModel"], [3, "data", "ngModel"], [3, "data", "allowCustom", "ngModel"], [3, "ngModelChange", "ngModel", "languages", "language"], [3, "ngModelChange", "ngModel"], ["mjFillContainer", "", 3, "ngModelChange", "ngModel", "fillWidth", "fillHeight"], [3, "valueChange", "value"], ["type", "checkbox", "kendoCheckBox", "", 3, "ngModelChange", "ngModel"], [3, "ngModelChange", "data", "ngModel"], [3, "ngModelChange", "data", "allowCustom", "ngModel"]], template: function MJFormField_Template(rf, ctx) { if (rf & 1) {
360
+ } }, inputs: { record: "record", EditMode: "EditMode", FieldName: "FieldName", Type: "Type", LinkType: "LinkType", LinkComponentType: "LinkComponentType", ShowLabel: "ShowLabel", DisplayName: "DisplayName", PossibleValues: "PossibleValues", Value: "Value" }, outputs: { ValueChange: "ValueChange" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["markdown", ""], ["class", "record-form-row", 4, "ngIf"], [1, "record-form-row"], [4, "ngIf"], ["mjEmailLink", "", 3, "field"], ["mjWebLink", "", 3, "field"], ["mjFieldLink", "", 3, "record", "fieldName"], [3, "value", "readonly", "languages", "language"], [3, "data"], [3, "record", "FieldName", "LinkComponentType"], [3, "ngModel", "languages", "language"], [3, "ngModel"], ["mjFillContainer", "", 3, "ngModel", "fillWidth", "fillHeight"], [3, "value"], ["type", "checkbox", "kendoCheckBox", "", 3, "ngModel"], [3, "data", "ngModel"], [3, "data", "allowCustom", "ngModel"], [3, "ngModelChange", "ngModel", "languages", "language"], [3, "ngModelChange", "ngModel"], ["mjFillContainer", "", 3, "ngModelChange", "ngModel", "fillWidth", "fillHeight"], [3, "valueChange", "value"], ["type", "checkbox", "kendoCheckBox", "", 3, "ngModelChange", "ngModel"], [3, "ngModelChange", "data", "ngModel"], [3, "ngModelChange", "data", "allowCustom", "ngModel"]], template: function MJFormField_Template(rf, ctx) { if (rf & 1) {
357
361
  i0.ɵɵtemplate(0, MJFormField_Conditional_0_Template, 2, 2);
358
362
  } if (rf & 2) {
359
363
  i0.ɵɵconditional(ctx.record.IsSaved || !ctx.IsFieldReadOnly ? 0 : -1);
360
364
  } }, dependencies: [i1.NgIf, i2.CheckboxControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.TextAreaComponent, i3.TextBoxComponent, i3.NumericTextBoxComponent, i3.CheckBoxDirective, i4.DatePickerComponent, i5.ComboBoxComponent, i5.DropDownListComponent, i6.EmailLink, i6.FieldLink, i6.WebLink, i7.FillContainer, i8.CodeEditorComponent, i9.MarkdownComponent, i10.MJLinkField], styles: [".record-form-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: auto 1fr;\n align-items: start;\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n align-items: start; \n\n} \n \n \n.record-form-row[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n font-weight: bold;\n padding-right: 10px;\n width: 240px;\n margin-bottom: 0;\n align-self: start; \n\n}\n\n\n\n\n\n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > p[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h1[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h2[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h3[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h4[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h5[_ngcontent-%COMP%], \n.record-form-row[_ngcontent-%COMP%] markdown[_ngcontent-%COMP%] > h6[_ngcontent-%COMP%] {\n margin:0 !important;\n}\n\nmarkdown[_ngcontent-%COMP%] {\n font-size: 1rem; \n\n}\n\n\n@media (min-width: 768px) {\n .record-form-row[_ngcontent-%COMP%] {\n display: grid; \n\n grid-template-columns: auto 1fr;\n align-items: start; \n\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n }\n\n .record-form-row[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n width: 240px;\n margin-bottom: 0;\n align-self: start; \n\n }\n}"] });
361
365
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJFormField, [{
362
366
  type: Component,
363
- args: [{ selector: 'mj-form-field', template: "<!--\n Using a blend of *ngIf and the new @switch control flow because of a bug in early Angular 17 where\n the @if wrapping the @switch didn't fully remove elements when swtiching back and forth. In the future if Angular fixes the bug\n we can get rid of the *ngIf outer div blocks and instead use @if and contain the @switch within the same div block.\n-->\n@if (record.IsSaved || !IsFieldReadOnly) {\n <!-- We only render a field if the record is either saved, or the field is editable. Meaning, for NEW records, we only show editable fields -->\n <div class=\"record-form-row\" *ngIf=\"!EditMode || IsFieldReadOnly\">\n <label *ngIf=\"ShowLabel\">{{DisplayName}}</label>\n @switch (LinkType) {\n @case ('None') {\n @if (ExtendedType === 'Code') {\n <mj-code-editor [value]=\"record.Get(FieldName)\" [readonly]=\"true\" [languages]=\"languages\" [language]=\"'JavaScript'\"></mj-code-editor>\n }\n @else if (FieldInfo.Length === -1) {\n <!-- this is a varchar(max) type of field -->\n <markdown #markdown [data]=\"record.Get(FieldName)\"></markdown>\n <!-- <span [innerHTML]=\"record.Get(FieldName) | formatText\"></span> -->\n }\n @else {\n <span>{{FormatValue(FieldName, 0)}}</span>\n }\n }\n @case ('Email') {\n <span mjEmailLink [field]=\"record.GetFieldByName(FieldName)\" >{{FormatValue(FieldName, 0)}}</span>\n }\n @case ('URL') {\n <span mjWebLink [field]=\"record.GetFieldByName(FieldName)\" >{{FormatValue(FieldName, 0)}}</span>\n }\n @case ('Record') {\n <span mjFieldLink [record]=\"record\" [fieldName]=\"FieldName\" >{{FormatValue(FieldName, 0)}}</span>\n }\n }\n </div>\n <div class=\"record-form-row\" *ngIf=\"EditMode && !IsFieldReadOnly\">\n @if (ShowLabel) {\n <label>{{DisplayName}}</label>\n }\n @if (FieldInfo && FieldInfo.RelatedEntity && FieldInfo.RelatedEntity.length > 0) {\n <!-- Foreign Key Link here -->\n <mj-link-field [record]=\"record\" [FieldName]=\"FieldName\"></mj-link-field>\n }\n @else {\n <!-- Not a foreign key -->\n @switch (Type) {\n @case ('code') {\n <mj-code-editor [(ngModel)]=\"Value\" [languages]=\"languages\" [language]=\"'JavaScript'\"></mj-code-editor>\n }\n @case ('textbox') {\n <kendo-textbox [(ngModel)]=\"Value\" />\n }\n @case ('textarea') {\n <kendo-textarea [(ngModel)]=\"Value\" mjFillContainer [fillWidth]=\"ShowLabel ? false : true\" [fillHeight]=\"ShowLabel ? false : true\" ></kendo-textarea>\n }\n @case ('numerictextbox') {\n <kendo-numerictextbox [(value)]=\"Value\" />\n }\n @case ('datepicker') {\n <kendo-datepicker [(value)]=\"Value\"></kendo-datepicker>\n }\n @case ('checkbox') {\n <input type=\"checkbox\" [(ngModel)]=\"Value\" kendoCheckBox />\n }\n @case ('dropdownlist') {\n <kendo-dropdownlist [data]=\"PossibleValues\" [(ngModel)]=\"Value\"></kendo-dropdownlist>\n }\n @case ('combobox') {\n <kendo-combobox [data]=\"PossibleValues\" [allowCustom]=\"true\" [(ngModel)]=\"Value\"></kendo-combobox>\n }\n }\n }\n </div>\n}\n", styles: [".record-form-row {\n display: grid;\n grid-template-columns: auto 1fr;\n align-items: start;\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n align-items: start; /* Ensure items are aligned at the top */\n} \n \n \n.record-form-row label {\n font-weight: bold;\n padding-right: 10px;\n width: 240px;\n margin-bottom: 0;\n align-self: start; /* Align the label to the start (top) */\n}\n\n/*\n Eliminate the default margin and padding for the markdown elements\n */\n.record-form-row markdown > p,\n.record-form-row markdown > h1,\n.record-form-row markdown > h2,\n.record-form-row markdown > h3,\n.record-form-row markdown > h4,\n.record-form-row markdown > h5,\n.record-form-row markdown > h6 {\n margin:0 !important;\n}\n\nmarkdown {\n font-size: 1rem; /* Adjust this value to match your app's regular text size */\n}\n\n\n@media (min-width: 768px) {\n .record-form-row {\n display: grid; /* Ensure grid display is used for larger screens as well */\n grid-template-columns: auto 1fr;\n align-items: start; /* Keep the alignment at the top */\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n }\n\n .record-form-row label {\n width: 240px;\n margin-bottom: 0;\n align-self: start; /* Ensure the label is aligned to the top */\n }\n}\n "] }]
367
+ args: [{ selector: 'mj-form-field', template: "<!--\n Using a blend of *ngIf and the new @switch control flow because of a bug in early Angular 17 where\n the @if wrapping the @switch didn't fully remove elements when swtiching back and forth. In the future if Angular fixes the bug\n we can get rid of the *ngIf outer div blocks and instead use @if and contain the @switch within the same div block.\n-->\n@if (record.IsSaved || !IsFieldReadOnly) {\n <!-- We only render a field if the record is either saved, or the field is editable. Meaning, for NEW records, we only show editable fields -->\n <div class=\"record-form-row\" *ngIf=\"!EditMode || IsFieldReadOnly\">\n <label *ngIf=\"ShowLabel\">{{DisplayName}}</label>\n @switch (LinkType) {\n @case ('None') {\n @if (ExtendedType === 'Code') {\n <mj-code-editor [value]=\"record.Get(FieldName)\" [readonly]=\"true\" [languages]=\"languages\" [language]=\"'JavaScript'\"></mj-code-editor>\n }\n @else if (FieldInfo.Length === -1) {\n <!-- this is a varchar(max) type of field -->\n <markdown #markdown [data]=\"record.Get(FieldName)\"></markdown>\n <!-- <span [innerHTML]=\"record.Get(FieldName) | formatText\"></span> -->\n }\n @else {\n <span>{{FormatValue(FieldName, 0)}}</span>\n }\n }\n @case ('Email') {\n <span mjEmailLink [field]=\"record.GetFieldByName(FieldName)\" >{{FormatValue(FieldName, 0)}}</span>\n }\n @case ('URL') {\n <span mjWebLink [field]=\"record.GetFieldByName(FieldName)\" >{{FormatValue(FieldName, 0)}}</span>\n }\n @case ('Record') {\n <span mjFieldLink [record]=\"record\" [fieldName]=\"FieldName\" >{{FormatValue(FieldName, 0)}}</span>\n }\n }\n </div>\n <div class=\"record-form-row\" *ngIf=\"EditMode && !IsFieldReadOnly\">\n @if (ShowLabel) {\n <label>{{DisplayName}}</label>\n }\n @if (FieldInfo && FieldInfo.RelatedEntity && FieldInfo.RelatedEntity.length > 0) {\n <!-- Foreign Key Link here -->\n <mj-link-field [record]=\"record\" [FieldName]=\"FieldName\" [LinkComponentType]=\"LinkComponentType\"></mj-link-field>\n }\n @else {\n <!-- Not a foreign key -->\n @switch (Type) {\n @case ('code') {\n <mj-code-editor [(ngModel)]=\"Value\" [languages]=\"languages\" [language]=\"'JavaScript'\"></mj-code-editor>\n }\n @case ('textbox') {\n <kendo-textbox [(ngModel)]=\"Value\" />\n }\n @case ('textarea') {\n <kendo-textarea [(ngModel)]=\"Value\" mjFillContainer [fillWidth]=\"ShowLabel ? false : true\" [fillHeight]=\"ShowLabel ? false : true\" ></kendo-textarea>\n }\n @case ('numerictextbox') {\n <kendo-numerictextbox [(value)]=\"Value\" />\n }\n @case ('datepicker') {\n <kendo-datepicker [(value)]=\"Value\"></kendo-datepicker>\n }\n @case ('checkbox') {\n <input type=\"checkbox\" [(ngModel)]=\"Value\" kendoCheckBox />\n }\n @case ('dropdownlist') {\n <kendo-dropdownlist [data]=\"PossibleValues\" [(ngModel)]=\"Value\"></kendo-dropdownlist>\n }\n @case ('combobox') {\n <kendo-combobox [data]=\"PossibleValues\" [allowCustom]=\"true\" [(ngModel)]=\"Value\"></kendo-combobox>\n }\n }\n }\n </div>\n}\n", styles: [".record-form-row {\n display: grid;\n grid-template-columns: auto 1fr;\n align-items: start;\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n align-items: start; /* Ensure items are aligned at the top */\n} \n \n \n.record-form-row label {\n font-weight: bold;\n padding-right: 10px;\n width: 240px;\n margin-bottom: 0;\n align-self: start; /* Align the label to the start (top) */\n}\n\n/*\n Eliminate the default margin and padding for the markdown elements\n */\n.record-form-row markdown > p,\n.record-form-row markdown > h1,\n.record-form-row markdown > h2,\n.record-form-row markdown > h3,\n.record-form-row markdown > h4,\n.record-form-row markdown > h5,\n.record-form-row markdown > h6 {\n margin:0 !important;\n}\n\nmarkdown {\n font-size: 1rem; /* Adjust this value to match your app's regular text size */\n}\n\n\n@media (min-width: 768px) {\n .record-form-row {\n display: grid; /* Ensure grid display is used for larger screens as well */\n grid-template-columns: auto 1fr;\n align-items: start; /* Keep the alignment at the top */\n gap: 10px;\n margin-bottom: 12px;\n padding-top: 5px;\n padding-bottom: 5px;\n }\n\n .record-form-row label {\n width: 240px;\n margin-bottom: 0;\n align-self: start; /* Ensure the label is aligned to the top */\n }\n}\n "] }]
364
368
  }], () => [{ type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], { record: [{
365
369
  type: Input
366
370
  }], EditMode: [{
@@ -371,6 +375,8 @@ MJFormField.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJFormField, se
371
375
  type: Input
372
376
  }], LinkType: [{
373
377
  type: Input
378
+ }], LinkComponentType: [{
379
+ type: Input
374
380
  }], ShowLabel: [{
375
381
  type: Input
376
382
  }], DisplayName: [{
@@ -385,4 +391,4 @@ MJFormField.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJFormField, se
385
391
  type: ViewChild,
386
392
  args: ['markdown', { static: false }]
387
393
  }] }); })();
388
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJFormField, { className: "MJFormField", filePath: "src/lib/base-field-component.ts", lineNumber: 18 }); })();
394
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJFormField, { className: "MJFormField" }); })();
@@ -1,4 +1,4 @@
1
- import { AfterViewInit, ElementRef } from "@angular/core";
1
+ import { AfterViewInit, ElementRef, ChangeDetectorRef } from "@angular/core";
2
2
  import { BaseRecordComponent } from "./base-record-component";
3
3
  import { BaseEntity, EntityFieldInfo, EntityInfo } from "@memberjunction/core";
4
4
  import * as i0 from "@angular/core";
@@ -9,6 +9,8 @@ import * as i0 from "@angular/core";
9
9
  * components for field rendering/editing when needed.
10
10
  */
11
11
  export declare class MJLinkField extends BaseRecordComponent implements AfterViewInit {
12
+ private cdr;
13
+ constructor(cdr: ChangeDetectorRef);
12
14
  /**
13
15
  * The record object that contains the field to be rendered. This object should be an instance of BaseEntity or a derived class.
14
16
  */
@@ -21,11 +23,21 @@ export declare class MJLinkField extends BaseRecordComponent implements AfterVie
21
23
  * For users of this component, if you already know the name of the record we are linking to, provide it here to reduce DB overhead
22
24
  */
23
25
  RecordName?: string;
26
+ /**
27
+ * The type of component to show, search is a user input field that searches for records, dropdown is a dropdown list of records. The default is 'search'. If the related entity has
28
+ * a small list of possible records, it is often desirable to show a dropdown list instead of a search box.
29
+ */
30
+ LinkComponentType?: 'Search' | 'Dropdown';
24
31
  RelatedEntityInfo: EntityInfo | undefined;
32
+ private _SelectedRecord;
33
+ get SelectedRecord(): BaseEntity | undefined;
34
+ set SelectedRecord(val: BaseEntity | undefined);
25
35
  RecordLinked: boolean;
26
- matchingRecords: BaseEntity[];
36
+ RelatedEntityRecords: BaseEntity[];
27
37
  showMatchingRecords: boolean;
38
+ dropDownColumns: EntityFieldInfo[];
28
39
  inputBox: ElementRef;
40
+ private initComponent;
29
41
  ngAfterViewInit(): Promise<void>;
30
42
  protected AttemptToLinkValue(): Promise<void>;
31
43
  get Value(): any;
@@ -35,8 +47,10 @@ export declare class MJLinkField extends BaseRecordComponent implements AfterVie
35
47
  onClearClicked(): void;
36
48
  RelatedEntityNameField: string;
37
49
  protected fetchMatchingRecords(query: string): Promise<void>;
50
+ protected populateDropdownList(): Promise<void>;
51
+ protected SetDropdownValue(): Promise<void>;
38
52
  onRecordSelected(linkedRecord: BaseEntity): void;
39
53
  static ɵfac: i0.ɵɵFactoryDeclaration<MJLinkField, never>;
40
- static ɵcmp: i0.ɵɵComponentDeclaration<MJLinkField, "mj-link-field", never, { "record": { "alias": "record"; "required": false; }; "FieldName": { "alias": "FieldName"; "required": false; }; "RecordName": { "alias": "RecordName"; "required": false; }; }, {}, never, never, false, never>;
54
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJLinkField, "mj-link-field", never, { "record": { "alias": "record"; "required": false; }; "FieldName": { "alias": "FieldName"; "required": false; }; "RecordName": { "alias": "RecordName"; "required": false; }; "LinkComponentType": { "alias": "LinkComponentType"; "required": false; }; }, {}, never, never, false, never>;
41
55
  }
42
56
  //# sourceMappingURL=link-field.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"link-field.component.d.ts","sourceRoot":"","sources":["../../src/lib/link-field.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAa,UAAU,EAAoB,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAgB,eAAe,EAAE,UAAU,EAAqB,MAAM,sBAAsB,CAAC;;AAGhH;;;;;GAKG;AACH,qBAKa,WAAY,SAAQ,mBAAoB,YAAW,aAAa;IACzE;;OAEG;IACM,MAAM,EAAG,UAAU,CAAC;IAE7B;;OAEG;IACM,SAAS,EAAE,MAAM,CAAM;IAEhC;;OAEG;IACM,UAAU,CAAC,EAAE,MAAM,CAAM;IAE3B,iBAAiB,EAAE,UAAU,GAAG,SAAS,CAAa;IACtD,YAAY,EAAE,OAAO,CAAS;IAC9B,eAAe,EAAE,UAAU,EAAE,CAAM;IACnC,mBAAmB,EAAE,OAAO,CAAS;IAEH,QAAQ,EAAG,UAAU,CAAC;IAGzD,eAAe;cA4BL,kBAAkB;IAoBlC,IAAW,KAAK,IAAI,GAAG,CAEtB;IAED,IAAW,KAAK,CAAC,GAAG,EAAE,GAAG,EAGxB;IAED,IAAW,WAAW,IAAI,eAAe,GAAG,SAAS,CAEpD;IACD,IAAW,WAAW,IAAI,MAAM,CAM/B;IAGM,cAAc;IAOd,sBAAsB,EAAE,MAAM,CAAM;cAC3B,oBAAoB,CAAC,KAAK,EAAE,MAAM;IA0B3C,gBAAgB,CAAC,YAAY,EAAE,UAAU;yCA/HvC,WAAW;2CAAX,WAAW;CAqIvB"}
1
+ {"version":3,"file":"link-field.component.d.ts","sourceRoot":"","sources":["../../src/lib/link-field.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAa,UAAU,EAAoB,iBAAiB,EAA4B,MAAM,eAAe,CAAC;AACpI,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAgB,eAAe,EAAE,UAAU,EAAqB,MAAM,sBAAsB,CAAC;;AAGhH;;;;;GAKG;AACH,qBAKa,WAAY,SAAQ,mBAAoB,YAAW,aAAa;IAC7D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,iBAAiB;IAG1C;;OAEG;IACM,MAAM,EAAG,UAAU,CAAC;IAE7B;;OAEG;IACM,SAAS,EAAE,MAAM,CAAM;IAEhC;;OAEG;IACM,UAAU,CAAC,EAAE,MAAM,CAAM;IAElC;;;OAGG;IACM,iBAAiB,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEvD,iBAAiB,EAAE,UAAU,GAAG,SAAS,CAAa;IAE7D,OAAO,CAAC,eAAe,CAAqC;IAC5D,IAAW,cAAc,IAAI,UAAU,GAAG,SAAS,CAElD;IACD,IAAW,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,EAOpD;IACM,YAAY,EAAE,OAAO,CAAS;IAC9B,oBAAoB,EAAE,UAAU,EAAE,CAAM;IACxC,mBAAmB,EAAE,OAAO,CAAS;IACrC,eAAe,EAAE,eAAe,EAAE,CAAM;IAEL,QAAQ,EAAG,UAAU,CAAC;YAElD,aAAa;IA0CrB,eAAe;cAIL,kBAAkB;IAoBlC,IAAW,KAAK,IAAI,GAAG,CAEtB;IAED,IAAW,KAAK,CAAC,GAAG,EAAE,GAAG,EAQxB;IAED,IAAW,WAAW,IAAI,eAAe,GAAG,SAAS,CAEpD;IACD,IAAW,WAAW,IAAI,MAAM,CAM/B;IAGM,cAAc;IAOd,sBAAsB,EAAE,MAAM,CAAM;cAC3B,oBAAoB,CAAC,KAAK,EAAE,MAAM;cAoDlC,oBAAoB;cA4BpB,gBAAgB;IAIzB,gBAAgB,CAAC,YAAY,EAAE,UAAU;yCAtOvC,WAAW;2CAAX,WAAW;CAuPvB"}
@@ -16,27 +16,70 @@ import * as i1 from "@angular/common";
16
16
  import * as i2 from "@angular/forms";
17
17
  import * as i3 from "@progress/kendo-angular-buttons";
18
18
  import * as i4 from "@progress/kendo-angular-inputs";
19
+ import * as i5 from "@progress/kendo-angular-dropdowns";
19
20
  const _c0 = ["inputBox"];
20
- function MJLinkField_ul_5_li_1_Template(rf, ctx) { if (rf & 1) {
21
- const _r2 = i0.ɵɵgetCurrentView();
21
+ function MJLinkField_Conditional_1_ul_4_li_1_Template(rf, ctx) { if (rf & 1) {
22
+ const _r3 = i0.ɵɵgetCurrentView();
22
23
  i0.ɵɵelementStart(0, "li", 6);
23
- i0.ɵɵlistener("click", function MJLinkField_ul_5_li_1_Template_li_click_0_listener() { const record_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.onRecordSelected(record_r3)); });
24
+ i0.ɵɵlistener("click", function MJLinkField_Conditional_1_ul_4_li_1_Template_li_click_0_listener() { const record_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onRecordSelected(record_r4)); });
24
25
  i0.ɵɵtext(1);
25
26
  i0.ɵɵelementEnd();
26
27
  } if (rf & 2) {
27
- const record_r3 = ctx.$implicit;
28
- const ctx_r3 = i0.ɵɵnextContext(2);
28
+ const record_r4 = ctx.$implicit;
29
+ const ctx_r1 = i0.ɵɵnextContext(3);
29
30
  i0.ɵɵadvance();
30
- i0.ɵɵtextInterpolate1(" ", ctx_r3.RelatedEntityNameField ? record_r3.Get(ctx_r3.RelatedEntityNameField) : "No Value", " ");
31
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.RelatedEntityNameField ? record_r4.Get(ctx_r1.RelatedEntityNameField) : record_r4.PrimaryKey.ToString(), " ");
31
32
  } }
32
- function MJLinkField_ul_5_Template(rf, ctx) { if (rf & 1) {
33
+ function MJLinkField_Conditional_1_ul_4_Template(rf, ctx) { if (rf & 1) {
33
34
  i0.ɵɵelementStart(0, "ul", 4);
34
- i0.ɵɵtemplate(1, MJLinkField_ul_5_li_1_Template, 2, 1, "li", 5);
35
+ i0.ɵɵtemplate(1, MJLinkField_Conditional_1_ul_4_li_1_Template, 2, 1, "li", 5);
35
36
  i0.ɵɵelementEnd();
36
37
  } if (rf & 2) {
37
- const ctx_r3 = i0.ɵɵnextContext();
38
+ const ctx_r1 = i0.ɵɵnextContext(2);
38
39
  i0.ɵɵadvance();
39
- i0.ɵɵproperty("ngForOf", ctx_r3.matchingRecords);
40
+ i0.ɵɵproperty("ngForOf", ctx_r1.RelatedEntityRecords);
41
+ } }
42
+ function MJLinkField_Conditional_1_Template(rf, ctx) { if (rf & 1) {
43
+ const _r1 = i0.ɵɵgetCurrentView();
44
+ i0.ɵɵelementStart(0, "input", 1, 0);
45
+ i0.ɵɵtwoWayListener("ngModelChange", function MJLinkField_Conditional_1_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.RecordName, $event) || (ctx_r1.RecordName = $event); return i0.ɵɵresetView($event); });
46
+ i0.ɵɵelementEnd();
47
+ i0.ɵɵelementStart(2, "button", 2);
48
+ i0.ɵɵlistener("click", function MJLinkField_Conditional_1_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onClearClicked()); });
49
+ i0.ɵɵtext(3, "Clear");
50
+ i0.ɵɵelementEnd();
51
+ i0.ɵɵtemplate(4, MJLinkField_Conditional_1_ul_4_Template, 2, 1, "ul", 3);
52
+ } if (rf & 2) {
53
+ const ctx_r1 = i0.ɵɵnextContext();
54
+ i0.ɵɵproperty("ngClass", ctx_r1.RecordLinked ? "record-linked" : "");
55
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.RecordName);
56
+ i0.ɵɵadvance(4);
57
+ i0.ɵɵproperty("ngIf", ctx_r1.showMatchingRecords);
58
+ } }
59
+ function MJLinkField_Conditional_2_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
60
+ i0.ɵɵelement(0, "kendo-combobox-column", 9);
61
+ } if (rf & 2) {
62
+ const d_r6 = ctx.$implicit;
63
+ i0.ɵɵproperty("field", d_r6.Name)("title", d_r6.DisplayNameOrName);
64
+ } }
65
+ function MJLinkField_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
66
+ const _r5 = i0.ɵɵgetCurrentView();
67
+ i0.ɵɵelementStart(0, "kendo-multicolumncombobox", 8);
68
+ i0.ɵɵtwoWayListener("valueChange", function MJLinkField_Conditional_2_Conditional_0_Template_kendo_multicolumncombobox_valueChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r1.SelectedRecord, $event) || (ctx_r1.SelectedRecord = $event); return i0.ɵɵresetView($event); });
69
+ i0.ɵɵrepeaterCreate(1, MJLinkField_Conditional_2_Conditional_0_For_2_Template, 1, 2, "kendo-combobox-column", 9, i0.ɵɵrepeaterTrackByIdentity);
70
+ i0.ɵɵelementEnd();
71
+ } if (rf & 2) {
72
+ const ctx_r1 = i0.ɵɵnextContext(2);
73
+ i0.ɵɵproperty("data", ctx_r1.RelatedEntityRecords)("textField", ctx_r1.RelatedEntityInfo.NameField ? ctx_r1.RelatedEntityInfo.NameField.Name : ctx_r1.RelatedEntityInfo.FirstPrimaryKey.Name)("valueField", ctx_r1.RelatedEntityInfo.FirstPrimaryKey.Name);
74
+ i0.ɵɵtwoWayProperty("value", ctx_r1.SelectedRecord);
75
+ i0.ɵɵadvance();
76
+ i0.ɵɵrepeater(ctx_r1.dropDownColumns);
77
+ } }
78
+ function MJLinkField_Conditional_2_Template(rf, ctx) { if (rf & 1) {
79
+ i0.ɵɵtemplate(0, MJLinkField_Conditional_2_Conditional_0_Template, 3, 4, "kendo-multicolumncombobox", 7);
80
+ } if (rf & 2) {
81
+ const ctx_r1 = i0.ɵɵnextContext();
82
+ i0.ɵɵconditional(ctx_r1.RelatedEntityInfo ? 0 : -1);
40
83
  } }
41
84
  /**
42
85
  * This component is used to automatically generate a UI for any field in a given BaseEntity object. The CodeGen tool will generate forms and form sections that
@@ -45,8 +88,9 @@ function MJLinkField_ul_5_Template(rf, ctx) { if (rf & 1) {
45
88
  * components for field rendering/editing when needed.
46
89
  */
47
90
  export class MJLinkField extends BaseRecordComponent {
48
- constructor() {
49
- super(...arguments);
91
+ constructor(cdr) {
92
+ super();
93
+ this.cdr = cdr;
50
94
  /**
51
95
  * The name of the field in the entity to be rendered.
52
96
  */
@@ -55,37 +99,74 @@ export class MJLinkField extends BaseRecordComponent {
55
99
  * For users of this component, if you already know the name of the record we are linking to, provide it here to reduce DB overhead
56
100
  */
57
101
  this.RecordName = '';
102
+ /**
103
+ * The type of component to show, search is a user input field that searches for records, dropdown is a dropdown list of records. The default is 'search'. If the related entity has
104
+ * a small list of possible records, it is often desirable to show a dropdown list instead of a search box.
105
+ */
106
+ this.LinkComponentType = 'Search';
58
107
  this.RelatedEntityInfo = undefined;
108
+ this._SelectedRecord = undefined;
59
109
  this.RecordLinked = false;
60
- this.matchingRecords = [];
110
+ this.RelatedEntityRecords = [];
61
111
  this.showMatchingRecords = false;
112
+ this.dropDownColumns = [];
62
113
  this.RelatedEntityNameField = '';
63
114
  }
64
- ngAfterViewInit() {
115
+ get SelectedRecord() {
116
+ return this._SelectedRecord;
117
+ }
118
+ set SelectedRecord(val) {
119
+ this._SelectedRecord = val;
120
+ if (val) {
121
+ this.Value = val.FirstPrimaryKey.Value;
122
+ }
123
+ else
124
+ this.Value = null;
125
+ }
126
+ initComponent() {
65
127
  return __awaiter(this, void 0, void 0, function* () {
128
+ var _a;
66
129
  if (!this.record)
67
130
  throw new Error('record property is required');
68
131
  if (!this.FieldName)
69
132
  throw new Error('FieldName property is required');
70
- yield this.AttemptToLinkValue();
71
- fromEvent(this.inputBox.nativeElement, 'input')
72
- .pipe(debounceTime(300))
73
- .subscribe(() => {
74
- if (!this.RecordLinked) {
75
- this.showMatchingRecords = true;
76
- this.fetchMatchingRecords(this.inputBox.nativeElement.value);
77
- }
78
- });
79
- fromEvent(this.inputBox.nativeElement, 'keydown').subscribe((event) => {
80
- if (this.RecordLinked && !['Backspace', 'Delete'].includes(event.key)) {
81
- event.preventDefault();
82
- }
83
- else if (['Backspace', 'Delete'].includes(event.key)) {
84
- this.RecordName = '';
85
- this.Value = null;
86
- this.RecordLinked = false;
133
+ const relatedEntityID = (_a = this.EntityField) === null || _a === void 0 ? void 0 : _a.RelatedEntityID;
134
+ const md = new Metadata();
135
+ if (relatedEntityID) {
136
+ this.RelatedEntityInfo = md.EntityByID(relatedEntityID);
137
+ this.cdr.detectChanges();
138
+ }
139
+ if (this.LinkComponentType === 'Search') {
140
+ yield this.AttemptToLinkValue();
141
+ if (this.inputBox) {
142
+ fromEvent(this.inputBox.nativeElement, 'input')
143
+ .pipe(debounceTime(300))
144
+ .subscribe(() => {
145
+ if (!this.RecordLinked) {
146
+ this.showMatchingRecords = true;
147
+ this.fetchMatchingRecords(this.inputBox.nativeElement.value);
148
+ }
149
+ });
150
+ fromEvent(this.inputBox.nativeElement, 'keydown').subscribe((event) => {
151
+ if (this.RecordLinked && !['Backspace', 'Delete'].includes(event.key)) {
152
+ event.preventDefault();
153
+ }
154
+ else if (['Backspace', 'Delete'].includes(event.key)) {
155
+ this.RecordName = '';
156
+ this.Value = null;
157
+ this.RecordLinked = false;
158
+ }
159
+ });
87
160
  }
88
- });
161
+ }
162
+ else {
163
+ yield this.populateDropdownList();
164
+ }
165
+ });
166
+ }
167
+ ngAfterViewInit() {
168
+ return __awaiter(this, void 0, void 0, function* () {
169
+ yield this.initComponent();
89
170
  });
90
171
  }
91
172
  AttemptToLinkValue() {
@@ -114,7 +195,12 @@ export class MJLinkField extends BaseRecordComponent {
114
195
  }
115
196
  set Value(val) {
116
197
  this.record.Set(this.FieldName, val);
117
- this.AttemptToLinkValue();
198
+ if (this.LinkComponentType === 'Search') {
199
+ this.AttemptToLinkValue();
200
+ }
201
+ else {
202
+ this.SetDropdownValue();
203
+ }
118
204
  }
119
205
  get EntityField() {
120
206
  var _a;
@@ -135,17 +221,38 @@ export class MJLinkField extends BaseRecordComponent {
135
221
  }
136
222
  fetchMatchingRecords(query) {
137
223
  return __awaiter(this, void 0, void 0, function* () {
138
- var _a, _b;
139
- const relatedEntityID = (_a = this.EntityField) === null || _a === void 0 ? void 0 : _a.RelatedEntityID;
140
- const md = new Metadata();
224
+ var _a;
141
225
  this.RecordLinked = false;
142
- this.matchingRecords = [];
143
- if (relatedEntityID) {
144
- this.RelatedEntityInfo = md.EntityByID(relatedEntityID);
145
- // do a lookup using runView for records that have the Name field that start with the query
226
+ this.RelatedEntityRecords = [];
227
+ if (this.RelatedEntityInfo) {
146
228
  this.RelatedEntityNameField = this.RelatedEntityInfo.NameField ? this.RelatedEntityInfo.NameField.Name : '';
229
+ const escapedQuery = query.replace(/'/g, "''");
230
+ let filter = '';
231
+ if (!this.RelatedEntityInfo.FirstPrimaryKey.NeedsQuotes) {
232
+ // the pkey is a number, so let's see if the query is a number
233
+ if (isNaN(Number(query)) && this.RelatedEntityNameField === '') {
234
+ // if the query is not a number (for number pkeys) and we don't have a name field to search on, then we can't search
235
+ return;
236
+ }
237
+ else {
238
+ // if we get here it means we either have a RelatedEntityNameField to search on or the query is a number
239
+ if (!isNaN(Number(query))) {
240
+ // if the query is a number, then we can search on the pkey
241
+ filter = `[${this.RelatedEntityInfo.FirstPrimaryKey.Name}] = ${query}`;
242
+ }
243
+ }
244
+ }
245
+ else if (this.RelatedEntityInfo.FirstPrimaryKey.Type.trim().toLowerCase() !== 'uniqueidentifier') {
246
+ // the pkey is a string that is NOT a unique identifier, so we can search on it no matter what the query is, a number, not a number, whatever
247
+ filter = `[${this.RelatedEntityInfo.FirstPrimaryKey.Name}] = '${escapedQuery}'`;
248
+ }
249
+ else {
250
+ // we can't search on the pkey so leave it alone
251
+ }
147
252
  if (this.RelatedEntityNameField) {
148
- const filter = `[${this.RelatedEntityNameField}] LIKE '${query}%'`;
253
+ filter = `[${this.RelatedEntityNameField}] LIKE '${escapedQuery}%'${filter.length > 0 ? ' OR ' + filter : ''}`;
254
+ }
255
+ if (filter && filter.length > 0) {
149
256
  const rv = new RunView();
150
257
  const result = yield rv.RunView({
151
258
  EntityName: this.RelatedEntityInfo.Name,
@@ -154,55 +261,90 @@ export class MJLinkField extends BaseRecordComponent {
154
261
  ResultType: 'entity_object',
155
262
  MaxRows: 5
156
263
  });
157
- if (result && ((_b = result.Results) === null || _b === void 0 ? void 0 : _b.length) > 0) {
158
- this.matchingRecords = result.Results.filter(record => record[this.RelatedEntityNameField].toLowerCase().includes(query.toLowerCase()));
264
+ if (result && ((_a = result.Results) === null || _a === void 0 ? void 0 : _a.length) > 0) {
265
+ this.RelatedEntityRecords = result.Results;
159
266
  }
160
267
  }
268
+ else {
269
+ this.RelatedEntityRecords = [{ Name: "Can't search on " + this.RelatedEntityInfo.Name + ' records' }]; // this will have the effect of a single record in the list that says "Can't search on..."
270
+ }
271
+ }
272
+ });
273
+ }
274
+ populateDropdownList() {
275
+ return __awaiter(this, void 0, void 0, function* () {
276
+ if (this.RelatedEntityInfo) {
277
+ this.dropDownColumns = this.RelatedEntityInfo.Fields.filter(f => f.DefaultInView || f.IsPrimaryKey || f.IsNameField);
278
+ this.cdr.detectChanges();
279
+ // run a view to get the records
280
+ const rv = new RunView();
281
+ const result = yield rv.RunView({
282
+ EntityName: this.RelatedEntityInfo.Name,
283
+ ExtraFilter: '',
284
+ OrderBy: this.RelatedEntityNameField,
285
+ ResultType: 'entity_object',
286
+ });
287
+ if (result && result.Success) {
288
+ this.RelatedEntityRecords = result.Results;
289
+ // look for a match between the records we have, finding a match between our this.Value and the pkey of the record
290
+ const match = this.RelatedEntityRecords.find(r => r.FirstPrimaryKey.Value === this.Value);
291
+ if (match) {
292
+ this.SelectedRecord = match;
293
+ this.SetDropdownValue();
294
+ }
295
+ this.cdr.detectChanges();
296
+ }
161
297
  }
162
298
  });
163
299
  }
300
+ SetDropdownValue() {
301
+ return __awaiter(this, void 0, void 0, function* () {
302
+ });
303
+ }
164
304
  onRecordSelected(linkedRecord) {
165
- this.RecordName = linkedRecord.Get(this.RelatedEntityNameField);
166
- this.RecordLinked = true;
167
- this.showMatchingRecords = false;
168
- this.record.Set(this.FieldName, linkedRecord.FirstPrimaryKey.Value);
305
+ if (linkedRecord.EntityInfo) {
306
+ this.SelectedRecord = linkedRecord;
307
+ this.RecordName = this.RelatedEntityNameField ? linkedRecord.Get(this.RelatedEntityNameField) : linkedRecord.PrimaryKey.ToString();
308
+ this.RecordLinked = true;
309
+ this.showMatchingRecords = false;
310
+ this.record.Set(this.FieldName, linkedRecord.FirstPrimaryKey.Value);
311
+ }
312
+ else {
313
+ // this means that we really don't have a selected record and we were just showing the "Can't search on..." record
314
+ this.showMatchingRecords = false;
315
+ this.RecordLinked = false;
316
+ this.RecordName = '';
317
+ }
318
+ this.cdr.detectChanges();
169
319
  }
170
320
  }
171
- MJLinkField.ɵfac = /*@__PURE__*/ (() => { let ɵMJLinkField_BaseFactory; return function MJLinkField_Factory(t) { return (ɵMJLinkField_BaseFactory || (ɵMJLinkField_BaseFactory = i0.ɵɵgetInheritedFactory(MJLinkField)))(t || MJLinkField); }; })();
321
+ MJLinkField.ɵfac = function MJLinkField_Factory(t) { return new (t || MJLinkField)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
172
322
  MJLinkField.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJLinkField, selectors: [["mj-link-field"]], viewQuery: function MJLinkField_Query(rf, ctx) { if (rf & 1) {
173
- i0.ɵɵviewQuery(_c0, 7);
323
+ i0.ɵɵviewQuery(_c0, 5);
174
324
  } if (rf & 2) {
175
325
  let _t;
176
326
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputBox = _t.first);
177
- } }, inputs: { record: "record", FieldName: "FieldName", RecordName: "RecordName" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 3, consts: [["inputBox", ""], ["type", "text", "kendoTextBox", "", 1, "input-box", 3, "ngModelChange", "ngClass", "ngModel"], ["kendoButton", "", 3, "click"], ["class", "matching-records", 4, "ngIf"], [1, "matching-records"], [3, "click", 4, "ngFor", "ngForOf"], [3, "click"]], template: function MJLinkField_Template(rf, ctx) { if (rf & 1) {
178
- const _r1 = i0.ɵɵgetCurrentView();
179
- i0.ɵɵelementStart(0, "div")(1, "input", 1, 0);
180
- i0.ɵɵtwoWayListener("ngModelChange", function MJLinkField_Template_input_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r1); i0.ɵɵtwoWayBindingSet(ctx.RecordName, $event) || (ctx.RecordName = $event); return i0.ɵɵresetView($event); });
181
- i0.ɵɵelementEnd();
182
- i0.ɵɵelementStart(3, "button", 2);
183
- i0.ɵɵlistener("click", function MJLinkField_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onClearClicked()); });
184
- i0.ɵɵtext(4, "Clear");
185
- i0.ɵɵelementEnd();
186
- i0.ɵɵtemplate(5, MJLinkField_ul_5_Template, 2, 1, "ul", 3);
327
+ } }, inputs: { record: "record", FieldName: "FieldName", RecordName: "RecordName", LinkComponentType: "LinkComponentType" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [["inputBox", ""], ["type", "text", "kendoTextBox", "", 1, "input-box", 3, "ngModelChange", "ngClass", "ngModel"], ["kendoButton", "", 3, "click"], ["class", "matching-records", 4, "ngIf"], [1, "matching-records"], [3, "click", 4, "ngFor", "ngForOf"], [3, "click"], [3, "data", "textField", "valueField", "value"], [3, "valueChange", "data", "textField", "valueField", "value"], [3, "field", "title"]], template: function MJLinkField_Template(rf, ctx) { if (rf & 1) {
328
+ i0.ɵɵelementStart(0, "div");
329
+ i0.ɵɵtemplate(1, MJLinkField_Conditional_1_Template, 5, 3)(2, MJLinkField_Conditional_2_Template, 1, 1);
187
330
  i0.ɵɵelementEnd();
188
331
  } if (rf & 2) {
189
332
  i0.ɵɵadvance();
190
- i0.ɵɵproperty("ngClass", ctx.RecordLinked ? "record-linked" : "");
191
- i0.ɵɵtwoWayProperty("ngModel", ctx.RecordName);
192
- i0.ɵɵadvance(4);
193
- i0.ɵɵproperty("ngIf", ctx.showMatchingRecords);
194
- } }, dependencies: [i1.NgClass, i1.NgForOf, i1.NgIf, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, i3.ButtonComponent, i4.TextBoxDirective], styles: [".record-linked[_ngcontent-%COMP%] {\n color: darkblue;\n text-decoration: underline;\n}\n\n.input-box[_ngcontent-%COMP%] {\n width: 400px;\n}\n\nbutton[_ngcontent-%COMP%] {\n width: 50px;\n}\n\n\n.matching-records[_ngcontent-%COMP%] {\n width: 400px;\n list-style-type: none;\n margin: 0;\n padding: 0;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n}\n\n.matching-records[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n padding: 8px;\n cursor: pointer;\n}\n\n.matching-records[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:hover {\n background-color: #f0f0f0;\n}"] });
333
+ i0.ɵɵconditional(ctx.LinkComponentType === "Search" ? 1 : 2);
334
+ } }, dependencies: [i1.NgClass, i1.NgForOf, i1.NgIf, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, i3.ButtonComponent, i4.TextBoxDirective, i5.MultiColumnComboBoxComponent, i5.ComboBoxColumnComponent], styles: [".record-linked[_ngcontent-%COMP%] {\n color: darkblue;\n text-decoration: underline;\n}\n\n.input-box[_ngcontent-%COMP%] {\n width: 400px;\n}\n\nbutton[_ngcontent-%COMP%] {\n width: 50px;\n}\n\n\n.matching-records[_ngcontent-%COMP%] {\n width: 400px;\n list-style-type: none;\n margin: 0;\n padding: 0;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n}\n\n.matching-records[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n padding: 8px;\n cursor: pointer;\n}\n\n.matching-records[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:hover {\n background-color: #f0f0f0;\n}"] });
195
335
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJLinkField, [{
196
336
  type: Component,
197
- args: [{ selector: 'mj-link-field', template: "<!-- <input type=\"text\" class=\"input-box\" [ngClass]=\"RecordLinked ? 'record-linked' : ''\" [(ngModel)]=\"RecordName\" kendoTextBox /><button kendoButton (click)=\"onClearClicked()\">Clear</button>\n -->\n\n<div>\n <input \n #inputBox\n type=\"text\" \n class=\"input-box\" \n [ngClass]=\"RecordLinked ? 'record-linked' : ''\" \n [(ngModel)]=\"RecordName\" \n kendoTextBox \n />\n <button kendoButton (click)=\"onClearClicked()\">Clear</button>\n\n <ul *ngIf=\"showMatchingRecords\" class=\"matching-records\">\n <li *ngFor=\"let record of matchingRecords\" (click)=\"onRecordSelected(record)\">\n {{ RelatedEntityNameField ? record.Get(RelatedEntityNameField) : 'No Value' }}\n </li>\n </ul>\n</div>\n", styles: [".record-linked {\n color: darkblue;\n text-decoration: underline;\n}\n\n.input-box {\n width: 400px;\n}\n\nbutton {\n width: 50px;\n}\n\n\n.matching-records {\n width: 400px;\n list-style-type: none;\n margin: 0;\n padding: 0;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n}\n\n.matching-records li {\n padding: 8px;\n cursor: pointer;\n}\n\n.matching-records li:hover {\n background-color: #f0f0f0;\n}\n"] }]
198
- }], null, { record: [{
337
+ args: [{ selector: 'mj-link-field', template: "<div>\n @if (LinkComponentType === 'Search') {\n <input \n #inputBox\n type=\"text\" \n class=\"input-box\" \n [ngClass]=\"RecordLinked ? 'record-linked' : ''\" \n [(ngModel)]=\"RecordName\" \n kendoTextBox \n />\n <button kendoButton (click)=\"onClearClicked()\">Clear</button>\n\n <ul *ngIf=\"showMatchingRecords\" class=\"matching-records\">\n <li *ngFor=\"let record of RelatedEntityRecords\" (click)=\"onRecordSelected(record)\">\n {{ RelatedEntityNameField ? record.Get(RelatedEntityNameField) : record.PrimaryKey.ToString() }}\n </li>\n </ul>\n }\n @else {\n @if (RelatedEntityInfo) {\n <kendo-multicolumncombobox \n [data]=\"RelatedEntityRecords\"\n [textField]=\"RelatedEntityInfo.NameField ? RelatedEntityInfo.NameField.Name : RelatedEntityInfo.FirstPrimaryKey.Name\"\n [valueField]=\"RelatedEntityInfo.FirstPrimaryKey.Name\"\n [(value)]=\"SelectedRecord\"\n >\n @for (d of dropDownColumns; track d) {\n <kendo-combobox-column\n [field]=\"d.Name\"\n [title]=\"d.DisplayNameOrName\"\n >\n </kendo-combobox-column>\n }\n </kendo-multicolumncombobox> \n }\n }\n</div>", styles: [".record-linked {\n color: darkblue;\n text-decoration: underline;\n}\n\n.input-box {\n width: 400px;\n}\n\nbutton {\n width: 50px;\n}\n\n\n.matching-records {\n width: 400px;\n list-style-type: none;\n margin: 0;\n padding: 0;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n}\n\n.matching-records li {\n padding: 8px;\n cursor: pointer;\n}\n\n.matching-records li:hover {\n background-color: #f0f0f0;\n}\n"] }]
338
+ }], () => [{ type: i0.ChangeDetectorRef }], { record: [{
199
339
  type: Input
200
340
  }], FieldName: [{
201
341
  type: Input
202
342
  }], RecordName: [{
203
343
  type: Input
344
+ }], LinkComponentType: [{
345
+ type: Input
204
346
  }], inputBox: [{
205
347
  type: ViewChild,
206
- args: ['inputBox', { static: true }]
348
+ args: ['inputBox', { static: false }]
207
349
  }] }); })();
208
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJLinkField, { className: "MJLinkField", filePath: "src/lib/link-field.component.ts", lineNumber: 17 }); })();
350
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJLinkField, { className: "MJLinkField" }); })();
@@ -75,4 +75,4 @@ SectionLoaderComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: Sect
75
75
  type: ViewChild,
76
76
  args: ['container', { read: ViewContainerRef }]
77
77
  }] }); })();
78
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SectionLoaderComponent, { className: "SectionLoaderComponent", filePath: "src/lib/section-loader-component.ts", lineNumber: 10 }); })();
78
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SectionLoaderComponent, { className: "SectionLoaderComponent" }); })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-base-forms",
3
- "version": "1.8.0",
3
+ "version": "2.0.0",
4
4
  "description": "MemberJunction: Angular Base Form Components",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -25,21 +25,21 @@
25
25
  "@angular/router": "18.0.2"
26
26
  },
27
27
  "dependencies": {
28
- "@memberjunction/global": "1.8.0",
29
- "@memberjunction/core": "1.8.0",
30
- "@memberjunction/ng-code-editor": "1.8.0",
31
- "@memberjunction/ng-shared": "1.8.0",
32
- "@memberjunction/ng-tabstrip": "1.8.0",
33
- "@memberjunction/ng-link-directives": "1.8.0",
34
- "@memberjunction/ng-container-directives": "1.8.0",
35
- "@memberjunction/ng-record-changes": "1.8.0",
36
- "@memberjunction/ng-base-types": "1.8.0",
28
+ "@memberjunction/global": "2.0.0",
29
+ "@memberjunction/core": "2.0.0",
30
+ "@memberjunction/ng-code-editor": "2.0.0",
31
+ "@memberjunction/ng-shared": "2.0.0",
32
+ "@memberjunction/ng-tabstrip": "2.0.0",
33
+ "@memberjunction/ng-link-directives": "2.0.0",
34
+ "@memberjunction/ng-container-directives": "2.0.0",
35
+ "@memberjunction/ng-record-changes": "2.0.0",
36
+ "@memberjunction/ng-base-types": "2.0.0",
37
37
  "@progress/kendo-angular-buttons": "16.2.0",
38
38
  "@progress/kendo-angular-dropdowns": "16.2.0",
39
39
  "@progress/kendo-angular-inputs": "16.2.0",
40
40
  "@progress/kendo-angular-dateinputs": "16.2.0",
41
41
  "@progress/kendo-angular-dialog": "16.2.0",
42
- "ngx-markdown": "18.0.0",
42
+ "ngx-markdown": "^18.0.0",
43
43
  "rxjs": "^7.8.1",
44
44
  "tslib": "^2.3.0"
45
45
  },