Related sites:

Newsletter: Perspectives on Power Platform

Company: Niiranen Advisory Oy

Clone records with Power Fx & custom command bar button

How to copy an existing record in Model-driven Power Apps, along with its child records, using the modern command designer & Power Fx formula.

In my previous post I built my very first command bar button based on the modern commanding feature currently in preview. This time we’ll continue creating similar generic features often requested yet missing in the standard Model-driven Power Apps or Dynamics 365 Customer Engagement apps.

Repeated entry of data that already exists in the system should always be a task we aim to minimize in the business applications we build, to increase the likelihood of A) the data being recorded in the first place and B) reducing errors resulting from manual data entry. Sometimes there may be a perfectly valid business need why several nearly identical records should exist in the Dataverse database. In such scenarios, it’s common that users will at some point request a “clone this record” feature to be added into the app when they get tired of typing in the same values over and over again.

Such functionality has traditionally been implemented via a custom command bar button added with Ribbon Workbench, which then runs JavaScript & possibly a plugin. You could also perform the action with an on-demand XRM workflow. How about the modern command designer, could we do all this in Power Fx? Let’s find out!

Power Fx for cloning asset records

In our scenario we are working with a custom IT asset management app that has the following Dataverse tables and relationships:

Let’s say that we often provide our employees the same phone model and we’d therefore want to make it faster to add these as asset records by being able to clone an existing asset as the starting point.

First we’ll launch the preview version of the Model-driven app designer and choose to edit the command bar for the asset table. This time we’ll target the main grid:

In the command designer, let’s add a new command bar button called “Clone” and start thinking about the required configuration for it.

We’re going to want to do a similar limitation of scope as in the previous article, by supporting only a single record at a time. Multi-record selection is beyond what we can currently process, so a visibility rule is needed for us to ensure that the button is clickable only when a single record from the table main grid has been selected. The way to achieve this is:

CountRows(Self.Selected.AllItems) = 1

Then for the button’s action part. The end result of what we need to create looks like this:

Unlike in the previous article, we are now not updating the currently selected record but rather creating a brand new one. We’re able to use the exact same kind of Patch formula that any Canvas app would leverage when adding Dataverse records and not using the built-in create forms.

Our assets table has fields than can be directly copied from the earlier asset record. These include text fields like description and lookup fields like vendor and product. The syntax for the formula is very straightforward for all these: just use “Self.Selected.Item.[YourFieldNameHere]” to populate the field on the new record to be patched.

We can of course manipulate the field values for our cloned record via simple tweaks in the Power Fx formula. Let’s set the asset’s purchased date value to Today() and add a “Copy of” prefix to the asset name field. This ease of modifying the field values based on evolving business requirements is one of the key benefits from having the logic defined with a low-code programming language that a citizen developer can use with confidence.

After the patch, we can call up the notification bar in the Model-driven app to display a confirmation message from the clone operation. That’s pretty much all there’s to it, so here’s our formula:

Patch(Assets, Defaults(Assets),{Name: "Copy of " & Self.Selected.Item.Name, Vendor:Self.Selected.Item.Vendor, Product:Self.Selected.Item.Product, 'Purchase Date':Today(), 'Purchased From':Self.Selected.Item.'Purchased From', 'Purchase Cost':Self.Selected.Item.'Purchase Cost', 'Warranty End Date':Self.Selected.Item.'Warranty End Date', Description:Self.Selected.Item.Description});
Notify("A copy of the selected asset, " & Self.Selected.Item.Name & ", has been created. Please open the record and update the necessary fields.")

Here’s the resulting end user experience for cloning a record from the main grid:

Not too bad at all for a relatively simple formula, added as a command bar button via a visual editor available in the platform.

Limitations of Power Fx commands today

To be honest, the first cloning scenario that I actually set out to build was targeting the main form. Adding the custom button here is just as simple as the main grid. Actually, since you don’t even have to think about the visibility rules and multiple selection, it would be the logical place for any new Power Fx user to start customizing their Model-driven app command bar. However, I didn’t get exactly the result I hoped for there.

The general cloning logic works on the form, too, but there is an unfortunate user experience issue. Can you spot it?

In the above image, we have already clicked on the Clone button. We have a notification bar as a confirmation. However, we are still sitting on the original record form, not the cloned version of it. If a user would for a moment think that “ok here’s the new cloned record, let’s modify a few fields” the business data would be messed up.

Could we take the user to the form of the newly created record instead? In theory, yes. In practice, nothing I’ve tried works as of today. The Navigate function that should be supported for custom command buttons doesn’t want to co-exist with any other function in the same action of a custom button. It doesn’t want to accept the newly patched record as the target to be navigated to. Even if we’d navigate to the view for all active assets after creating a new record, the UI will still return to the original record after everything in our button’s action formula has been run.

