evnt.target;
187 | // @ts-ignore
188 | var contextInfo = this._context.mode.contextInfo;
189 | var recordId = contextInfo.entityId;
190 | var thisEntity = contextInfo.entityTypeName;
191 | var thatEntity = this.getEntityPluralName(this._entityName);
192 | var thisEntityPlural = this.getEntityPluralName(thisEntity);
193 | var associationTable = this._context.parameters.associationTable.raw!;
194 | var lookupFieldTo = this._context.parameters.lookuptoAssociatedTable.raw!;
195 | var lookupFieldFrom = this._context.parameters.lookuptoCurrentTable.raw!;
196 | var lookupToLower = lookupFieldTo.toLowerCase();
197 | var lookupFromLower = lookupFieldFrom.toLowerCase();
198 | var lookupDataTo = lookupFieldTo + "@odata.bind";
199 | var lookupDataFrom = lookupFieldFrom + "@odata.bind";
200 | var associationTableNameField = this._context.parameters.associationLable.raw!;
201 |
202 | var data =
203 | {
204 | [associationTableNameField]: targetInput.name,
205 | [lookupDataTo]: "/" + thatEntity + "(" + targetInput.id + ")",
206 | [lookupDataFrom]: "/" + thisEntityPlural + "(" + recordId + ")"
207 | }
208 | var actual = 0;
209 | var cState = this._selStates.findIndex(e => e.text === targetInput.value);
210 | if (cState !== -1)
211 | actual = this._selStates[cState].actual;
212 |
213 | if (targetInput.checked) {
214 | await this._context.webAPI.createRecord(associationTable, data);
215 | actual++;
216 | }
217 | else {
218 | await this.deleteRecord(associationTable, lookupToLower, targetInput.id, lookupFromLower, recordId);
219 | actual--;
220 | }
221 |
222 | this._notifyOutputChanged();
223 | } catch (error) {
224 | swal.fire("checkBoxChanged", "Error:" + error.message , "error");
225 | }
226 | }
227 |
228 | //Async delete record process called when a check-box is unchecked
229 | private async deleteRecord(associationTable: string, lookupToLower: string, targetInput: string, lookupFromLower: string, recordId: string) {
230 | let _this = this;
231 | try {
232 | var result = await this._context.webAPI.retrieveMultipleRecords(associationTable, '?$select=' + associationTable + 'id&$filter=_' + lookupToLower + '_value eq ' + targetInput + ' and _' + lookupFromLower + '_value eq ' + recordId)
233 | for (var i = 0; i < result.entities.length; i++) {
234 | var linkRecordId = result.entities[i][associationTable + 'id'];
235 | }
236 | _this._context.webAPI.deleteRecord(associationTable, linkRecordId)
237 | } catch(error) {
238 | swal.fire("deleteRecord", "Error:" + error.message, "error");
239 | }
240 | }
241 |
242 | public async showHideControl(show: boolean) {
243 | try {
244 | var display = "inline";
245 | if (show === false) {
246 | display = "none";
247 | }
248 | this._unorderedList.style.display = display;
249 | } catch (error) {
250 | swal.fire("showHideControl", "Error:" + error.message, "error");
251 | }
252 | }
253 |
254 | public async refreshItems() {
255 | try {
256 | await this.getRecords();
257 | return true;
258 | } catch (error) {
259 | swal.fire("refreshItems", "Error:" + error.message, "error");
260 | }
261 | }
262 |
263 | //Retrieve plural name of a table
264 | private getEntityPluralName(entityName: string): string {
265 | if (entityName.endsWith("s"))
266 | return entityName + "es";
267 | else if (entityName.endsWith("y"))
268 | return entityName.slice(0, entityName.length - 1) + "ies";
269 | else
270 | return entityName + "s";
271 | }
272 | }
--------------------------------------------------------------------------------
/AssociationTableControl/AssociationTableControl/AssociationTableControl/strings/AssociationTableControl.1033.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | text/microsoft-resx
51 |
52 |
53 | 2.0
54 |
55 |
56 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
57 |
58 |
59 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
60 |
61 |
62 | Control Name
63 |
64 |
65 | Control Description
66 |
67 |
--------------------------------------------------------------------------------
/AssociationTableControl/AssociationTableControl/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pcf-project",
3 | "version": "1.0.0",
4 | "description": "Association Table Control (PCF).",
5 | "scripts": {
6 | "build": "pcf-scripts build",
7 | "clean": "pcf-scripts clean",
8 | "rebuild": "pcf-scripts rebuild",
9 | "start": "pcf-scripts start"
10 | },
11 | "dependencies": {
12 | "@fluentui/react": "^8.6.1",
13 | "@types/node": "^10.12.18",
14 | "@types/powerapps-component-framework": "^1.2.0",
15 | "react-bootstrap": "^1.5.2",
16 | "sweetalert2": "^10.15.7"
17 | },
18 | "devDependencies": {
19 | "pcf-scripts": "^1",
20 | "pcf-start": "^1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AssociationTableControl/AssociationTableControl/pcfconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "outDir": "./out/controls"
3 | }
--------------------------------------------------------------------------------
/AssociationTableControl/AssociationTableControl/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/pcf-scripts/tsconfig_base.json",
3 | "compilerOptions": {
4 | "typeRoots": ["node_modules/@types"],
5 | }
6 | }
--------------------------------------------------------------------------------
/AssociationTableControl/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/AssociationTableControl/Solutions/Other/Customizations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 1033
17 |
18 |
--------------------------------------------------------------------------------
/AssociationTableControl/Solutions/Other/Relationships.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/AssociationTableControl/Solutions/Other/Solution.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | crmvet_AssociationTableControl
6 |
7 |
8 |
9 |
10 |
11 | 1.0.0.3
12 |
13 | 2
14 |
15 |
16 | crmvet
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | crmvet
29 |
30 | 29305
31 |
32 |
33 |
34 | 1
35 | 1
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 1
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | 2
63 | 1
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | 1
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/AssociationTableControl/Solutions/Solutions.cdsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps
5 |
6 |
7 |
8 |
9 |
10 |
11 | cc0710f0-106c-4362-95bb-801bc7722666
12 | v4.6.2
13 |
14 | net462
15 | PackageReference
16 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/AssociationTableControl/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pcf-project",
3 | "version": "1.0.0",
4 | "description": "Association Table Control (PCF).",
5 | "scripts": {
6 | "build": "pcf-scripts build",
7 | "clean": "pcf-scripts clean",
8 | "rebuild": "pcf-scripts rebuild",
9 | "start": "pcf-scripts start"
10 | },
11 | "dependencies": {
12 | "@fluentui/react": "^7.153.0",
13 | "@types/jquery": "^3.3.33",
14 | "@types/node": "^10.12.18",
15 | "@types/powerapps-component-framework": "^1.2.0",
16 | "office-ui-fabric-react": "^7.105.1",
17 | "react": "^16.13.1",
18 | "react-dom": "^16.13.1"
19 | },
20 | "devDependencies": {
21 | "pcf-scripts": "^1",
22 | "pcf-start": "^1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AssociationTableControl/pcfconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "outDir": "./out/controls"
3 | }
--------------------------------------------------------------------------------
/AssociationTableControl/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/pcf-scripts/tsconfig_base.json",
3 | "compilerOptions": {
4 | "typeRoots": ["node_modules/@types"],
5 | }
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 crmvet
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Overview
3 |
4 |
5 |
6 | This control provides a nice UX to work with custom Microsoft Dataverse association (aka joint or many-to-many)
7 | tables on model-driven Power Apps (D365) forms.
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | There are several advantages to using custom Microsoft Dataverse tables to implement many-to-many relationships in
17 | Microsoft Dataverse. For more information and examples please read this post.
19 |
20 |
21 |
22 |
23 | Data structure model for this control:
24 |
25 |
26 |
27 |
29 |
30 |
31 |
32 | Installation
33 |
34 |
35 |
36 | You can either take source code and embed it to your DevOps process (if planning to modify the control) or install a
37 | ready-to-use solution. You can find it in Releases Folder.
39 |
40 |
41 |
42 | Configuration
43 |
44 |
45 |
46 | On the string (Single Text or Multiline Text) you can go to the Field Properties > Controls > Add Control.
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 | Configuration Parameters Description
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Parameter
64 | Description
65 | Example
66 |
67 |
68 | Selector Table
69 | Name for the Selector Table to show records to choose from in UI
70 | crmvet_table2
71 |
72 |
73 | Selector Label
74 | Logical Name for the Selector Table field to be used as a Label in UI for selector items
75 | crmvet_uilabel
76 |
77 |
78 | Association Table
79 | Name for the Association Table (aka N:N Table) where joint/association records are stored
80 | crmvet_table1_table2_association
81 |
82 |
83 | Association Lable
84 | Logical Name for the Association Table field to be used as a Label in UI for associated items
85 | crmvet_uiassociatedlabel
86 |
87 |
88 | Lookup to Associated Table
89 | Schema Name of the lookup to the associated/target table in the joint/association table (N:N)
90 | crmvet_Table2Id
91 |
92 |
93 | Lookup to Current Table
94 | Schema Name of the lookup to the current table in the joint/association table (N:N)
95 | crmvet_Table1Id
96 |
97 |
98 | Default Filter
99 | Default Filter for Selector Table to define what records would be visible in UI
100 | statuscode eq 1
101 |
102 |
103 | Visibility Toggle
104 | Show or Hide in UI Selector Values. Useful if due to the number of options PCF takes too much space
105 | on a form
106 | Yes/No
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/images/Association-Table-Control-Configuration-Form-Example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crmvet/AssociationTableControl/c035fa80795cec6c6cc002c249b375e18fa7360c/images/Association-Table-Control-Configuration-Form-Example.png
--------------------------------------------------------------------------------
/images/Association-Table-Control-ERD.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
46 |
49 |
50 |
53 |
54 |
55 |
56 |
75 |
76 |
78 |
85 |
90 |
95 |
100 |
101 |
108 |
113 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
157 |
159 |
163 |
164 |
174 |
182 |
183 |
193 |
201 |
202 |
204 |
208 |
209 |
217 |
225 |
226 |
228 |
232 |
233 |
241 |
249 |
250 |
251 |
257 |
258 |
262 |
266 |
270 |
274 |
278 |
282 |
286 |
290 |
294 |
298 |
302 |
306 |
310 |
314 |
318 |
322 |
326 |
330 |
334 |
338 |
342 |
346 |
350 |
354 |
358 |
362 |
366 |
370 |
374 |
375 | Plain.1005
377 |
382 |
383 |
387 |
391 |
395 |
399 |
403 |
407 |
411 |
415 |
419 |
423 |
427 |
431 |
435 |
439 |
443 |
444 | Sheet.1043
446 |
452 | Sheet.1044
454 |
455 |
459 |
463 |
467 |
471 |
475 |
479 |
483 |
487 |
491 |
495 |
499 |
503 |
507 |
511 |
512 |
513 |
518 |
522 |
523 |
524 |
530 |
531 |
535 |
539 |
543 |
547 |
551 |
555 |
559 |
563 |
567 |
571 |
575 |
579 |
583 |
587 |
591 |
595 |
599 |
603 |
607 |
611 |
615 |
619 |
623 |
627 |
631 |
635 |
639 |
643 |
647 |
651 |
655 |
659 |
663 |
667 |
671 |
675 |
679 |
680 | Sheet.1045
682 | OOTB Dataverse Many to Many relationship
684 |
689 | Sheet.1046
691 |
692 |
696 |
700 |
704 |
708 |
712 |
716 |
720 |
724 |
728 |
732 |
736 |
740 |
744 |
748 |
752 |
753 |
754 |
759 |
761 |
766 | OOTB Dataverse Many to Many relationship
772 |
773 |
779 |
780 |
784 |
788 |
792 |
796 |
800 |
804 |
808 |
812 |
816 |
820 |
824 |
828 |
832 |
836 |
840 |
844 |
848 |
852 |
856 |
860 |
864 |
868 |
872 |
876 |
880 |
884 |
888 |
892 |
896 |
900 |
901 | Plain.1010
903 |
908 |
909 |
913 |
917 |
921 |
925 |
929 |
933 |
937 |
941 |
945 |
949 |
953 |
957 |
961 |
965 |
969 |
973 |
977 |
981 |
982 | Sheet.1048
984 |
990 | Sheet.1049
992 |
993 |
997 |
1001 |
1005 |
1009 |
1013 |
1017 |
1021 |
1025 |
1029 |
1033 |
1037 |
1041 |
1045 |
1049 |
1050 |
1051 |
1056 |
1060 |
1061 |
1062 |
1068 |
1069 |
1073 |
1077 |
1081 |
1085 |
1089 |
1093 |
1097 |
1101 |
1105 |
1109 |
1113 |
1117 |
1121 |
1125 |
1129 |
1133 |
1137 |
1141 |
1145 |
1149 |
1153 |
1157 |
1161 |
1165 |
1169 |
1173 |
1177 |
1181 |
1185 |
1189 |
1193 |
1197 |
1201 |
1205 |
1209 |
1213 |
1217 |
1221 |
1225 |
1229 |
1233 |
1234 | Sheet.1050
1236 | Custom Association Table in Dataverse to implement custom Man...
1238 |
1243 | Sheet.1051
1245 |
1246 |
1250 |
1254 |
1258 |
1262 |
1266 |
1270 |
1274 |
1278 |
1282 |
1286 |
1290 |
1294 |
1298 |
1302 |
1306 |
1307 |
1308 |
1313 |
1315 |
1320 | Custom Association Table in Dataverse to implement custom Many to Many relationship
1326 |
1327 |
1332 | Sheet.1062
1334 | Table 1
1336 |
1338 |
1343 |
1350 | Table 1
1362 | Dynamic connector
1364 | N:N
1366 |
1368 |
1373 |
1377 |
1385 | N:N
1396 | Sheet.1064
1398 | Table 2
1400 |
1402 |
1407 |
1414 | Table 2
1425 | Sheet.1065
1427 | Table 1
1429 |
1431 |
1436 |
1443 | Table 1
1455 | Dynamic connector.1001
1457 |
1461 |
1466 | Sheet.1067
1468 | Table 2
1470 |
1472 |
1477 |
1484 | Table 2
1495 | Sheet.1068
1497 | Table 1 to Table 2 Association
1499 |
1501 |
1506 |
1513 | Table 1 to Table 2 Association
1529 | Dynamic connector.1004
1531 |
1535 |
1536 |
1537 |
--------------------------------------------------------------------------------
/images/Association-Table-Control-Form-Example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crmvet/AssociationTableControl/c035fa80795cec6c6cc002c249b375e18fa7360c/images/Association-Table-Control-Form-Example.gif
--------------------------------------------------------------------------------