Skip to content

PolyLookup

Khoa Nguyen edited this page Dec 11, 2024 · 55 revisions

Overview

Compatible with Model Driven App Type standard Template Field Version 1.7.0 Sponsor on Github

PolyLookup is a multi-select control in Model Driven App. It allows users to select one or more items from a list of options.

PolyLookup control supports various types of relationships such as OOTB N-N relationship, custom N-N relationship, and N-N using Connection table.

Features

  • Supports various types of N-N relationships
  • Clean design
  • Minimal configuration
  • Dynamic filtering
  • Quick Create
  • Optionally output selected as text

Component Props

Property Usage Type Description
Bound Field* bound SingleLine.Text
SingleLine.TextArea
MultipleLine
The field to host the control and output value
Relationship Type* input Enum
Many to many Custom N-N Connection
Relationship type between tables
Relationship Name* input SingleLine.Text - N-N: The relationship name to the associated table.
- Custom N-N: The relationship name from the current table to the intermediate table.
- Connection: Connected From relationship name
Relationship 2 Name* (required for Custom N-N and Connection) input SingleLine.Text - N-N: Not applicable.
- Custom N-N: The relationship name from the intermediate table to the associated table.
- Connection: Connected To relationship name
Should Output Selected Text* input Enum
Yes No
Whether the control should output selected items as text
Output Selected Field (required if you want to use the control in Create form) input SingleLine.Text
SingleLine.TextArea
MultipleLine
The field to output the selected records as a JSON array. Can be the same as "Bound Field"
Lookup View input SingleLine.Text
SingleLine.TextArea
MultipleLine
Lookup View configuration. Value can be a static text or bound to a field on the form, the value can be:
1. a FetchXML
2. a OData URL to a column of a record containing a FetchXML
3. An environment variable logical name containing a FetchXML
4. System View name
Item Limit input Whole.None Maximum number of selected items
Show Icon input Enum
None Use Entity Icon Use Record Image. Default: None
Whether to show an icon in option item.
Allow Quick Create input Enum
Yes No
Enable or disable the Quick Create button
Tag Action input Enum None Open Inline Open Dialog Open Inline (intersect relationship) Open Dialog intersect relationship Perform an action when a tag is clicked. Default: None
Selected Item Template input SingleLine.Text Dynamic template to display selected option text. Default to the first column in the lookup view if not provided.
Show Option Details input Enum
Collapsed Expanded Tooltip
Option to show more details in suggestion list. Default: Collapsed.
Language Pack Path input SingleLine.Text Path to a resx web resource, relative to the environment url (eg. /webresources/new_/resx/PolyLookup.1033.resx), to be used as Language Pack. Default to English if not provided.

** is a required prop

Important

Please use the relationship name, not the relationship table name, see below samples

Sample Relationship Names

Sample Table Relationships

N:N