With this current limitation in mind, I started to explore if we’d have any other method to take the user away from the original record form. Navigating to a dedicated “Cloned Assets” view was out of the question, due to the aforementioned feature/bug. Using the Model-driven app notification bar to display and action button with a link seems to not be an option since the documentation says the Notify function available in the command bar is actually that from the Canvas world. This doesn’t have the same action property as addGlobalNotification on the XRM side that can show a clickable link.

One thing I really hoped would work is the new in-app notifications for Model-driven apps. This is such an excellent scenario for leveraging Power Fx to construct a rich message aimed at the specific user with targeted actions and helpful information. It could work the way shown below:

But in the case of the command bar, it doesn’t work today. You see, the above examples of in-app notifications have been triggered from a dummy Canvas app I used for testing the notification feature in general. I took the Power Fx code from the awesome blog post by Diana Birkelbach that describes how to send in-app notifications from Custom Pages.

The reason why you can’t make this work (at least to my knowledge) inside the command bar is the lack of support for collections. You see, in order to construct the JSON data needed for adding an action button into the notification, you would in practice need to feed a collection into the JSON function in your Power Fx. This will result in a “formula within canvas component manipulates collection” warning inside the command designer and this part of your formula will silently fail upon clicking the button.

Update 2021-10-10: Scott Durow gave me a great tip over on LinkedIn, reminding that the in-app notification body text actually supports Markdown syntax. This way you could include a dynamic link that points to the newly created record clone, without having to insert the more complex strings needed for displaying proper action buttons in the notification:

Presumably all of these things will one day work, which will give you plenty of options to design a great user experience for your custom commands. Just be cautious when building on top of the preview feature of modern commanding.

Cloning parent + child records

The above example was a very simple case of record cloning, as we only needed to replicate data from one table. In real world scenarios we often run into a requirement where also the child records in related tables would need to be cloned together with the parent record. Can we achieve this with Power Fx?

Let’s use the same IT asset management data model but move one level higher and build a custom command button to clone a product record and its related asset records. Meaning, both A + B in this picture:

So, we’d like to first create a new product record, which is the exact same Power Fx formula pattern as before. After that’s done, we’d then need to have one or more record create events, depending on how many assets (if any) there are under the original product.

Let’s use the ForAll function to do a patch for each of the existing assets. To identify these records, we can reference them from under the current record with the familiar “dot notation” to travel down the Dataverse table relationships: Self.Selected.Item.Assets.

Then what we need to do is to ensure we link these newly patched assets to the new product record we created in the first step. To achieve this, I’ve added a ClonedProduct variable that is set to the result of the first patch. We can then use this ClonedProduct object when setting values for the N asset records we create inside the ForAll loop.

Our formula for the “Clone With Assets” button on the product main grid is as follows:

Set(ClonedProduct, Patch(Products, Defaults(Products),{Name: "Copy of " & Self.Selected.Item.Name, 'Product Category': Self.Selected.Item.'Product Category', Vendor: Self.Selected.Item.Vendor, Description: Self.Selected.Item.Description}));
ForAll(Self.Selected.Item.Assets, Patch(Assets, Defaults(Assets),{Name: "Copy of " & Name, Vendor:ClonedProduct.Vendor, Product:ClonedProduct, 'Purchase Date':Today(), 'Purchased From':'Purchased From', 'Purchase Cost':'Purchase Cost', 'Warranty End Date':'Warranty End Date', Description:Description}));
Notify("Product " & Self.Selected.Item.Name & " and its assets have been cloned. Please open the records and update the necessary fields.")

When selecting a product from the grid and clicking on the button, here’s what the end user will see:

This is already a much more powerful example of a low-code feature that can save time when users aren’t required to re-create similar sets of records over & over again. You will have the flexibility of easily adjusting the specific fields, field values and defaults used in your record cloning action.

Sure, you need to understand the concepts used in the Power Fx formula first. I’d still say the barrier for app makers with no software development background is still lower here compared to building the same thing with client-side JavaScript or server-side C# plugins.

