Skip to content

Commit

Permalink
Support Lookup field for binding and bug fixing
Browse files Browse the repository at this point in the history
  • Loading branch information
rezanid committed Jan 3, 2023
1 parent fd82e89 commit 17998c9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 17 deletions.
8 changes: 6 additions & 2 deletions DynamicDropdown/ControlManifest.Input.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control namespace="be" constructor="DynamicDropdown" version="0.0.1" display-name-key="DynamicDropdown_Display" description-key="DynamicDropDown_Desc" control-type="standard" >
<control namespace="rn" constructor="DynamicDropdown" version="0.0.1" display-name-key="DynamicDropdown_Display" description-key="DynamicDropDown_Desc" control-type="standard" >
<!--external-service-usage node declares whether this 3rd party PCF control is using external service or not, if yes, this control will be considered as premium and please also add the external domain it is using.
If it is not using any external service, please set the enabled="false" and DO NOT add any domain below. The "enabled" will be false by default.
Example1:
Expand All @@ -18,7 +18,7 @@
-->
</external-service-usage>
<!-- property node identifies a specific, configurable piece of data that the control expects from CDS -->
<property name="value" display-name-key="Value_Display" description-key="Value_Desc" of-type="SingleLine.Text" usage="bound" required="true"/>
<property name="value" display-name-key="Value_Display" description-key="Value_Desc" of-type-group="ValueType" usage="bound" required="true"/>
<property name="entityType" display-name-key="EntityType_Display" description-key="EntityType_Desc" of-type="SingleLine.Text" usage="input" required="true" />
<property name="query" display-name-key="Query_Display" description-key="Query_Desc" of-type="SingleLine.Text" usage="input" required="true" />
<property name="textField" display-name-key="TextField_Display" description-key="TextField_Desc" of-type="SingleLine.Text" usage="input" required="true" />
Expand All @@ -39,6 +39,10 @@
</type-group>
<property name="sampleProperty" display-name-key="Property_Display_Key" description-key="Property_Desc_Key" of-type-group="numbers" usage="bound" required="true" />
-->
<type-group name="ValueType">
<type>SingleLine.Text</type>
<type>Lookup.Simple</type>
</type-group>
<type-group name="QueryValueType">
<type>SingleLine.Text</type>
<type>Lookup.Simple</type>
Expand Down
50 changes: 39 additions & 11 deletions DynamicDropdown/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DynamicComboBox } from './DynamicComboBox';
export class DynamicDropdown implements ComponentFramework.StandardControl<IInputs, IOutputs> {
private notifyOutputChanged: () => void;
rootContainer: HTMLDivElement;
selectedValue: string | number;
selectedValue: string | number | ComponentFramework.LookupValue[] | null;
context: ComponentFramework.Context<IInputs>;

/**
Expand Down Expand Up @@ -54,45 +54,61 @@ export class DynamicDropdown implements ComponentFramework.StandardControl<IInpu
type possibleQueryValueNames = 'queryValue1' | 'queryValue2' | 'queryValue3' | 'queryValue4' | 'queryValue5';

const getQueryValue = (_: string, index: any) => {
const queryValue = queryValues['queryValue' + index as possibleQueryValueNames];
const queryValue = queryValues['queryValue' + index as possibleQueryValueNames];
if (queryValue.type == 'Lookup.Simple') {
const value = (queryValue as ComponentFramework.PropertyTypes.LookupProperty).raw
return value ? value[0].id : null;
return value && value.length > 0 ? value[0].id : '00000000-0000-0000-0000-000000000000';
}
return queryValue.raw;
}

if (value && value.attributes && query && entityType.raw && query.raw) {
const q = queryValues ? query.raw.replace(/\${(\d+)}/gm, getQueryValue) : query.raw;
let p = context.webAPI.retrieveMultipleRecords(entityType.raw, q).then(
(value) => { options = value.entities; },
const p = context.webAPI.retrieveMultipleRecords(entityType.raw, q).then(
(val) => {
options = val.entities;
// const valueToMatch = this.tryGetLookupId(value) ?? value.raw;
// const isValueFound = valueToMatch == null || valueField !== null && valueField.raw !== null && val.entities.some((entity) => entity[valueField.raw ?? ''] == valueToMatch);
// if (!isValueFound) {
// this.selectedValue = null;
// this.notifyOutputChanged();
// }
},
(error) => { errorMessage = error.message; }
);

const getAttributeValue = (entity: ComponentFramework.WebApi.Entity, attribute: string): string => entity[attribute];
const getAttributeValueRegEx = (entity: ComponentFramework.WebApi.Entity, attribute: string): string => attribute.replace(/\${([\w_]+)}/gm, (_,m)=> entity[m]);

p.then(() => {
ReactDOM.render(
React.createElement(DynamicComboBox, {
//label: value.attributes.DisplayName,
label : '',
value: value.raw,
/* fetch list from webApi / fetchXml, then map and return it. */
value: this.tryGetLookupId(value) ?? value.raw,
options: options,
optionKeyField: valueField.raw,
optionTextField: textField.raw,
disabled: disabled,
masked: masked,
errorMessage: errorMessage,
onChange: this.onChange,
onChange: value.type == 'Lookup.Simple' ? this.onChangeLookup : this.onChange,
getKeyValue: valueField.raw && valueField.raw.indexOf('$') > -1 ? getAttributeValueRegEx : getAttributeValue,
getTextValue: textField.raw && textField.raw.indexOf('$') > -1 ? getAttributeValueRegEx : getAttributeValue
}),
this.rootContainer,
);
})
}
}