erDiagram
    COURSE }o--o{ CATEGORY : relationship_1
Loading

Custom N:N

erDiagram
    STUDENT ||--o{ ENROLLMENT : relationship_1
    CLASS ||--o{ ENROLLMENT : relationship_2
Loading

Connection

erDiagram
    COURSE ||--o{ CONNECTION : course_connections1
    COURSE ||--o{ CONNECTION : course_connections2
Loading

Usage

Suggestions

Empty search term

When user first clicks on the control and the search term is empty, it will load the first 50 records from the associated table.

The page size 50 is retrieved from the user's from personal settings.

Search

When search token {{PolyLookupSearch}} is not present in Lookup View (see Dynamic Templating section below), the control will use default search by Primary Name attribute for records that start with the search term from the associated table. And wildcard search using * is also supported by default in this case. For example, *ABC will return records having the primary name contains ABC.

Note that Wildcard Search is not applied when Default Search is not used. This is to respect whatever filter condition operator that you specify in your FetchXML with the {{PolyLookupSearch}}.

For example, given the below FetchXML which filters for active Course Categories

<fetch>
  <entity name="storm_coursecategory">
    <attribute name="storm_name"/>
    <attribute name="storm_sortnumber"/>
    <attribute name="storm_description"/>
    <order attribute="storm_sortnumber" descending="false"/>
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0"/>
    </filter>
  </entity>
</fetch>

There is no {{PolyLookupSearch}} token in the query. When user enters this search text *at*2, the Default Search with wildcard search on the primary name attribute will be added and the query becomes like this

<fetch>
  <entity name="storm_coursecategory">
    <attribute name="storm_name"/>
    <attribute name="storm_sortnumber"/>
    <attribute name="storm_description"/>
    <order attribute="storm_sortnumber" descending="false"/>
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0"/>
      <condition attribute="storm_name" operator="like" value="%at%2%"/>
    </filter>
  </entity>
</fetch>

This will return all categories that contain at<any thing in between>2

In some cases, you may want to search on one or more columns other than the primary name, then you can pass a query like this

<fetch>
  <entity name="storm_coursecategory">
    <attribute name="storm_name"/>
    <attribute name="storm_sortnumber"/>
    <attribute name="storm_description"/>
    <order attribute="storm_sortnumber" descending="false"/>
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0"/>
      <condition attribute="storm_description" operator="like" value="%{{PolyLookupSearch}}%"/>
    </filter>
  </entity>
</fetch>

This will return all categories that have the description containing the search text. Primary name filter won't be added, and the * wildcard won't apply in the search text. This can be improved in future releases.

There is a 100ms delay before the control starts searching.

Quick Create

The Quick Create button helps users quickly create a new option right in the current form without navigating away.

Quick Create Form

The Quick Create button can open the table's Quick Create Form, if "Allow Quick Create" is enabled in table settings and a Quick Create form is created and the Quick Create form is added to the model driven app

Important

An additional requirement for Quick Create Form to display is that there must be a lookup to the associated table and the lookup field is required. After you verified that the Quick Create Form is shown correctly by clicking the Quick Create button, you can safely delete the lookup field if you don't need it.

If "Allow Quick Create" table setting isn't enabled, the main form will be used as a dialog.

Lookup View

Look View property is where you configure a custom query (FetchXML) which is used to retrieve including sort, filter, and display extended info in the suggestion items in the dropdown.

From v1.5.0, Lookup View becomes more powerful. Besides System View name, there are more ways to provide a FetchXML:

With this flexibility, you don't need to create empty System Views if you don't want to.

The control will determine the Lookup View Configuration based on the below flow:

flowchart TD
    VALUE[LookupView value] --> IS_FETCH{contains < fetch>?}
    IS_FETCH -->|Yes| FETCH[use value as is]
    FETCH --> X
    IS_FETCH -->|No| IS_ODATA{contains odata url?}
    IS_ODATA -->|Yes| ODATA[use value returned from odata]
    ODATA --> X
    IS_ODATA -->|No| SEARCH_EV[search environment variable]
    SEARCH_EV --> IS_EV{found?}
    IS_EV -->|Yes| EV[use ev fetchxml]
    EV --> X
    IS_EV -->|No| SEARCH_VIEW[search view name]
    SEARCH_VIEW --> IS_VIEW{found?}
    IS_VIEW -->|Yes| VIEW[use view fetchxml]
    VIEW --> X
    IS_VIEW -->|No| DEFAULT[use default lookup view]
    DEFAULT --> X(((End)))
Loading

Static FetchXML

you can pass a static FetchXML directly to the Lookup View property like this

Please note there is a maximum length depends on the type and form editor you choose:

Classic editor Modern editor
SingleLine.Text 160 100
SingleLine.TextArea 2052 2000
Multiple lines of text 2052 1048576

OData URL

You can store the FetchXML in any table column. And you can pass the query to the control using the single-property-value OData URL. For example, given a Custom Configuration table record to store the same static FetchXml above like this

The fetchXml is stored in the storm_value field on the form. Then you can pass this query to the control like this

  • full URL: https://org941f632d.crm4.dynamics.com/api/data/v9.2/storm_customconfigurations(8F990A4A-4653-EF11-BFE3-000D3A445FE3)/storm_value
  • relative URL: /api/data/v9.2/storm_customconfigurations(8F990A4A-4653-EF11-BFE3-000D3A445FE3)/storm_value
  • shorter relative URL: /storm_customconfigurations(8F990A4A-4653-EF11-BFE3-000D3A445FE3)/storm_value

Environment Variable

The FetchXML can be stored in an environment variable like this

Then you can pass the query to the control using either the logical name or display name of the environment variable.

Note that the maximum length in Envrionment Variable is only 2000

System View Name

You can create a normal system view using the view editor

Then pass the view name to the control

If the view is not found, the default system lookup view of the table will be used.

Note

Make sure you don't have two or more views with the same name.

Binding to a form field

✨ Binding lookup view configuration to a form field is like the "Infinity Gauntlet". You have all of the power from the above "gems", plus the ability to "react" to the field value changes.

That means when you bind the lookup view property to a field on the form. The field value will be used to determine the fetchxml to filter the dropdown options. When the field value changes, the filter will also be refreshed accordingly. The field value can be anything from the above options: your own custom fetchxml, odata URL, environment variable name, or a system view name. and of course, dynamic templating is supported in all options.

Now depending on how flexible your requirement is, you can update the bind field value with your logic, even mix and match the options, for example:

  • populate bind field value with "view name 1" initially, when something happened on the form, set the value to "view name 2", etc..., or
  • if (some condition), use "env variable 1", else if (some other condition), use "env variable 2", else use "view name 1", or
  • use this <fetch>...</fetch> by default, if (some condition), update the fetch, else if (some condition), use "view name 1"

Note that you can use dynamic templating in all your fetchxml, doesn't matter where the fetch is stored. so you don't need to create new views/variables/fetch if it can be done using dynamic templating.

You can see the possibilities are endless. The only limitation is your imagination! ✨

Important

Lookup View may not work if it's too long/complex with lots of conditions. This is because the component is using the FetchXML in the Lookup View to retrieve data using Web API GET method and the maximum length of the URL is about 8000 characters. But the fetchxml will need to be URL encoded, so we only have about 4600 characters maximum for the actual FetchXML. It should be enough in most cases.

Dependent filtering with Dynamic Templating

Another advanced feature is dynamic templating. This helps you to insert data from the current record into your lookup field.

Dynamic templating syntax: {{ field_logical_name_on_form }}

With nested objects such as a Lookup field, you can access its id or name using {{ lookup_field.id }} or {{ lookup_field.name }}

Important

The fields you want to insert to the Lookup View must be available on the form, either visible or invisible

Reserved Token {{PolyLookupSearch}} is a placeholder for the search text of the control.

Suggestion item details

The Lookup View is also used to show information in the suggestion list. When there are more than one columns in the view, there will be a toggle button to show/hide more details for each suggestion item.

If you are not using system view as lookup view, the list of columns and the order to display depends on your fetchxml.

Pagination

The suggestion list only shows the first page of the results. The "Load more" button is available and the bottom of the list. Clicking the button will load the second page and add results to the list, and the button won't be available until users start searching again. There are indications at the end of the list to show if there are more results or not.

This is for the component to perform better and provide a better user experience as users need to refine the list using the search box instead of browsing through multiple pages in the suggestion list.

Selected items

Selected items are retrieved from the underlying relationship. When user selects an item from the suggestion list, the control will create a new associated record in the relationship. And when user removes an item from the selected items, the control will delete the associated record in the relationship.

Because of this, the component can't update the relationship in create form directly before the record is saved. You'll need to handle this case by yourself using output selected items feature below.

Selected Item Text

From v1.7.0, the first column in the Lookup View will be used as selected item text, this allows some flexibility to control which column should be displayed as selected text.

Optionally, you can provide a template in Selected Item Template to customize the selected item text.

For example, with {{ new_aliasname }} ({{ new_email }}), the selected item text will be displayed as "Contact Alias Name ([email protected])".

Output selected items as text

The selected items display text, as discussed in the previous section, can be output as a comma-separated string to the Bound Field. When the selected items are changed, output will be updated accordingly.

You can leverage this feature to handle the change event of the output field to add more logic when selected items are changed.

This can be enabled optionally in the control properties.

Output selected items as a JSON array

Additionally, the selected items can be output as a JSON array with the below schema. You'll need to specify what field you want to output the result to in the Output Selected Field.

[{
  "id": "<Selected item Guid>",
  "name": "<Selected item text>",
  "etn": "<Selected item entity name>"
},
{
  "id": "<Selected item Guid>",
  "name": "<Selected item text>",
  "etn": "<Selected item entity name>"
}]

You must use this feature if you want to use the component in the create form. Then create a plugin triggered after the record is saved/created, the plugin should associate the current record with selected records stored in the JSON array.

Please see the sample plugin under Samples folder here

Note

You don't need to handle this on update because the component can update the relationship directly when the current record already exists.

Disabled State

When the field is set as read-only in form designer, the control is disabled respectively.

Required Field Validation

Should Output Selected Text should be enabled for this to work

Language

you can add your own language by passing a URL to a .resx web resource to the Language Pack Path property.

you can use the default English language pack here PolyLookup.1033.resx as the template to create a new resx web resource in your environment, then update the value in your language.

you can use this VS Code extension to help you edit the resx web resource in a user interface https://marketplace.visualstudio.com/items?itemName=DominicVonk.vscode-resx-editor

For example, the resx web resource looks like this

resx web resource

the value passed to the Language Pack Path can be relative and won't need the environment part. it looks like this:

/WebResources/storm_/resx/PolyLookup.1031.resx

Limitation

  • ❌ Association doesn't work in create form as related records can only be linked when the current record exists after saved.
  • ❌ Lookup View may not work if it's too long/complex with lots of conditions
  • ❌ Bulk edit is not supported
  • ❌ Can't output selected items larger than output field's maximum length
  • ❌ N:N relationship is not supported in Mobile Offline Mode. More details here: https://learn.microsoft.com/en-us/power-apps/mobile/offline-capabilities#limitations

CHANGELOG

1.7.0

  • bug fixes
  • enhancements

1.6.1

  • fixed minor bugs

1.6.0

  • supports new look
  • added tag icon
  • selected items won't show up in dropdown list again to avoid duplication

1.5.0

  • dynamic Lookup View configuration.
  • clickable selected item tags when control is readonly.
  • wildcard search when default search is used.
  • fixed a bug where underline character (_) is escaped when LIKE operators (contains, begins with, ends with) are used in filter

1.3.0

  • added language support

1.2.2

  • fixed dynamic templating variables not refreshing cache causing suggestion results loaded incorrectly
  • fixed selected values not loaded in inactive form

1.2.1

  • added Connection relationship type.

1.1.4

  • fixed selected items not loaded in readonly form.
  • fixed control display error in form editor.

1.1.3

  • Upgraded handlebars fixing errors when building the component.

1.1.2

  • Fixed a bug in custom N-N mode, selected items is not showing when associated table primary id column and intersect table lookup column have different logical names.
  • Fixed a bug where suggestion list is not showing when lookup view name is blank.

1.1.1

  • Fixed a bug where selected values are replicated when there are more than one PolyLookup components on the form.

1.1.0

  • Supports Custom N:N relationship type
  • Added ability to search using a System View for Lookup View

1.0.17

  • Fix JSON output in the create form

1.0.16

  • Added load more button for paging

1.0.15

  • Added prop to output selected items as a JSON array

1.0.14

  • Added suggestion item details using Lookup View

1.0.13

  • Added Dynamic Search Filter

1.0.12

  • Added Quick Create button

1.0.11

  • Added page limit prop

1.0.10

  • First release

TODO

  1. ⏹️ Permission Handling
  2. ✅ Error Handling
  3. ✅ Localization
  4. ⏹️ unit test & code coverage