30 Comments

  1. This is great, Jukka.

    I have built something similar using an instant flow (which isn’t really that instant compared to this) in Power Automate to clone opportunities. The user experience will be much better with a solution like yours.

    If possible, next step could be to launch a custom page as a dialog, where the user can enter the values of the fields, that will be different that the record they are copying.

    I look forward to playing around with this once it is out of preview.

    • Yes, the interactive part of changing the values before record creation is definitely something where a dialog would come in handy. It would remove the need for notifications and directing the user to update the new records. It’s another interesting preview that I should experiment with, although currently there doesn’t yet seem to be a supported way to launch the custom page dialog with Power Fx. I’m pretty sure it’s coming, though, at which point the command bar buttons can be even more powerful!

  2. I liked the edit with Markdown feature but I still would go with the previous approach of showing “View New Asset” in terms of UX design as it is more prominent for users to click rather than inline hyperlink which is easy to miss – just my opinion 😊

  3. I think it is already quite powerful in its current state. The two examples you have blogged about are cases where we use Power Automate and classic workflows today (re-open activities). Using Power Fx and custom command bar buttons will make it much more intuitive and improve the user experience.

    I can see so many use cases for that custom command buttons and custom pages. However, I am still holding back a bit while it is in preview. However, I am gathering a lot of inspiration from your blogs 🙂

  4. Hello Jukka! This post is very similar to a use case I am working on now. Unfortunately, I cannot seem to use the Set() function in the command bar editor. At it’s simplest expression, I get the following error when I enter this formula in OnSelect : Set(test, “test”) // Error: Invocation of unsupported function. Of course, I also tried using the Patch function instead of “test” but it doesn’t work. We are still in preview, so maybe there was an issue with Set() and Microsoft disabled it. Did I misunderstand something or maybe Microsoft disabled this function for now? What do you think?

    • Charles, you are correct in your assumption. 2 weeks after I published this post, Microsoft removed the support for the Set function in the modern commanding preview. I’ve heard that there are plans to restore the support for global variables in the future, but right now you’d have to build the functionality using the With() function. Now, unfortunately this makes the Power Fx formula so much more complex that I don’t have a direct answer on what would be the right way to reference these various pieces of data in examples like this record cloning scenario. It probably can be done, but it was A LOT easier with the variables.

      • Ah! Thank you for the reply. Now at least I know I’m not going crazy. 😉

        A temporary work around could be to add a Command Bar button that changes an attribute on the selected record. This change could trigger a Power Automate flow to copy the record and it’s child records, as described here: https://www.youtube.com/watch?v=jLZ9FgRFR5U

      • I have just try With function in my code, and it run. Here is example:

        With(
        Patch(
        Products,
        Defaults(Products),
        {Name: “Copy of ” & Self.Selected.Item.Name}
        ) As ClonedProduct,
        ForAll(
        Self.Selected.Item.Assets,
        Patch(
        Assets,
        Defaults(Assets),
        {
        Name: “Copy of ” & Name, Vendor:ClonedProduct.Vendor, Product:ClonedProduct, ‘Purchase Date’:Today(), ‘Purchased From’:’Purchased From’, ‘Purchase Cost’:’Purchase Cost’, ‘Warranty End Date’:’Warranty End Date’, Description:Description
        }
        )
        )
        );

  5. Jukka,

    Do you know if it is possible, using Power Fx and a custom command bar button, to set/update fields/columns on a form in a model-driven app, which is not yet saved?

    The form type is ‘Create’/1 – and there is no GUID yet, as the record is not yet saved.

    Docs here: https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/formcontext-ui/getformtype)?

    Example: I am creating a new opportunity, and I press a custom command bar button that populates certain fields on the opportunity based on a record related to the user.

  6. Thank for an informative post. I was wondering if you’d encountered any issues with setting Lookups? When I clone a record, the text and choice fields clone fine, but if I try and set a lookup field from the original record, it just comes up empty every time.

    As an example:

    Patch(‘Project Components’,Defaults(‘Project Components’),{
    Component:Self.Selected.Item.Component & ” (Cloned)”,
    Project:Self.Selected.Item.Project,
    Comments:Self.Selected.Item.Comments,
    ‘Quality Check Requirements’:Self.Selected.Item.’Quality Check Requirements’,
    ‘Uploaded By’:Self.Selected.Item.’Uploaded By’
    });

    Project and ‘Uploaded By’ are lookups to a Project and the User entity respectively and no matter what I try, they come up empty every time. It doesn’t error and creates the new record with the other fields populated, just with empty lookups.

    • John, I now have the same error as you when using my app described here in the article. Lookups come back empty, even though they worked perfectly fine back in October. I seem to also have new error dialogs with “Error code: FunctionNotFound” in some of the environments that have this solution installed – but not all of them…

      Unfortunately this & the other issues reported here demonstrate why you can’t rely on a Power Apps public preview feature to be stable enough for production scenarios yet. Power Fx has lots of potential in Model-driven apps, but we’ll need to wait for a Generally Available version of it to be launched. Obviously it hasn’t been an easy task for MS to implement the formulas originally designed for Canvas app scenarios to work in a brand new context like the modern command designer.

      • This is unfortunate as I am found myself hitting the same exact issue unfortunately. All lookup fields are coming back empty for me. I think I will revert back to creating a flow to achieve the same goal but it feels a bit more involved than the solution you proposed which seemed straight forward. Interesting post in any case so thanks again for posting.

  7. Jukka your post is amazing, you made much effort to inform community about this very useful feature, thank you for it!
    Have you ever noticed some delay with column names available to Patch in certain table? I added some columns, made publish couple of times and kept refreshing but none of those columns are available to patch. It’s like fx does not see any change within this table. Cache issues?

    • Cheers, Archie! Glad you’ve found it useful.

      If the changes are not showing up, go and open up the Canvas app that the command designed generates automatically for your app when starting to customize the buttons. It should be called “YourAppName” + _DefaultCommandLibrary. While techically it’s a Component Library and not a Canvas app, the data sources for Dataverse are still visible there for you to manually click “refresh” on. I’ve seen that this can have an impact on whether the latest schema shows up or not.

    • Greg, it is one of the unsupported Power Fx functions in modern commanding that Microsoft retrospectively blocked from the service. We may need to wait until the GA release in April 2022 to regain support for it. Of course there are no guarantees that it would get unblocked at that point, but I’m expecting MS would be able to iron out the issues that originally caused them to remove the support during the feature preview phase.

  8. Thanks very much for your tutorial. I’m keen to replicate the functionality covered in the first part of this post. I want to replice Parent records (but not generate new child records) as for the most part much of the child content is lookups where I may only want 1 or 2 elements to change while most fields simply point to the same look ups. The issue I’m having is levering the Patch formula referenced at the start of this post, I continue to get the following errors:

    “Unexpected characters. The formula contains ‘ParenClose’ where ‘Semicolon’ is expected.”
    “The type of this argument ‘cr943_Campaign’ does not match the expected type ‘Record’. Found type ‘Text’.” and last but not least “The function ‘Patch’ has some invalid arguments.”. The code looks as follows: “(Patch(‘Modular-Tests’,Defaults(‘Modular-Tests’),{Campaign:”Copy of ” & Self.Selected.Item.Name});”

    I thought I had replicated essentially what you had up above but perhaps I’m butchering the syntax?

    I am not a developer but have found the platform model driven platform to be great for what I needed to build. I do want to make it even more useful by adding this ability to Clone records as it would really be a killer feature.

  9. Hi Jukka,

    I made a comment earlier that you can disregard. I rewrote the code you used in your example again just moments again (only using a limited number of the fields I need to replicate) and it actually worked for me! Very excited about this functionality being added to my application. Feel free to not post/publish my previous comment.

    Best,

    Dave

    • I was able to navigate directly to the newly created record. My scenario: Create a record on a child table and navigate to the new record (don’t forget to add the child table to the component library!). I used the with function that Vivek recommended. The final result looked like this:

      With(Patch(table, Defaults(table), {columnstring: “string”, columnlookup: Self.Selected.Item, …}), Navigate(ThisRecord))

      (german syntax: With(Patch(table; Defaults(table); {columnstring: “string”, columnlookup: Self.Selected.Item, …); Navigate(ThisRecord))

      Thanky you very much, Jukka, for your amazing blog and all the others who contributed here with ideas.

      • This worked like a treat, Christian! Thanks to you, Jukka, and everyone’s who’s contributed here!

  10. I’d like to do the same with JS and have a problem.
    Is it possible to call Instant Flow in Model driven with JS?
    Of course I think it is possible to use httpreguest and addres like “https://prod-98…..logic.azure.com:443/workflows/..0939′ but this solution doesn’t look good

  11. Very helpful article! Are there certain tables, though, that Microsoft possibly blacklists from being able to do this action?
    I was able to get it to work on some basic tables, but I was really wanting to add this for Quotes – and I just can’t get it working. It will show the notification I have at the end, but won’t actually copy/create the new Quote record and I can’t figure out why unless if Microsoft is just restricting this on Quotes or something.

  12. Thanks for sharing. I also came across Scott’s Youtube recently and then combined with your posting I was able to Make a Clone Button on the Main Form that would Clone the Parent Order as well as the OrderDetails (line items). Here is my version incase it helps anyone. Thanks again to both you and Scott!

    With(
    {
    purchaseOrder: Patch(
    Orders,
    Defaults(Orders),
    {Name: “Copy of “& Self.Selected.Item.Name,
    Customer:Self.Selected.Item.Customer,
    //OrderDate:Self.Selected.Item.OrderDate}
    OrderDate:Today()}
    )
    },
    // Clone child record ie line items for this order
    Patch(OrderDetails,
    ForAll(Self.Selected.Item.OrderDetails As LineItem,
    Patch(
    Defaults(OrderDetails),
    {
    Order: purchaseOrder,
    Name: LineItem.Name,
    Product: LineItem.Product,
    Qty: LineItem.Qty
    }
    )
    )
    );
    Navigate(purchaseOrder);
    Notify(“Order Successfully Copied! Now edit the copied order as needed.”);

    );

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.