tryGetLookupId = (value : ComponentFramework.PropertyTypes.Property) => {
if (value == null || value.type !== 'Lookup.Simple' || value.raw == null) { return null; }
const lookupValue = value as ComponentFramework.PropertyTypes.LookupProperty;
return (lookupValue.raw.length == 0) ? null : lookupValue.raw[0].id;
}

retrieveOption = async (entityType: string, query: string, webAPI: ComponentFramework.WebApi): Promise<any> => {
if (!entityType || !query) { return; }

try {
const t = await webAPI.retrieveMultipleRecords(entityType, query);
return { options: t.entities }
Expand All @@ -101,11 +117,23 @@ export class DynamicDropdown implements ComponentFramework.StandardControl<IInpu
}
}

onChange = (newValue: string | number): void => {
onChange = (newValue: string | number | null, newText: string | null): void => {
this.selectedValue = newValue;
this.notifyOutputChanged();
};

onChangeLookup = (newValue: string | number | null, newText: string | null): void => {
this.selectedValue = (newValue == null) ? [{} as ComponentFramework.LookupValue] :
[
{
id: newValue,
name: newText,
entityType: (this.context.parameters.value as ComponentFramework.PropertyTypes.LookupProperty).getTargetEntityType()
} as ComponentFramework.LookupValue
];
this.notifyOutputChanged();
}

/**
* It is called by the framework prior to a control receiving new data.
* @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
Expand Down
2 changes: 1 addition & 1 deletion DynamicDropdown/strings/DynamicDropdown.1033.resx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
<value>Dynamic Dropdown</value>
<comment/>
</data>
<data name="DynamicDropdown_Desc" xml:space="preserve">
<data name="DynamicDropDown_Desc" xml:space="preserve">
<value>Displays a drop-down control with a list that can be filled based on a query that can depend on the value of other fields.</value>
<comment/>
</data>
Expand Down
6 changes: 3 additions & 3 deletions solutions/src/Other/Solution.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
<UniqueName>DynamicDropDown</UniqueName>
<LocalizedNames>
<!-- Localized Solution Name in language code -->
<LocalizedName description="Dynamic Dropdown for Power Platform" languagecode="1033" />
<LocalizedName description="Dynamic Dropdown" languagecode="1033" />
</LocalizedNames>
<Descriptions />
<Version>1.0</Version>
<Version>1.1.24</Version>
<!-- Solution Package Type: Unmanaged(0)/Managed(1)/Both(2)-->
<Managed>2</Managed>
<Publisher>
<!-- Unique Publisher Name of Cds Solution -->
<UniqueName>Reza Niroomand</UniqueName>
<UniqueName>RezaNiroomand</UniqueName>
<LocalizedNames>
<!-- Localized Cds Publisher Name in language code-->
<LocalizedName description="Reza Niroomand" languagecode="1033" />
Expand Down

1 comment on commit 17998c9

@rezanid
Copy link
Owner Author

@rezanid rezanid commented on 17998c9 Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closes #1, Closes #3, Closes #4

Please sign in to comment.