[Update June 2022] The modern command designer is now GA!
Power Fx command bar buttons (Commanding V2) is the latest exciting feature to be released into preview by the Power Platform team! Check out Casey's blog post and my first look video where I show how amazingly easy it is to add complex functionality to your model-driven command bars!
The Ribbon Workbench marked its 10-year anniversary this year and so it's fitting that the new Power Fx command buttons for model-driven apps have been released. This exciting new feature is part of the continued journey of converging the goodness of both model-driven apps and canvas apps into a single app that gives the best of both worlds! In this post, I'll identify some of the differences in functionality. This initial release provides the foundation for Power Fx and as you'll see there are still gaps - but I am confident that the functionality will be developed over the coming months.
Key Design differences
The Ribbon Workbench (and the underlying RibbonXml
that supports it) has many legacy components that are baggage from the days when there was a Ribbon rather than a command bar. Things like Groups, Tabs & Templates have no meaning in the command bar as we see it today. For this reason, the new Power Fx command buttons have greatly simplified the model for customizing the model-driven app command bar.
Here are some of the key differences in the design:
- Buttons, Commands & Visibility Rules are linked - In the Ribbon Workbench, you would create a button and then associate it with a command. With Power Fx commands, the button, command, and visibility rules are all linked together as a single unit.
- Localized Labels are part of the solution translations - In the Ribbon Workbench, button label translations were part of the
RibbonXml
, whereas with Power Fx commands you can use the standard export/import translations feature for the solution to provide translations.
- Customizations are deployed via separate solution components - In the Ribbon Workbench, your command buttons were deployed via the entity/table solution component. With Power Fx commands, you add the Component Library to your solution to deploy command buttons.
- No need for a solution import - Since the Power Fx commands are deployed using Component Libraries, there is no need for the lengthy export/unpack/update/rezip/import cycle that happens when you publish from inside the Ribbon Workbench. This makes working with the Power FX Command buttons much quicker!
- Power Fx solution packager required to see command details - When exporting the solution that contains the Command Component Libraries, the expressions are inside the
.msapp
files. To see the details, you will need to use the new Power Fx Solution Packager functionality to extract into yaml
files and add this to source control. The great news is that canvas app unpacking/packing is now included in the PAC CLI.
You can still use JavaScript Commands!
Possibly one of the most important features of the new commanding feature is that you can still call your existing JavaScript for commands (but not Enable rules at this time). Why is this important? Because it makes the path to migrate to Version 2 commands easier where the functionality is not yet possible in Power Fx expressions.
Common Requirements
The following table shows common requirements that I find needed when customizing the command bar using the Ribbon Workbench. You'll see that there are still gaps that will require the Ribbon Workbench for the time being - but these will be addressed over time.
Common Requirement |
Ribbon Workbench |
Commanding V2 |
Hide existing OOTB button |
Hide Action |
Not yet available |
Move existing OOTB button |
Customize Button and Drag to the new location |
Not yet available |
Change label/icon of existing OOTB button |
Customize Button and edit properties |
Not yet available |
Change command of existing OOTB button |
Customize Command and edit actions |
Not yet available |
Pass CommandValueId to JavaScript Context when the same command is used on multiple buttons |
Set CommandValueId property |
Not applicable since the command is not separate from the button |
Update a form value and then save the record |
Ribbon Workbench 'QuickJS' Smart Button or custom JavaScript. The PrimaryControl Parameter provides the event context which can be used to access the form context. |
Patch(Accounts,Self.Selected.Item,{'Credit Hold':'Credit Hold (Accounts)'.Yes}); Note: The form is automatically saved and refreshed! |
Update/Create a related record |
Ribbon Workbench 'QuickJS' Smart Button or custom JavaScript that uses the WebApi and then calls refresh on the formContext provided by a PrimaryControl parameter. |
Update Related Record:
Patch(Accounts,Self.Selected.Item,{'Description':"edit"});
Create related record (An additional data source must be added to the component library )
Patch(Tasks,Defaults(Tasks),{Regarding:Self.Selected.Item,Subject:Concatenate("Test",Text(Now()))}); Note: The form is automatically refreshed! |
Add buttons to a flyout button |
Use the Flyout or SplitButton toolbox control with a MenuSection |
[UPDATE] Now available at GA! |
Dynamically populate a flyout button (e.g. from a WebApi call) |
Use the PopulateQueryCommand with a Custom JavaScript Action |
Not yet available |
Add buttons to the Application Ribbon so that they appear in multiple locations (including the global command bar) |
Add the Application Ribbon to the solution loaded into the Ribbon Workbench. The entity type can be used in an EntityRule to show buttons for multiple entities. |
[UPDATE] Although you cannot add to the Global Command Bar - you can change the scope of buttons to appear across apps or across all tables. There is no designer support at this time for changing scope. |
Run a command on multiple selected records on a grid |
Use a Custom JavaScript Command that accepts SelectedControlSelectedItemIds as a parameter - and then iterate over the array, performing an action for each. |
New! To apply an update to multiple selected records use something similar to:
Patch(
Accounts,
ForAll(Self.Selected.AllItems,
{
Account:ThisRecord.Account,
'Credit Hold':'Credit Hold (Accounts)'.Yes
}
)
)
Note: This will ensure that the records are updated in parallel, instead of one at a time.
|
Display a blocking wait spinner whilst a long-running task is in progress |
Use showProgressIndicator inside Custom JavaScript. |
Not yet available in Power Fx command expressions |
Run a workflow |
Ribbon Workbench 'Run Workflow' Smart Button or custom JavaScript. |
Trigger a workflow on change of a form field change |
Run a report |
Ribbon Workbench 'Run Report' Smart Button or custom JavaScript. |
Use a Custom JavaScript function - you can call the Smart Button JavaScript |
Run a cloud flow |
Ribbon Workbench 'Run Webhook' Smart Button or custom JavaScript. |
Use a Custom JavaScript function - you can call the Smart Button JavaScript
Open a custom page using Navigate() (or JavaScript) and then run the flow from there.
|
Open a popup custom page dialog from a button |
Ribbon Workbench 'Open dialog' Smart Button linked to a Canvas App |
Use a Custom JavaScript function - you can call the Smart Button JavaScript or use your own JavaScript. There is also Confirm() that allows you to present a simple Yes/No style popup. |
Visibility Rules
Perhaps the biggest gap in functionality at this time is in the area of visibility rules:
Common Requirement |
Ribbon Workbench |
Commanding V2 |
Show button only for a specific Form |
Use a Custom JavaScript Enable Rule or add the RibbonXml to the FormXml |
UPDATE: This no longer works and currently there is no alternative. Use a visibility rule similar to :
'My custom table (Forms)'.'My custom form'=true |
Show button only for a specific View |
Use a Custom JavaScript Enable Rule |
UPDATE: This no longer works and currently there is no alternative. Use a visibility rule similar to :
'My custom table (Views)'.'My custom form'=true
|
Show button only for a specific App |
Use a Custom JavaScript EnableRule that returns true for specific app unique names |
Commands are added to specific apps. [Update] You can change the scope of buttons to appear across apps or across all tables. There is no designer support at this time for changing scope. |
Show a button only when online/offline |
Use the CrmOfflineAccessStateRule |
Not yet available |
Show a button based on the user's security privileges |
Use the RecordPriviledgeRule or MiscellaneousPrivilegeRule |
Use DataSourceInfo() to determine if the user has access to a specific table. Use RecordInfo() to determine access to a specific record. |
Show a button based on certain entity metadata (e.g. IsActivity ) |
Use the EntityPropertyRule in a Display Rule |
Some information is available from the DataSourceInfo. |
Show a button only for existing or read-only records. |
Use the FormStateRule in a Display Rule |
[UPDATE] Use a visibility expression similar to:
Self.Selected.State = FormMode.New
You can also determine if the record is 'Dirty' using: Self.Selected.Unsaved = true
|
Show a button only when a single record is selected in the grid |
Use the SelectionCountRule inside an EnableRule |
Visibility Expression:
CountRows(Self.Selected.AllItems)>0
OR !IsEmpty(Self.Selected.AllItems)
I prefer the CountRows version because it's more consistent with other situations like this next one. |
Show a button only when a single record is selected in the grid |
Use the SelectionCountRule inside an EnableRule |
Visibility Expression:
CountRows(Self.Selected.AllItems)=1 |
Show a button based on a form field value |
ValueRule inside an EnableRule . refreshRibbon must be called inside the onchange event of the form field. |
Visibility Expression:
Self.Selected.Item.'Credit Hold'='Credit Hold (Accounts)'.Yes
NOTE: refreshRibbon still must be called if you want the button to show/hide when the field is changed.
Currently, there is an issue when using optionsets/status reasons like this where you will need to cast to a String and compare using:
Text(Self.Selected.Item.'Credit Hold')="Yes"
|
Show a button only when a related record column has a specific value |
Use a Custom JavaScript EnableRule that performs a WebApi query. |
Self.Selected.Item.'Parent Account'.'Credit Hold'='Credit Hold (Accounts)'.Yes |
Show a button when a form value matches a complex expression |
Use a Custom JavaScript EnableRule that performs a WebApi query or uses the provided formContext . |
StartsWith(Self.Selected.Item.'Account Name',"a") |
Show a button when there are a specific number of related records matching a query |
Use a Custom JavaScript EnableRule that performs a WebApi query. |
CountRows(Self.Selected.Item.Contacts)>0 |
Summary
I will come back to this page and update it as new features are unlocked. You can also read more in the official documentation. As you'll see from the tables above, there are some gaps (especially with Enable/Display rules) but I have no doubt that they will be filled 'in the fullness of time'. The ease at which you can create complex Power Fx expressions to perform logic that would have previously required some complex JavaScript is very exciting and will unlock many scenarios that were previously off-limits to low-code app makers.
@ScottDurow
[Updated 2 June 2022]