Tag: customization

  • A few notes on the Timeline: model-driven Power Apps form tweaks

    A few notes on the Timeline: model-driven Power Apps form tweaks

    In the default form components arrangement for tables in model-driven Power Apps or Dynamics 365 CE apps, the Timeline is usually front and center. This makes perfecet sense. Scenarios that revolve around accounts and contacts typically include the need to show activities and notes related to these records.

    Compared to how things used to be just a few years ago, meaning mostly hard coded, we now have an amazing amount of configuration options for the Timeline control. This is reflected in the MS Learn article “set up the Timeline control” which has an estimated reading time of 32 minutes! And that’s just the “how” part of official documentation, not the “why” that you’ll learn as the settings are applied in real world business application context.

    This blog post is my attempt to highlight the recent, most useful features that the modern Timeline form component enables for improving the user experience of your model-driven apps.

    Rolling up the notes

    The one huge feature that 2022 Release Wave 2 has delivered for model-driven Power Apps has been buried deep into the release plan. It seems almost like an intentional “let’s hope no one notices this” move since the feature is listed under Dynamics 365 Customer Service and called “Improve agent productivity with timeline enhancements”:

    Notes that work like other activities and roll up on contact, account, and opportunity form timelines.

    Woohoo! We’ve been waiting for this feature close to 2 decades already! If you haven’t experienced the Dynamics CRM era, then the significance of the rollup capability in the platform may not be immediately obvious. I won’t go deep into it now, instead I’ll dig up an illustration that I created 11 years ago for a blog post I wrote about the native activities associated views vs. form subgrids:

    Unlike other standard or custom tables in Dataverse, activities have one superpower: they can be shown not just from the directly related child records but also rolled up from deeper in the hierarchy across many different record types (tables, entities, you pick the terminology). This means that in the account Timeline you’re able to see tasks, phone calls, emails, appointments that are set regarding a child record, like an opportunity where that account is the potential customer.

    While the same form component has been used to display both activities and notes (and activity feed posts, in case someone’s still using them), the rollup functionality has been exclusive to activity data. This has lead to a classic CRM functional gap where a note record added onto a lead will not be carried over once the lead is qualified and becomes an account/contact/opportunity. Notes have been “sticky” in the sense that you slap them on a specific record and that’s where they’ll forever stay.

    Well not anymore! While the official docs on Learn are still pending to be updated to describe the feature in more detail, the “Notes rollup type” setting already appears on the Timeline component properties:

    Setting this to “Extended” for the Timeline control on the account means that we’ll now see notes that have been added to the originating lead, any of the account’s contacts or opportunities. Here’s an example where there are zero notes on the account itself, yet we can see 3 notes on the Timeline:

    All the notes from these child records appear just as like they were the traditional notes on the account record itself. In fact, you can simply click the pen icon to start editing a note on the account form Timeline, even if it has originally been added via a different record:

    This can be a bit confusing for the user, since there’s no visual indication on the Timeline card on which exactly is the original hosting record of that note. Which probably is due to the Dataverse data model that doesn’t treat the notes (annotation) table as something where the users could freely set the Regarding record information. With such a non-standard implementation it might be tricky even for the team developing the Timeline to surface this information in the UI.

    You need to keep in mind that this notes rollup isn’t applicable to custom tables, nor across all system tables either. Just like the activity rollup, this is feature of showing relational data from beyond a single relationship “hop” is not a generic platform capability in Dataverse. It works in these basic CRM style scenarios using predefined record types. And when it does, it can be hugely beneficial to end users.

    Alternative Timelines

    There are more goodies in 2022 Release Wave 2 under the release item “Try enhancements to timeline maker experience”. One of them is the support for having multiple Timelines on a single form. This means you could add a dedicated “Notes” tab on the account form and configure it to include only notes data, not activities or posts. The benefit from this would be to tailor the display settings in the main Timeline differently than in the Timeline dedicated for notes only.

    In the example above I’ve ticked the box “expand first component to full tab” in the tab settings. This removes all the whitespace and labels around the Timeline, giving it maximum space. Yeah, it’s not the prettiest tab in the world when applying this technique to the Timeline. Yet from a purely functional perspective it’s an option worth trying out.

    One common question from customers who want to customize their CRM system’s terminology has been “can we rename the Timeline to something else”? The answer is still: no. However, now that you can have multiple Timelines on a form, the workaround you can apply is this: 1) under Timeline – Properties – Advanced – Additional Settings, check the box “Hide Timeline label”, 2) set your form column label to show the string you want.

    Now the custom text will appear and the place where it used to read “Timeline” is blank:

    Native controls like search or options menu will still use the timeline terminology, though. It’s always worth asking a few times whether it’s absolutely necessary to try and change such native features of a SaaS product like Dynamics 365. (The argument “because we had it like that in our previous system” is always the wrong answer.)

    Timeline forms and actions

    An area of the Timeline that has really exploded in terms of configuration options is the forms used for interacting with records shown on or created from the Timeline. Let’s look at one example of how the default functionality for tasks could be optimized on the opportunity form Timeline. Without any customizations, it looks like this:

    Looking at the standard Timeline, there’s a bunch of command buttons available for every activity. Problem 1 is: it’s not easy to spot which icon opens the task’s detail form (at least I’ve never learned to immediately pick the right one). Problem 2: when we open the task, it takes us to a whole different web page, thus we lose the context of the original business record (opportunity).

    The Timeline allows us to fix both these problems by changing the settings just a bit. By going to Record settings – Activities and clicking on Task, we get a flyout pane with options specific to that activity type in this specific Timeline instance. Problem 1 can be fixed by disabling rarely needed actions like “Assign”, “Add to queue” (who uses queues anyway?) and “Delete” (deletion of data in CRM does not need to be easy!).

    Problem 2 is resolved by changing the “Open Task using” setting to “Main form dialog”. What does that do exactly? It gives you a modal window on top of the current record, which is a much less jarring experience than bouncing between full forms. Closing it from the X icon will allow you to keep working on the opportunity without forcing any page refresh.

    Want to learn about the Power Apps main form dialog? Read my earlier blog post on how to leverage MFDs for making it easier to work with relational data.

    Timeline configuration sprawl

    The one caveat of all this configurability that we’ve received via the modern Timeline control is that we now have a huge number of different settings we must remember to change. Performing any of the above mentioned changes is relatively easy as an isolated task, but we need to do it for all the tables and their forms where the Timeline control is used. There is no global system setting or “apply to all” option to help us. With any larger business application you’ll quickly need your own Excel sheet just to keep track of all the N settings and N forms that you must manually harmonize.

    Where things can potentially get really laborious is modifications to the card forms used for displaying activity and note data on the Timeline. If the standard date fields aren’t what we want to show, for example (created on vs. modified on vs. due date etc.), there’s a way to change most of them. However, as you can quickly see after browsing through the documentation, the card form editor from the CRM 2011 era and the modern Timeline control have nothing in common when it comes to rendering the visible UI:

    If you want to include some custom columns from your activity tables into the Timeline cards, the good news is that it’s supported. The bad news is, you’ll likely need to keep the documentation on one screen as a reference, the form editor one the second screen, and the card form legacy editor on the third one. Good luck doing these tweaks from only your laptop screen while working away from the office!

    These configuration tasks start to quickly add up. Let’s say we need to change one date field on the task card and configure its display options and label options. Perhaps our application has 20 tables where the Timeline is used for showing this activity type. Maybe we’ve also created role based forms and targeted them via different model-driven app modules, like “Sales” and “Admin”. That single requirement could mean updating the settings on maybe 30 different forms. With all the loading, saving and navigating in the Maker portal – it could easily take more than an hour to complete.

    Now, let’s say that in a Dynamics 365 project you’ve been assigned the responsibility to take care of all the details for activity and notes management features on every Timeline in the system. Sorting, filtering, control display options, supported activity types, form types per activity, actions per activity… Planning, configuring and validating all these changes will quickly consume many days, even in an SMB level CRM system. And because it’s so easy to miss a few clicks in performing this repetitive work over & over again, you’ll also need to reserve time for fixing the things that were missed.

    Achieving a consistent user experience requires plenty of work – this simple truth has not changed from the early days of Dynamics CRM projects. As Microsoft invests money in modernizing their client controls and introducing more no-code configuration options, the customers must also do their part in ensuring the changes and new possibilities in the evergreen cloud platform are taken into use in a controlled manner. Striking a balance between what can be customized and what should be customized – this remains the eternal question.

  • Dataverse results inside Microsoft Search in Office, SharePoint, Bing

    Dataverse results inside Microsoft Search in Office, SharePoint, Bing

    Whether you are configuring Dynamics 365 Customer Engagement apps from Microsoft or building custom applications with Power Apps, often the solution design and implementation focuses too much on the data entry experience and not enough on data discovery. Yet it’s the process of searching for information rather than entering new records that consumes far more time in the lives of information workers.

    Throughout the history of my blog (originally “Surviving CRM”), topics such as the Advanced Find feature or configuration of views in XRM/CDS/Dataverse have been the most popular ones. To put it another way: a lot of people search for information on how to make search better in Microsoft business apps.

    While they are nothing like Google, Microsoft still has a wealth of R&D budget spent on search related services. Just because Bing isn’t what you’re likely using in your private life, that doesn’t mean you couldn’t benefit from the Microsoft Search infrastructure. Today we’ll explore how data from Dataverse environments can now be exposed in Microsoft Search.

    Enabling Dynamics 365 results in Microsoft Search

    The feature for showing Dataverse results in Microsoft Search was originally promised at Ignite 2021 in November, but on the release plans it has been postponed a few times. Now the ability for enabling search federation has appeared and the roadmap item for it says “GA in June 2022”. The feature appeared in a couple of tenants, so it’s time for a test drive.

    First you should ensure that these prerequisites for configuration are met. The modern Dataverse search needs to be enabled for the environment you want to target. Second, the search admin user account that you’re using to run the configuration must have both admin access and a valid Dynamics 365 license for the environment.

    Next you can proceed to Microsoft 365 Admin center. Go to “All admin centers”, choose “Search & Intelligence” and select the “Data sources” tab. You should see the section “Microsoft apps and services” that allows you to add a new app for search federation.

    What if that section does not appear? It may be that you don’t have a paid Dynamics 365 subscription in the tenant. The detailed requirements haven’t been documented by Microsoft yet, but based on my experiments, having just Dataverse with Power Apps premium licensing is not enough. Neither is a trial subscription for Dynamics 365.

    In the configuration section for Microsoft apps and services, it says that “connections to these data sources do not count toward your index quota limits.” If you’re not familiar with Microsoft Search, then the data sources accessible via Microsoft Graph connectors are indeed a paid service. With Dynamics 365 we’re seeing the search capability bundled into the product, but for things like Azure Data Lake, Azure DevOps or third party services like Salesforce or ServiceNow, you’ll need a Microsoft Search paid license.

    If we were to use the paid features, how much would it cost? The required product can be found under “purchase services” in the M365 admin center – although not with any generic term like “search”, of course. You’ll need to know that SKU name is “Extra Graph Connector Capacity”. The minimum purchase is 1 million items and that would be around $1.000 per month, or in euro prices these sums below:

    If you had Microsoft 365 E5 or Office 365 E5 licenses, those would also accrue some index quota per each licensed seat, as shown here.

    Luckily we don’t need to make purchases since our tenant has Dynamics 365 Customer Engagement licenses in place. We can proceed to adding a new app under the Search & Intelligence data sources, by selecting the only supported MS app at this time: Dynamics 365.

    Next we get to choose the Dataverse environment. Except not all your environments may be listed here. Why? I did not discover a valid explanation for it in my initial tests. Neither the environment type (sandbox/production) nor existence of Dynamics 365 apps was the reason why environments didn’t always show up. Oh well, let’s proceed with what we’ve got and pick “Jukka’s Business Cloud” that contains the core CRM data in this tenant.

    If you’re wondering whether you could configure data from multiple Dataverse environments from your tenant to show up in the Microsoft Search results, the answer currently is: no.

    Once you’ve added the data source, it can take a while before the results will appear in the search experience. In our production tenant this took less than 30 minutes, in my personal tenant it’s been ~2h and nothing yet. MS says it can take 24 hours before the indexing is complete. Patience is a virtue – if you’ve got some to spare. I’ll just hop over to the production tenant now and explore the end user experience.

    How Dataverse records show up in Microsoft Search

    The users will discover the results from the previously configured Dynamics 365 connection when they access search either in Office.com, SharePoint Online or Bing. The UI to get to those results varies slightly, but the listed results from the Microsoft Graph connector seem to be identical.

    Obviously in Bing you need to be signed in and access the Work tab that switches you from the public web search to the internal results from your work tenant. In addition to all the regular results, plus custom search bookmarks like the one you see below, there will be a new tab to represent the “search vertical”. In the case of our production tenant, I named it “Business Forward” as it is the primary app used in our production Dataverse environment.

    I personally am more likely to leverage these search results from under Office.com. Here’s an example of what data is pulled from the Dataverse environment for a basic query term “forward”:

    We immediately see that the results cover both standard tables from CDM like accounts and contacts, but also custom tables like our work orders that are managed in Dataverse. We see a preview of fields from the matching records, which varies based on what columns are non-empty for each row in the corresponding Quick Find View.

    Configuration of the searchable fields is done in the Dataverse search settings, which the Microsoft Graph connector for Dynamics 365 then respects for each table. This means there’s very little to be configured on the Microsoft Search experience specifically. You really only decide A) which 1 environment to point it at, and B) what name do you want to show in the search vertical in Office.com, SharePoint, Bing.

    Dataverse in-app search vs. Microsoft Search experience

    Now that we have enabled the discovery of Dataverse data from within Microsoft Search, should that become the recommended entry point for all your business data searching needs? The answer to this is: through Microsoft Search you’ll only get a subset of search features that are available in a full model-driven Power App. Whereas with Microsoft Search you may well get close enough results, the detailed search capabilities are better within Power Apps native Dataverse search.

    If I enter a search term like “governance” into Office.com search bar, I’ll get a long list of results that I can not filter nor sort in any way. When I do the same search inside our Business Forward app (a model-driven Power App on Dataverse), I get the results tabbed per specific tables that allow me to narrow the query by record type. Also the filter pane gives access to owner and date filters, which get even more detailed as I select one specific table from the results.

    Another feature that can be a welcome addition is that the Dataverse search within the app UI covers only the tables included in that app. This can lead to search challenges in a larger Dynamics 365 environment that covers multiple different processes.

    For example, our production environment covers not only our CRM info on customers, sales etc. but it also hosts our internal IT Asset Management solution and its data. Now, if I search for “Nokia”, the results could include both the account management data related to Nokia Corporation as well as Nokia smartphones (from HMD Global) that our team members have registered as their IT assets. Sure, all results are filtered based on security roles and won’t reveal any data to unauthorized users, but anyone with broader access to Dataverse will also get broader, nonfilterable results with Microsoft Search.

    Sometimes you may get more results from Microsoft Search than from within Power Apps, because of the way documents stored in notes and attachments get indexed. I even discovered that the note record (annotation) which traditionally hasn’t had any user accessible table form in the native UI can actually be opened independent of the parent record. The form of a note is very nice and readable, providing a link to the related document:

    Things aren’t quite as awesome when it comes to attachments of email records. While these also open a form within a model-driven app, the attachment table form doesn’t offer any link to either the document or the related email message from where the file could be opened.

    The many faces of Dataverse search experiences

    It’s really awesome that we’re now seeing the mainstream search capabilities in Microsoft cloud services reaching into the domain of business applications with these out-of-the-box capabilities. The investments that Microsoft has made into Dataverse Search as a service that isn’t just for doing freetext search inside your CRM system is starting to pay off. Just like makers can tap into Dataverse Search from within Power Automate actions, now information workers can do simple queries into enterprise systems from within Office experiences.

    In its current state, with support only for Dynamics 365 customers and only a single environment per tenant, the Microsoft Search experience isn’t yet as powerful as it could become. Covering the Power Platform use cases where business data is managed in various environments and via specific custom apps would be a logical direction to broaden Microsoft Search with a true low-code platform story.

    The way I see it, the search experiences for business data managed within Dataverse are being developed in three separate areas:

    1. Free text search: building search indexes that cover several sources/tables, with multiple entry points (in-app, Office, flow, APIs), evolving into support for natural language queries and supporting conversational UIs (Teams chatbots etc.). This is the area where Dataverse Search and Microsoft Search are operating.
    2. Structured queries: building complex query criteria to filter the rows in a specific Dataverse table. “Show accounts with orders in last X months and no activities from members of sales team Y”. The Advanced Find feature and the FetchXML query language have traditionally covered this front. The modern advanced find experience is making these filters easier for casual app users to approach, while FetchXML is still alive (now available for download in the UI) and can help flow makers design complex queries more efficiently, for example.
    3. References: the next generation of “set regarding” features are aiming to broaden the traditional scenario of associating emails in Outlook with records from CRM. If the MS vision for Context IQ comes to life, we should be able to at-mention basically any record from a Dataverse environment and collaborate interactively on it via Loop components. Similar to the Microsoft Search initial limitations, it will be interesting to see how the lookup field experience can be optimized with machine learning algorithms when all records in a large tenant are behind a single @ symbol…

    Returning back to what I mentioned at the start of this blog post, as a solution designer it’s very important to understand all these different means through which users can search for your business application’s data. There’s a lot you can do by configuring the settings in Dataverse views to make the experience enjoyable for the user.

    Studying the Dataverse search documentation is a good start. However, it’s all just theory until you have some actual data and real user interfaces to test the usability of your search configuration with. What this means is that you’re unlikely to ever get all the settings right in the V1 release of your app. In practice the user experience for business data search requires attention throughout the lifecycle of your app. Microsoft will continue to change and expand the ways in which search queries and results are handled, so you better keep up with these new features and explore what configuration options are being introduced in the future, too.

  • Working with solutions in Dataverse for Teams

    Working with solutions in Dataverse for Teams

    For over a year now, we’ve been living in a world where Microsoft has two separate offerings of Dataverse. When it comes to the tools especially around solution management, there are certain differences app makers should be aware of.

    In this article I’ll go through the peculiarities I’ve ran into when building solutions in Dataverse for Teams (DV4T). But first, a few words on why you should care about the Teams based environments when building Power Apps.

    Positioning of Dataverse for Teams

    Understandably, not everyone is a fan of the Teams version of Dataverse. After all, it’s a cut-down version of the full Microsoft Dataverse that many have been using for Dynamics 365 and XRM scenarios for ages. However, there’s that one unbeatable feature in Dataverse for Teams that’s hard to ignore: it’s FREE!!! (*With capacity limits, though.)

    If you are building a complex business application for a specific audience, it’s almost always sensible to pay a little more and get the Power Apps Per App or Per User license to unlock the full Dataverses platform capabilities. But when you need an app with a casual usage pattern that applies to a huge number of individual users, sometimes it just ain’t feasible to go premium all at once. So, to avoid falling back to SharePoint lists, Dataverse for Teams a.k.a. DV4T can be a well justified choice from an architecture perspective.

    Just because you’re using Teams as a platform for your low-code apps doesn’t mean you don’t need to plan for ALM (application lifecycle management). A single production environment ain’t going to be sustainable in the long run for the more important business processes. There are good news and bad news when it comes to Dataverse for Teams in this regard.

    Let’s get this one bad news out of the way first: all of the APIs to programmatically interact with Dataverse are not available when working in a Teams environment. So, any pro-dev ALM processes that involves automation via DevOps pipelines and such is out of the question.

    That leaves us manual ALM, meaning export and import of solutions by a human being. Nothing wrong with that, since surely the vast majority of business apps running on the platform globally are developed and maintained this way. After all, there really isn’t a low-code specific ALM process available in Microsoft’s world today that wouldn’t require at least some pro-code developer skills in the delivery team.

    The secret life of solutions in DV4T

    When you build apps from within the Teams UI, you will never encounter the word “solution” in the app maker experience. The Power Apps app inside Microsoft Teams will instead show you tabs called “built by this team” and “installed apps”:

    How does one create new apps here alongside the already installed ones? By importing managed solutions into the DV4T environment. Of the three apps in the above example, IT Self-service has been imported as a managed solution package from another environment. Milestones is a sample app from MS with its own proprietary install experience, whereas Flow Approvals is just the raw infrastructure bits that get auto-provisioned when you create your first approval request in an environment.

    All the unmanaged components in this environment will be found under the “built by this team” tab. Looking at the source DV4T environment from where the above IT Self-service app has been exported from, here’s how its bits and pieces show up in that environment:

    When you click “see all” you’re taken to what looks a bit like the modern solution explorer in Dataverse, except it’s missing most of the features you’d expect to find there. When you consider for a moment that this experience is aimed at new citizen developers who aren’t really even aware of what “Dataverse” means, it does make a lot of sense.

    We’re not like them, though. We are advanced low-code app makers who know what we want to achieve with these tools. Our escape hatch from this simplified world is the “Open in Power Apps” button that can be found when selecting “Apps” from the tree view.

    We’re then taken into a special version of the Power Apps Maker portal that cannot be accessed directly from make.powerapps.com. This is because the DV4T environments do not show up in the environment picker of the usual Maker portal (they do on the Power Automate side, though). Time to add a browser bookmark for this URL, to keep you from having to always remember the curious navigation acrobatics required to land here.

    Anyway, at least we’re now in a place where the good ol’ solutions can be created:

    You’ll quickly notice that only a subset of the full solution explorer features are available here. I’ll next demonstrate how this can lead to some challenges in your app development tasks.

    Choices in a DV4T world with less… choices

    The IT Self-service app that was mentioned above is a fairly straightforward app with a few tables, a canvas app and a cloud flow. After getting the initial version ready in the development environment and preparing to configure it for another Dataverse for Teams environment, I ran into an interesting issue. This example reflects some of the “gotchas” you should be aware of when working without access to the full Dataverse tools.

    We can import solution files from the Power Apps experience within Microsoft Teams, too – even if the word “solution” is avoided. When importing my test app from here I encountered the error “import failed due to missing dependencies”. Clicking on the “show dependencies” button didn’t do anything at all.

    Switching over to the Maker portal UI for this DV4T environment, then initiating the traditional solution import process, I was able to see the that my solution package was apparently missing a Choice:

    Ah, the awkward naming choices that Microsoft has recently made with our beloved platform… Ok, I’m now switching back to the old terminology that makes more sense.

    So: I had added an option set field for my entity. However, it was a global option set, and now the solution package was missing the actual list of values that are stored within this separate solution component.

    How did I end up in this situation? I’m not actually sure. I did a test with a different app, to see whether it makes any difference if I create the table columns from the Teams experience (which is very citizen friendly in its Microsoft Lists type UI) or the Maker portal. It is different in the sense that Teams creates a local choice vs. the global choice that the Maker portal defaults to (as recommended by Microsoft’s own Docs). Still, the solution export/import worked without any dependency errors on both versions.

    No big deal, I’ll just add the missing component into the solution… Not so fast, Mr. Advanced Maker! Just because you’re working in a UI that resembles the full Dataverse solution explorer, doesn’t mean you have the same features at your disposal. Although Choices is one of the nodes that does show up in the tree view, you can’t actually find it in the “Add existing” menu:

    Those of you who work with full Dataverse know that the “Add existing” menu contains well over a hundred items. The official Docs on solution component types only list 90 values at the time of writing but MS is adding internal components there all the time. We sure don’t need most of them in DV4T, but this omission sounds a bit unfortunate.

    I had already done all sorts of debugging and hacking to find a way around this annoying limitation until I discovered a UI based way out. I saw that the existing Choices in the solution had the “Add required components” option behind the three dots. Unfortunately the Tables view didn’t include the same option. In the full Dataverse solution explorer we’ve got it in the main toolbar, but DV4T is different in this regard, too.

    Finally I found the place where the option does apply to tables, too: the all objects view. Success!

    It took a few hours for an experienced low-code developer to discover how to do what he wants to do in this simplified environment. It was still faster than replacing everything with a new column at this point, though. I had almost accepted the fact that I had simply clicked on the wrong navigation path somewhere in building the app and managed to break the boundaries of the DV4T sandbox. Luckily that wasn’t the case today.

    Solution publishers

    When you start adding items into a Dataverse for Teams environment via the Teams UI, they will get created under the CDS Default Publisher. The prefix for new solution components will be something like “crdc2”, meaning a random value that’s different in each environment. At least it isn’t the dreaded “new_” prefix…

    You can create a your own custom publisher when creating a new solution via the DV4T edition of the Maker portal, similar to how the full solution explorer works. As long as you create the items from under this solution in the Maker UI rather than the Teams UI, the correct prefixes get assigned to them. However, since you cannot create a new canvas app (or chatbot) via the Maker UI, this means your app schema names will always get the CDS Default Publisher prefixes.

    There are nice new features like the table editor in the Teams UI, which you might be tempted to use for quick editing of demo data while building your app. If you’ve created your tables from the Maker portal side, though, you won’t find them in the Teams UI:

    The reason for this is that the Power Apps app inside Teams will only display the unmanaged solution components for the environment’s default publisher. So, if you tried to follow solution management best practices and created your own solution publisher, the doors will be closed over on the DV4T native app building experience.

    Unmanaged, invisible solutions

    This leads us to another gotcha on how DV4T treats solutions that come from outside the current environment. We already saw the differences in how the managed solutions imported into the environment are listed under “installed apps” and unmanaged stuff is under “built by this team”. That’s how the Teams experience shows things, and as we’ve come to now understand, it isn’t always sufficient for advanced makers.

    When we do the equivalent of “switch to classic” in the Teams world and hop over to the Maker portal view of the environment, are things in their right places again? Well, not if you imported an unmanaged solution into the environment. Here, try and find the unmanaged IT Self-service solution from this list below:

    No matter how many times you hit refresh on that page, your imported solution won’t show up. All the imported components do exist in the environment, though. You can access them via the Common Data Services Default Solution:

    Why isn’t the solution visible then? Because Microsoft decided that you don’t need to see it, apparently. Deep down in the database all the solution metadata is stored. You can tell this from the fact that if you try to re-import the same solution, you’ll see a message “this version of the solution package is already installed”.

    The limitation isn’t technical then, just a UI level filter. Yet from a practical point of view it is very real in a DV4T environment. This is because all the standard Dataverse APIs are blocked, so using tools like XrmToolBox to work around UI limitations and improve app maker/developer productivity are out of the question.

    Closing thoughts

    Dataverse for Teams has capabilities for running quite extensive business applications (just look at the CoE Starter Kit for reference). Many internal low-code apps could benefit from having a proper relational database behind them, yet they may not be feasible to be implemented with features requiring Power Apps premium licenses due to their usage patterns. The current app packaging story of DV4T raises some concerns on how solid this foundation will be for applications that will need ongoing maintenance and development.

    Ultimately it would be best if we had a clear path for using the full Dataverse as our development environment, then publish the final app into DV4T for testing and production. As it stands today, the Canvas app editor experiences between the two environments are technically different. Also, it can be all too easy to “infect” your solution with some dependencies to features of full Dataverse that are going to block importing it into a DV4T environment, based on my experiences.

    Update 2022-10-20: Today I learned about one more gotcha with Dataverse for Teams that involves Power BI reporting. If you build your tables from within the Teams Power Apps UI, you won’t see a “publish all customizations” button. However, unless you publish your customizations, the TDS endpoint won’t pick up the choice column labels. You’ll only have the ID values but the “name” fields will be blank. So, as a best practice, always keep the full Power Apps Maker portal open while building Teams apps/tables and remember to publish your customizations from there – just like you always have in the XRM days.

  • Modern advanced find test drive

    Modern advanced find test drive

    The single most powerful end user feature of Dynamics CRM, XRM, CDS and Dataverse was always Advanced Find. Period.

    There, now that we’ve settled that argument, it’s time to move forward and see what life is going to look like when Dataverse no longer includes Advanced Find. Yes, there will still be the broad capabilities of advanced find, but I will refer to it without capitalization. This is in line with the fresh new documentation on a very important feature that aims to take the place of Advanced Find: modern advanced find.

    So modern you can barely see it

    Advanced Find was always its own UI in a separate popup window. This was perfectly in line with the Dynamics CRM 2011 UI – which was the last time there were any material changes to the visible features. Let’s have one last look at Advanced Find’s glorious Ribbon icon before we move on:

    OK, so this wasn’t actually the last time Microsoft touched the user experience for this feature. The CRM 2013 release did a complete overhaul of the application UI (I’d say a more visible change than Unified Interface even), which lead to the Advanced Find buttons getting either hidden or missing from parts of the app navigation. I had to write a blog post called “Finding Advanced Find in CRM 2013” to help out users who were worried that the feature had been removed from the product entirely.

    The modern advanced find that has been introduced as a preview feature in February 2022 takes the hiding game to a whole new level. You see, the entry point to modern advanced find is hidden within the global search bar. You have to 1) click the bar and then 2) click “search for rows in a table using advanced filters”.

    Be sure to not enter any search text into the bar, because that will hide this new option. Also, whatever you do, do not press enter after this text – otherwise you’ll be taken to the global search results page. There’s no way to “go advanced” from here, it’s a completely different search experience.

    Search “any” table

    A big new feature in the modern advanced find is what happens after you click on the new button within the search bar. (I already forgot the name of that button since it wasn’t anything as nearly catchy as “advanced find”, so I’ll refrain from scrolling up this post to see what it was and just move on instead.)

    You will see a sidebar saying “select a table to search”. Cool, just like in good ol’ Advanced Find! The difference you need to understand, though, is that it’s not a list of all the tables in the environment. Yes, it will likely be a longer list that what your Model-driven app’s sitemap navigation contains. This is because modern advanced find covers all the table’s that your app’s maker has chosen to include in the app module when working with the Model-driven app designer.

    There are pros and cons to this approach. The obvious upside is the ease of use for the casual data finder. He/she doesn’t need to confront the ever growing list of both standard and custom tables in a Dataverse environment (especially those with many Dynamics 365 apps installed in them). Things are quite different now compared to back when Advanced Find was originally designed for a simple CRM systems. Showing hundreds of cryptic tables isn’t a great UX to most users, so cutting down the noise of the underlying data model is understandable.

    How about those users who DO understand the data model, or even work in configuring and extending it? The advanced app makers, consultants and developers will most likely be frustrated by this limitation whenever they need to examine the detailed contents of an environment. Quite often I myself need to build new views and filters just to understand what data has been entered into the system (that has been designed by someone else).

    Sounds to me like there will be a growing demand for a “Super Advanced Find” type of a tool to be introduced in XrmToolBox. If you’ve already got a favorite tool for ad-hoc data exploration needs, be sure to leave a comment below.

    Query criteria remains the key criteria

    The one area where an advanced search feature in Dataverse simply has to perform well is the creation of complex query criteria across the relational data model of the environment. One of the most read articles on my blog has been Advanced Queries with Advanced Find, which illustrates both the possibilities of the tool as well as the demand for such advanced query criteria in real-life business scenarios.

    Luckily this is already included in the non-preview feature set of Model-driven Power Apps. The filter editor that you can find in any table view today is a very worthy replacement for what Advanced Find used to offer. Although I haven’t done a detailed comparison, I couldn’t easily spot any missing capability from the tools available for defining the view filters in modern advanced find vs. the classic pop-up query editor for Advanced Find.

    Just imagine if it was this easy to define the filter criteria for Dataverse tables on the modern automation side, too – not just views? Perhaps one day something similar will be made available for Power Automate cloud flows, based on what is being planned for SharePoint data sources in 2022 wave 1 (because priorities). Until then, luckily we have great articles and reference guides created by the community to copy-paste our “flow code” from while we wait for a GUI to arrive.

    Actually viewing the data

    Far too often I’ve seen people put all their effort in defining the correct query criteria and then being lazy when it comes to actually showing the results of that query. You could say I wrote the book on the importance of Dynamics CRM view design back in the 2013 era, so you can image how such user experience oversights have irritated me over the years.

    Smart choice of view columns and sort order are what defines the user experience outcome. Query filters are merely table stakes.

    Jukka Niiranen, in this blog post right here.

    As a data exploration tool, Advanced Find wasn’t always perfect, but us consultants learned to make the most of it. When needing to dig up data from columns that weren’t readily available in the system views, it was easy to just go and add all columns into an exploration view. Whereas when building more targeted we had two useful features in the “add columns” screen of Advanced Find: sort by name, sort by data type.

    Unfortunately this is where the modern advanced find is a little behind on the classic Advanced Find. Let’s look at what the column editor experience is like.

    First of all, the editor includes the very same modern stumbling block that you might have noticed when working in the Power Apps Maker portal and searching for table columns. When you open the “add columns” side pane, the list of available columns doesn’t cover everything that the table contains. Instead you are defaulted to a “Default” filter that’s easy to miss. Switch to “All” and you’ll finally see that field you wanted to include in your view.

    What exactly does “Default” mean in this context and how is it defined? No one can tell, not even Microsoft. It appears to be one of those good intentions in tidying up the menus of what should be a citizen developer friendly platform yet also serves huge enterprise CRM systems. A tough balance to get right, that’s for sure.

    One very nice improvement in the modern advanced find (as well as the Maker portal) is the ability to search for things while you are configuring those very things. Narrowing down the long list of available columns with a free text search term could be especially handy when you’re looking for information stored on a related parental ent… table.

    What could make such a feature even better? I’ve got one idea: extend the search index to include also the schema names. Believe it or not, the display names of f… columns can become quite misleading when you eventually need to adjust them for the places in the app UI where they can’t be customized (which just happens to mainly be: VIEWS!).

    Anyway, often the schema name may have a pattern that makes it easier to logically group columns, which in turn makes the app maker more productive as he/she finds them more easily when defining the views. Right now that’s something the modern advanced find doesn’t yet support. If you think it should, then go and vote for it.

    So, you’ve created a hundred and one views…

    If there was something that the classic Advanced Find feature really didn’t handle well at all, it was the process of managing the views created with it. The way how the Ribbon UI managed to both hide the feature and yet make it highly misleading at the same time was something… special.

    Compared to what we used to have in Advanced Find, the enanced personal view management now launched in preview is a thing of beauty. Changing the sort order and hiding unnecessary views are very welcome additions to how the power users will be able to take control of their business app UI details.

    The new view hiding feature is something to pay attention to:

    “Hiding a view is a way to personalize the view list and reduce clutter by making views not be visible in the view selector. A view may be needed for a specific purpose periodically or a view could be shared with you that you may not need it anymore. In such instances, hiding enables you to manage your view list by seeing only the views that are most important for you.”

    This probably won’t solve all the pain associated with view management as their number grows in “busy” tables used for different kinds of analysis, both via system and personal views. Yet it’s a simple, user-driven concept to offer personalization within Model-driven apps.

    Closing the Advanced Find popup

    We’ve known for quite some time that eventually the legacy web client popup with Advanced Find will not be supported anymore. This modern advanced find feature in 2022 release wave 1 aims to include the necessary features to allow deprecating a very central part of the product. It does a pretty good job and the general principles in modern advanced find are quite justified.

    Will I miss the good ol’ Advanced Find once the new feature is enabled and the icon to the classic experience is gone? Of course I will. Like with many of the modernization efforts around Power Apps as a business app platform, the new experiences always take away something that used to allow XRM veterans like myself to be productive. Often we get less information density in the screens, blocking the use of multiple tabs, slower loading times due to new API dependencies, more options hidden/missing due to simplification efforts…

    Enough with the complaining already! Things weren’t better before, they were just different. What used to be a single app in a single service is now a cloud where traditional boundaries are vanishing at an astonishing rate. Now everyone really can make apps, and there can be a thousand “XRMs” in an organization. Both the audience and the purpose of these tools is forever evolving – and so must their features.

    Update 2022-06-08: Microsoft has started to roll out an enhancement to the view filter editing screen. There will be a button to download the FetchXML definition of the view filters. Also, export to Excel will now honor the filter modifications done, without requiring the view to be saved. Just like in the good ol’ Advanced Find days then.😊

    Update 2022-08-02: As we get closer to 2022 Release Wave 2 when the classic Advanced Find button will be hidden from the UI for everyone (see Modern advanced find turned on by default), you should keep in mind that there is still a way to open the Advance Find page with the direct URL. You can (and should) install the LevelUp extension for Chrome/Edge/Firefox to have access to this dynamic URL. This will hopefully work for as long as Microsoft keeps the legacy web client page infrastructure alive.

  • How to control the lookup view columns for a customer field

    How to control the lookup view columns for a customer field

    It’s the little things in a user interface that can drive me nuts – at least when I keep running into them repeatedly.

    One such detail in Dynamics 365 CE apps / Model-driven Power Apps is the scenario where you’re creating a new contact record and linking it to a parent account. Filling in the lookup field gives a nice little preview of the matching records. Like this:

    Now, look at the first two results: aren’t they actually saying the same thing, but in different order?

    At least I have a hard time distinquishing which record I should pick when I want to link this new contact under the account Forward Forever Oy. So, why is row nr. 1 the right answer and row nr. 2 the absolutely wrong choice to pick here?

    The underlying dilemma is that this lookup field is a customer field (column). It can reference either an account or a contact record (row). It’s one of those non-simple types of lookups that Dataverse has contained since forever, thanks to it being originally designed for the purpose of being a CRM database.

    Now, in a B2B CRM scenario you would almost never want anyone to link child contacts under other contacts. Unfortunately, even after 2 decades of shipping a mighty fine CRM product, Microsoft still hasn’t considered it worthwhile to offer customer organizations a configuration option to force contacts to be linked only to parent accounts.

    Being an extensible enterprise business application platform, you can of course get a developer to write some JavaScript to change the default behaviour of the lookups that bother you. As for me personally, I always like to explore if there are no-code ways that would allow me to achieve a similar result without adding even a few lines of script into the environment for the future me / someone else to manage.

    In this case what I want to do is this: don’t show the Primary Contact field in the lookup view of an account. As we’ve seen, it can be highly confusing, since this very same contact itself can also show up in the list of results. For a contact, it’s very logical that the parent account should be shown in the lookup preview results. (and used as a search field). For accounts, the Primary Contact value would likely be irrelevant in 99% cases when looking up records.

    To give Microsoft some credit on the UX front, they have invested in developing a new Advanced Lookup feature that gives the end-users more filtering options to find the right record to link to. Opening up this modal dialog also gives us a way to examine why the previews behave the way they do.

    Initially this lead me to scratch my bald head even more. Based on what the documentation says about lookup field behavior, I shouldn’t see the Primary Contact field value in the dropdown preview of the lookup, as there are columns like Account Number before it in the view.

    “For system lookups that allow for multiple table types, the first two columns of the table lookup view are shown.”

    It turns out this is not true anymore. To demonsrate the real behavior, l added an Account Number “111” for Forward Forever Oy account record. This is what happens with the lookup preview:

    Ah. The current lookup in the modern Unified Interface is so darn clever that it shows the first non-empty column from the lookup view.

    The solution is simple then. You can just add columns at the beginning of the table’s lookup view that are always going to have data. These will then push further left the lookup fields that aren’t relevant in the dropdown preview. This means you can still keep the other fields visible by default when opening the Advanced Lookup dialog. Even that confusing Primary Contact field can be left there, just in case we need it.

  • Find contact’s LinkedIn profile with Power Fx & custom command bar button

    Find contact’s LinkedIn profile with Power Fx & custom command bar button

    So far in my experiments with Power Fx and the modern commanding preview in Model-driven Power Apps, I’ve worked with changing record status and cloning a set of existing records. This time I’ll create a custom button for another common CRM scenario: viewing the contact’s LinkedIn profile information.

    In our internal CRM application (Business Forward app) I’ve earlier added a custom field “LinkedIn URL” for contact records, which can be used for storing the reference to the contact’s profile. Not all contacts will of course have this field filled, which is why it’s a common need to go and search for the information on linkedin.com. Could we save a few clicks in this manual process with a simple button? We certainly can!

    This scenario is straightforward enough that you could well use it as your first custom command bar button in your Model-driven Power App or Dynamics 365 CE app.

    Button 1: LinkedIn Search

    Open up your Model-driven app in the preview editor that allows you to access the list of pages in the app. Then for the contact page select the three dots and click “edit command bar (preview)”:

    Select the contact’s main form as the target and the command designer will open. Pick a suitable spot in the existing list of commands and add a new button called “LinkedIn Search”. You can choose the out-of-the-box “LinkedInLogo” icon for it.

    We will want to have a visibility rule that will conditionally show/hide the button, based on whether the contact’s custom text field “LinkedIn URL” contains data or not. The IsBlank function will return a true value when the field is empty, so we can directly input that into the Visible property of our button:

    IsBlank(Self.Selected.Item.'LinkedIn URL')

    Next, let’s go over to LinkedIn website and study how their search actually works. There is of course the global search box in the top navigation bar, but when looking at the search results page we discover additional options for applying specific filters.

    This is the faceted search page, which allows us to specify that we are interested in finding people (1) rather than companies or content. Clicking on “all filters” (2) reveals an even more granular search experience, allowing us to enter keywords (3) for fields like “first name” and “last name”.

    If we were to search for Satya Nadella as an example, the result page URL would turn into this:

    https://www.linkedin.com/search/results/people/?firstName=satya&lastName=nadella

    This is a great scenario for using Power Fx to manipulate a text string for our app’s purposes and inject some dynamic values in there. We can now start designing our OnSelect formula needed for our LinkedIn Search button. It should do these things:

    • Open the LinkedIn search page in a new browser tab
    • Insert the contact’s name information into firstName and lastName query parameters
    • Ensure that any spaces in the name fields don’t mess up our URL validity

    The Launch function will take care of opening the browser tab. For joining the static and dynamic parts of the URL together, we use the string concatenation operator “&”. As for handling any non-URL friendly characters, we can use the EncodeURL function and pass the contact’s first & last name fields to it.

    Let’s save and publish these button configurations, which will generate a component library called “OurAppName_DefaultCommandLibrary” into our solution. Be sure to publish all customizations for your solution and also hit refresh a few times on the Power Apps application screen to make things come alive. (Note: you may also get an error the first time you click a custom command bar button, at least for me that happens every time, but then clears away.)

    Now when we open a contact record that doesn’t have a value in the “LinkedIn URL” field, the new button should be visible. Clicking on it will open up the LinkedIn people search page with results where the first & last name match the contact’s information:

    That was pretty easy!

    Button 2: LinkedIn Profile

    It would be nice if the next user of our CRM system wouldn’t have to browser through the people search results page to repeat the same query. Unfortunately there’s nothing we could grab with Power Fx from the first user’s new browser tab automatically, so we’ll need to ask the user to copy the URL and past it into our contact’s custom field.

    To remind them about the importance of updating CRM contact data, let’s show a notification bar that’s waiting for the user once they return back to the Power Apps tab:

    With this additional step, the OnSave event of our button 1 will therefore have two actions: launch and notify.

    Launch("https://www.linkedin.com/search/results/people/?firstName=" & EncodeUrl(Self.Selected.Item.'First Name') & "&lastName=" & EncodeUrl(Self.Selected.Item.'Last Name'));
    Notify("Found a matching LinkedIn profile for " & Self.Selected.Item.'First Name' & " " & Self.Selected.Item.'Last Name' & "? Please update the LinkedIn URL field for the contact. Thanks!")

    Now we can move on to button number 2. If the contact record already has a LinkedIn URL, we’d of course want to skip the unnecessary search part and open the profile page directly.

    Let’s add another command bar button called “LinkedIn Profile”. The OnSelect property will be very simple this time: we just pass the Launch function a parameter containing the value of the “LinkedIn URL” field.

    This second button should be visible only when the first button is not, to cover the two possible scenarios of the URL field having a value or not. While we could have conditional logic in our Power Fx to handle either scenario with a single formula, we don’t have the possibility to dynamically change the display names (or other properties) of the command bar buttons. So, two buttons with a similar feature is the approach we need to take.

    How do we reverse the visibility rule? For some reason adding a simple exclamation mark before the function doesn’t work as a negation operator in a custom button Power Fx formula like it would in a Canvas app formula, so I wrote the Visible formula of the new button in a less pretty format:

    If(IsBlank(Self.Selected.Item.'LinkedIn URL'), false, true)

    Save, publish, hit F5 a few times & wait. Now let’s open a contact record that already has the URL field filled. We see that instead of “LinkedIn Search” the command bar button now says “LinkedIn Profile”. Clicking on it takes us straight into the correct profile page:

    That’s all there’s to it!

    Closing thoughts

    I would have actually wanted to write a bit more complex formula than this, but there are a number of limitations in the current modern commanding preview feature that have blocked my other scenarios.

    For instance, there doesn’t seem to be any way currently to access the parent account information of a contact. Since under the hood it’s a customer lookup, meaning a polymorphic relationship that can reference either an account or contact record, we’d need to use the AsType function. That doesn’t appear to be supported for command bar buttons, so I couldn’t even grab the name of the company to be injected into the LinkedIn search URL to get better matches. Not a showstopper, but definitely something to add into the button once it becomes supported.

    Due to its preview status, some things in custom commands that used to work earlier aren’t working anymore. The record cloning example I built three weeks ago used the Set function, but today that has been removed from the modern commanding preview supported functions. Now that we can’t use any variables, the resulting Power Fx formulas would have to be constructed via the With function. In the custom button scenarios I had originally planned to build, I can’t easily see how to build them without variables and not causing a headache for myself, so they are currently on hold until the feature matures a bit.

    One question that comes to mind when building this example LinkedIn button is “why do we have to do it ourselves?” After all, Microsoft owns LinkedIn, so it would kinda make sense if there was some built-in integration at least between Dynamics 365 and LinkedIn to cover such simple profile matching scenarios. In fact, back in the April 2019 release wave an out-of-the-box integration for Dynamics 365 contacts was initially promised, but later cancelled:

    Sure, there is the Relationship Sales package available at $162/user/month for those who need a hardcore social selling tool with Dynamics 365 Sales and LinkedIn Sales Navigator. Alternatively, there’s the “$0” feature built into Outlook that will show the matching LinkedIn profile previews for the contacts stored in your address book.

    In between, there’s… nothing. It was interesting to reflect back on this topic by reading my own blog post from 2013 on LinkedIn, Dynamics CRM and Social Selling. The world has of course changed a lot from those days, with data privacy concerns as well as legislation affecting the ways in which this type of contact information can be exposed for commercial purposes. All the old iFrame options and LinkedIn APIs have been shut down, meaning pretty much all you can do without the paid LinkedIn plan is to create a low-code shortcut button like the one I showed in this article.

  • Clone records with Power Fx & custom command bar button

    Clone records with Power Fx & custom command bar button

    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.

  • Reopen tasks with Power Fx & custom command bar button

    Reopen tasks with Power Fx & custom command bar button

    The preview feature for customizing command bars in Model-driven Power Apps is one of the most exciting examples of how the converging app types allow doing more with less code. Instead of having to learn how to write JavaScript, the low-code app maker can now leverage the Power Fx language familiar from Canvas apps development (and of course Excel formulas) to add custom business logic for command bar buttons.

    This preview was launched late July, but up until now I hadn’t come across a need to use it in an actual Power App. Today I encountered a familiar platform limitation that I though would be a great opportunity to try the new Power Fx based commanding in practice.

    The missing feature in Power Apps activity management

    The scenario is this: for any “normal” table in Dataverse, there’s usually the possibility for the user to change the status not just from active to inactive but also from inactive back to active. For example, re-activating an inactive account is a feature available natively in the command bar:

    The story is different when working with activity tables. Let’s say we have a task record that we’ve closed as completed but would want to append with further information. Hmm, where’s the “activate” button on this form’s command bar…?

    It’s not there. Activities like tasks, phone calls, appointments aren’t something you can easily reopen. This has been the situation for as long as we’ve had Dataverse / Common Data Service / XRM / Dynamics 365 / Dynamics CRM. It’s not that the user wouldn’t technically be allowed to perform the reactivation. There just isn’t a feature that would allow you to click on a button and start editing an activity that has already been closed.

    The traditional no-code way for making this available to the user would have been to create an on-demand XRM workflow for them to run. A more advanced option would have been to create a JavaScript web resource and use the Ribbon Workbench to add a custom button for the user to click on.

    Yeah, the Ribbon was cool in 2011, but let’s see if we would have a better way to achieve this with Power Apps functionality now in the year 2021.

    Adding a command

    The first thing we need to do is to locate the new command designer. Currently this lives inside another preview feature, the modern app designer. We can launch this designer when looking at the options for editing a Model-driven app module inside a solution:

    In this new world the command bar is a component found from under a page in the app module. So, let’s look at the page for the task table and choose “edit command bar”:

    We need to keep in mind that we’ve got 4 different command bar locations to choose from. All of them are potentially relevant for such a generic feature, but to keep things simple we’ll focus on the main form command bar. This means that when we open a task record into a full window (or Main Form Dialog modal window), we want to see our new custom button there. It’s better to start from a command specific to a single record rather than any of the views where a command might need to apply to a number of different records from the same table at once.

    In the command designer window we can add a new custom button. The simple no-code parts are the visual editing experience of giving the button a label, icon, tooltip and dragging it into the suitable relative position in the existing command bar for the task table. Once we get to the low-code part of writing the Power Fx formula to perform an action for this button, it’s a good idea to pause for a moment and think about what elements we need to work with.

    First of all, what we want to do is to update the current record (rather than creating a new one). We need to use the Power Fx Patch function to accomplish this. The documentation gives us an idea of how to set the target object (Self.Selected.Items), but when it comes to the actual fields to be updated, we’re going to need to do some research on how activity tables in Dataverse behave.

    An important detail to understand is that the record status isn’t something that you just set with a single field value. It’s a combination of the Activity Status (statecode) and Status Reason (statuscode) that need to be aligned in order for the status change to go through succesfully on Dataverse side. A combination of Power Apps Community posts and the Dynamics 365 Customer Engagement developer docs for the task EntityType is needed here to figure out what values your Power Fx formula should use. The end result is:

    Patch(Tasks, Self.Selected.Item, {statuscode:'Status Reason (Tasks)'.'In Progress',statecode:'Activity Status (Tasks)'.Open,percentcomplete:0})

    Due to the funny data types for choice columns in Dataverse (formerly option sets in CDS / XRM), we need to reference the available values for a specific choice, as seen above. Now, this gets us to the question of “how do I make sure my command bar formula is correct?”

    Unlike with the traditional Canvas app Maker studio, you can’t easily run the formula on a test record and see whether you get an error or success. Which is why I very quickly proceeded to creating a dummy Canvas app to validate my Power Fx formula against a real record in Dataverse to see the results in action. I’d recommend you do the same for any custom command formula that you’re not 100% sure to work when added as an action onto a button.

    OK, looks like we’ve got the status change part nailed down. But what’s with that “percentcomplete” value in the formula? It has to do with how we control the button visibility later on.

    Setting command visibility

    While we can get the job done by just adding a button that will perform the action missing from default Power Apps / Dynamics 365 activity forms, we should also pay a little more attention to the user experience. In this scenario, the action for reopening a task isn’t going to be relevant for any task record that has not yet been closed. To make sure we don’t create unnecessary clutter in the UI, let’s hide our custom command from records that are not valid targets for its action.

    In theory we should be able to accomplish this by adding a visibility formula for our custom command that reads:

    Self.Selected.Item.'Activity Status' = 'Activity Status (Tasks)'.Completed

    This should be true only when the task record we’re looking at has been closed as completed. Yet it doesn’t work. Even though Mr. Ribbon Workbench himself, Scott Durow, uses this structure in his example of a “set visibility” expression, it didn’t produce the desired results in my app.

    How about the Microsoft docs then? Well, they claim that the formula to control visibility based on record data should be something like this:

    //Button will be visible for accounts with Account Rating > 20
    Self.ThisContext.SelectedItem.'Account Rating'>20

    Unfortunately “Self.ThisContext” isn’t something that the intellisense feature in the command designer editor recognizes at all, so we’re kinda stuck here. Unless we can figure out a way to identify the closed status of an activity record without referencing those pesky choice fields.

    Let’s do what an experienced XRM customizer would try and launch Advanced Find to explore all the column values in our task table rows to look for clues. A-ha! There we have it! The system apparently populates the Percent Complete (percentcomplete) integer field with the value 100 whenever the task status is changed to completed:

    Let’s use this information to design an alternative formula for our custom command’s visible property:

    Self.Selected.Item.'Percent Complete' = 100

    Now we only see the “Re-open” button on task forms where the record is in completed status. We should make sure that upon reopening the task we also set his value back to less than 100, which is what we’ve already got in our action formula.

    Notification to end user

    We now have functionality in place for both the action we want to perform and when we want it to be visible in the command bar. To add more polish into the user experience, we could take advantage of the confirm function and show the user an “OK / Cancel” confirmation dialog box before the action to reopen the task is performed.

    The only problem is: it doesn’t work. Once again, the intellisense feature for the Power Fx formula refuses to acknowledge that “Confirm” would be a valid function. Saving it into our action formula will not produce any visible results. OK, we need to keep in mind that modern commanding is still in preview, so let’s give Microsoft some time to fix these issues before using them in our production apps.

    Thankfully the notify function does work with custom commands already today. We can add this line into the action property of our button:

    Notify("Task status has been set to Open - In Progress.")

    When the user then goes and clicks on the button for a completed task, the experience will be the following:

    Showing the custom notification bar in the same location as where the default system message “Read-only – This record’s status: Completed” would normally be is actually a pretty nice experience. The user probably can’t distinguish this from a native command behaviour.

    Custom commands in grids

    With similar steps we are able to also make commands available outside the single record form. Now, the big difference here will be that instead of always being sure we have exactly one record to work with when on a form, grids provide users the opportunity for multi-select. Which can be a bit of a problem, since at least based on the current preview documentation for modern commanding in Model-driven Power Apps, I don’t know how the Power Fx formulas should be structured to loop through a record set.

    Let’s therefore build something that we know will work, based on the above example. On our custom table “inspection” we have a subgrid of tasks related to that parent record. To streamline the process of marking the tasks completed, we can add a button on the subgrid command bar that allows us to change the status without leaving the main inspection form. Here’s what it will look like:

    I’ve added a “Complete” button to the subgrid view commands for the task table. The action part is pretty much the same as before, only we’re setting the status & status reason to “Completed” this time:

    Patch(Tasks,Self.Selected.Item, {statuscode:'Status Reason (Tasks)'.Completed,statecode:'Activity Status (Tasks)'.Completed})

    As for the command visibility, I want to check that A) there is only a single record selected and B) the status is not “Completed”. We’ll use the same workaround with “Percent Complete” field as before and include a CountRows function:

    If(CountRows(Self.Selected.AllItems) = 1 && Self.Selected.Item.'Percent Complete' < 100, true, false)

    Now the command is ready for use in the subgrid. The visibility rule does work, although there seems to be a bit of a caching delay in determining the status of the chosen task. If immediately after closing a task as completed we go and select the same row again, the “Complete” button will still show there.

    Although the clicking on a command will perform AutoSave, it is run before the command itself is initiated. After we perform a manual refresh of the form, the button is correctly hidden for the recently completed task. Just a minor detail you should be aware of when testing your custom commands.

    Conclusion

    The modern commanding feature looks very promising. If you have been using Power Fx in Canvas apps, building the action and visibility rules in the command designer isn’t that big of a leap. Sure, it is low-code rather than no-code, meaning we’ll need a larger number of documentation samples to understand how the properties and functions can be used in the context of an app command bar specifically. However, it’s a much safer sandbox for app makers to work in than full blown JavaScript, which one of the reasons it makes sense for Microsoft to invest in developing their programming language for low-code.

    Ever since The Ribbon was introduced in 2011 (and later evolved into a Command Bar), I’ve always felt like it has been a missed opportunity for optimizing the user experience of business applications built on Power Platform / XRM. The barrier for modifying and extending it, even with awesome community tools like the Ribbon Workbench, has simply been too high for getting many of these nice-to-have features implemented in real life customer projects. The native command designer tool and common Power Fx logic can definitely help in lowering this barrier.

    At the same time, we need to keep in mind that the modern commands don’t yet replace the classic commands from the RibbonXML era. Common requirements for usability improvement like hiding irrelevant default buttons isn’t yet supported. Check out Scott’s article on Ribbon Workbench vs. Power Fx Command Buttons to understand what is & isn’t available in Power Apps modern commanding.

  • One-to-one relationships and forms within forms

    One-to-one relationships and forms within forms

    There’s no such thing as 1:1 relationship in Dataverse, and hence your Power Apps Model-driven apps or Dynamics 365 Customer Engagement apps can’t directly have such a data model. Only 1:N (one-to-many), N:1 (many-to-one) and N:N (many-to-many) relationships are available between tables, be it standard or custom ones.

    In practice, even the N:N relationship doesn’t actually exist in the database. While the Dataverse table configuration UI allows you to create this relationship type, it actually consists of a hidden intersect table and two 1:N / N:1 relationships that connect the actual tables together (see Dataverse table relationships documentation). Seasoned XRM professionals may even discourage the use of native N:N relationships, as you lose some control and visibility to the relationship due to its hidden nature.

    Just because it’s not available in the platform, doesn’t mean there aren’t many real life business scenarios where a requirement to have exactly one record per a record in another table. (OK, “rows” in the latest Dataverse terminology, but I prefer the business process lingo where “record” still is more appropriate.) Also, like with N:N relationships, just because it’s not directly possible to create one, doesn’t mean we couldn’t build the required functionality by using the no-code tools in Power Platform.

    In this blog post I’ll demonstrate not only how to create a 1:1 relationship but also how you can offer a pretty nice user experience for working with related records – thanks to the new Form Component Control feature. I’ve covered the feature details in an earlier blog post (“Relational data on Model-driven forms, part 2: Form Component Control”) so please refer to that for more info.

    Why would we need 1:1 relationships?

    From a theoretical data modelling perspective, you probably shouldn’t be splitting data into multiple tables if there is only a single match expected from either side. On a practical level there can be reasons why it makes sense to not cram everything into a single table, though.

    A common source of such requirements are the restrictions of access rights to data. Let’s say that the contact information of a person needs to be widely available to users of the application for various purposes (billing, marketing etc.). However, this contact also happens to be a patient, with details about his or her medical profile being recorded into the same system. Only the doctors should have access to this data. A single contact will match a single patient record (or none, if it has been created for other purposes). If these are in two separate tables, granting access rights can be easily achieved via standard Dataverse security roles: everyone sees the contact table data, but only doctors see the patient details.

    “Couldn’t we just use field level security to hide the confidential stuff?” We could, but you have to evaluate whether the approach will really scale to how the system will be used. You see, in addition to security we’ll also need to consider if we’re overloading a single table with too much data. There are hard limits of the maximum number of columns that SQL Server supports for a single table. Thanks to the value-add provided by Dataverse, adding one column into the data model can create many columns in SQL. This means you don’t have anywhere near the 1024 columns per table at your disposal. Also, if you’re working with a standard CDM entity like contact, there will already be close to 300 attributes taking up space before you extend the data model for your specific needs.

    I was recently working with a customer that is planning to use Dynamics 365 Customer Service for managing all their service requests in every department they have. This will mean that tens of different types of services will be creating case records into the system. The amount of service specific information that must be available to be captured on case records is easily hundreds, if not thousands of fields. Adding all of these to the case (incident) table wouldn’t be feasible, so instead the solution architecture was designed to incorporate “service detail” tables specific to each service. Each case will have one (or zero) of these records, so it’s a 1:1 relationship between the standard case table and these custom service detail tables.

    Establishing 1:1 in the data model

    In the scope of our example, the data model will consist of these main tables:

    • Service 1, Service 2, …, Service N: parental record under which the cases will be created. Think of these as service contracts that a contact person can have for one or more services.
    • Case: the standard Dataverse / Dynamics 365 table, with lookups to all of the aforementioned Service tables. No other service specific data is stored here.
    • Service 1 Detail, Service 2 Detail, …, Service N Detail: service specific information that should be found from under each Case, depending on which service it applies to.

    Just like the N:N relationships in Dataverse consist of two 1:N’s, the same applies to our manually created 1:1 relationship. Only this time we’re not going to need an intersect table, rather we’ll just link the two records together via the relationships like this:

    The Case record will be parental to the Service Detail record, but at the same time the Service Detail will be the Case’s parent. These will appear just as two custom relationships under our table:

    Next, we’ll want to ensure that there is always one and only one Service Detail record for a case – IF the case is related to the delivery of the specific Service. Furthermore, we’ll want to get the Service Detail created automatically immediately after case creation, so that users can start entering data on it.

    The real-time requirement rules out Power Automate that is asynchronous by nature, so we’ll use the classic XRM workflow engine instead. There will be two levels in the automation:

    1. When a Case record is created, check which of the many Services it is linked to and create a record in the corresponding Service Detail table (establish 1:N relationship).
    2. When a Service Detail record is created, update its parent Case with a reference that sets the Service Detail to also be the parent of that Case (establish N:1 relationship).

    Workflow 1 looks like this:

    It will then in turn trigger workflow 2:

    Notice that we have a check in place that stops the creation of a Service 1 Detail record if one already exists for the Case. If the lookup to Service 1 Detail is empty, we put the reference to our newly created record there and establish the 1:1 relationship.

    Working with 1:1 data in the user interface level

    This is where the Form Component Control comes in handy. In short, the control is meant to allow both the display and inline editing of a parental record’s form, embedded inside another form. An example of the standard data model use cases would be to show the fields of a the customer contact on a Case form and allow the service representatives to update them without having to open the actual Contact form.

    It works in our 1:1 scenario, whereby we can edit the Service Details fields directly on Case form. The reason is that not only is the Service Detail a child record of the Case, it is also the parent – thanks to what we’ve just built above.

    You’ll find the explanation of how to use Form Component Controls in my earlier blog post. For now you need to do the configuration in the legacy Solution Explorer side, by editing the form and setting one of the lookup fields to be rendered as Form Component Control:

    Now when we create a new Case record and have the Service 1 lookup value populated, after the first save the user can immediately continue to fill the Service 1 Detail values right within the same Case form:

    The beauty here is that for the user who’s working with a Case record, they won’t need to know there are two different Dataverse tables used for storing the data. Both the Case record details, Service 1 Details and even the Contact record details are all editable on the single screen. The world looks flat, regardless of our data model with several relationships configured behind the scenes.

    Conclusions

    Dataverse offers you plenty of configuration tools to get creative with both the data model and the UI in Model-driven Power Apps. While the standard hierarchical structure of parent-child records and table (entity) specific forms is the most common pattern, there are alternatives that may be useful when faced with more complex business requirements.

    Dividing the business data into multiple tables with 1:1 relationship may sometimes be perfectly justified, to accomodate the security and data storage requirements. The user interace of Model-driven apps today offers great tools like the Main Form Dialog and Form Component Control to simplify working with proecsses that span across different tables in the underlying database.

    If you’d like to see Microsoft implement a native one-to-one feature for Dataverse, please vote on this idea.

  • What will Power Fx mean for Model-driven Power Apps?

    What will Power Fx mean for Model-driven Power Apps?

    By now many of you will have read about the announcement Microsoft made at Ignite 2021 on their “low-code programming language for everyone”. I’ve written about the wider role of Power Fx in the high level Power Platform story in the Forward Forever team blog, which will give you all the basic information on what/why/how/when:

    Recently I also recorded a quick 10 minute video discussion I had with Julie Yack from 365.training where I had a chance to verbally express some of my thoughts on Power Fx:

    I’ve personally started my low-code journey with Microsoft Business Applications back when the concept of XRM was first promoted, which is why most of my hands-on experience with Power Apps lies firmly on the Model-driven side. Power Fx as a formula language originates from a time when the two app types (Canvas & Model-driven) were still completely separate product offerings. If you’ve always built Canvas apps, there’s essentially nothing new for you in Power Fx. If you’ve only worked with the Model-driven business apps (Dynamics 365), there will be plenty of changes ahead.

    In this blog post I’ll share some initial thoughts on how I see the arrival of Power Fx potentially affecting the way Model-driven Power Apps are built.

    Why Model-driven needs Power Fx

    There has traditionally been a clear divide between no-code and pro-code tasks when building apps the XRM way – meaning customizing and extending Dynamics 365 Customer Engagement apps in most cases. You’ve had a GUI for clicking your way through a variety of configuration options the application platform offers, to perform tasks like:

    • Define entities, fields and relationships
    • Determine the layouts of your forms, sitemaps and views
    • Design business logic via workflows, business rules, BPF

    Our reliance on the almighty mouse pointer as the sword with which we fight our way through a battle field full of fierce business problems might be considered a weakness. Sure, we’ve often managed to emerge as the no-code hero who’s been able to tackle a business requirement most bystanders would have expected to require custom code. Yet there’s frequent frustration to be experienced when the particular configuration option we would have needed was not present in the graphical user interface (GUI).

    Such problems don’t need to be very complex to become unsolvable. All it takes is for the specific function to have fallen outside of the scope of features that the Microsoft product team has managed to squeeze into the platform’s built-in toolkit. Sometimes you might find a community tool that generates the required JavaScript for you, or a custom workflow activity comes and extends the built-in actions to let you implement that specific calculation your customer needs.

    Yet it remains mostly a black box for the non-pro developers: the next time you run into a similar limitation, you’re likely to again need help from the pro-devs to move forward. Venturing beyond the GUI tends to always push us into uncharted territory.

    What the announcement of Power Fx promises is far more empowering. “Formula based Power Apps Model-driven customizations” sounds like you could actually get a peek of the underlying logic layer in text format rather than a finite list of picklist options to choose from. At the same time you probably would not need to assume responsibility of any software code implementation details. The lower level of how the conditions defined by your formula are actually met by the big computer running in the cloud would not be your concern.

    This aligns with the promise of declarative programming:

    The maker describes what they want their logic to do, not exactly how or when to do it. This allows the compiler to optimize by performing operations in parallel, deferring work until needed, and pre-fetching and reusing cached data.

    Power Fx documentation on the language’s design principles

    For me, the big reason to steer clear from copy-pasting JavaScript snippets from websites and tweaking them to be used in business applications built for customers is that I’m all too aware of what I don’t know. While I can often read what the specific scripts are doing, I don’t fully understand why they work vs. why they would not work under different circumstances. It would be far too easy for me to accidentally create something that breaks in the near future – not because of a bug in Microsoft’s code but a bug in my code.

    Formulas that are written in Power Fx (today only in Canvas apps) are very different. Sure, they can be very complex as well, but there’s no knowledge required from outside the domain of Power Apps. If I pick a tutorial video from Shane Young’s YouTube catalog that explains the usage of a particular function in Canvas apps, most of the time I get it. I can immediately adapt it for my own scenarios, and get helpful Intellisense error messages from Power Apps Maker Studio when there’s something missing from my function (well, not always helpful messages, but at least an indicator of “keep trying”).

    Taking the step to writing your Power Fx formulas instead of just clicking around the GUI helps in unleashing your creativity in app design and significantly expanding the realm of what’s possible. There’s also big potential in improving the efficiency of the app building process when you can copy-paste text based formuals instead of having to repeat a series of clicks in the MS admin portals that seem to only get slower and slower as years go by…

    Why Power Fx may be scary for Model-driven app makers

    Canvas apps have been promoted as the way to create “pixel perfect experiences” tailored to the end users’ specific requirements and needs. This is a completely different starting point for low-code app creation than the traditional business apps in CRM style projects where you are mostly customizing an existing application. On a high level, everything will look & behave pretty much the same way every time.

    I’d argue that many of the limitations in Model-driven apps are in reality a safety net for the no-code app makers. Back in 2019 I did a presentation on the topic “Canvas apps for the Model-driven mind”, which describes the kind of leap that is needed both on mental and practical level for an XRM pro to turn into a modern Power Apps app maker. Power Fx will now play a big role in this transformation.

    The relational Dataverse data model that sits behind every Model-driven Power App largely dictates how the app UI will function. As a result, there’s not too much creativity required nor allowed in creating the visual side of your business application. Navigation and Command Bar buttons will be generated for you, and you can rely on them being constantly presented to the user. Everything’s responsive out of the box, too.

    The other side of Power Apps that’s not built on relational data structures and predefined UI grids is the world where everything runs on Power Fx. In Canvas apps the buttons don’t exist until you drop them on the screens and they do absolutely nothing until you add a piece of Power Fx text into the OnSelect property. Any dynamic behavior that’s desired for the app’s controls (visibility, size) must be expressed in Power Fx for their respective properties.

    Building Canvas apps doesn’t require you to have programming skills, but it does quickly push you much closer to the experience of custom app development. You’re no longer merely a system customizer like in Dynamics 365 scenarios, now you’re an app maker. The whole purpose of the low-code tooling is that it blurs the lines between traditional developer roles, thus powering the new “fusion teams” that may share many of their tools – and now also the programming language.

    As a result, it becomes less obvious what you can & can’t do without custom code extensions when building Power Apps. Configuration expressed as text based formulas can be vastly more powerful than GUI based options, allowing the most creative makers to achieve functionality that others might assume to be impossible with the available tools.

    Journey towards “just Power Apps”

    Back in Summer 2019 Microsoft revealed their long term roadmap on how the two types of Power Apps would eventually converge into one. We haven’t yet seen much concrete changes on how Model-driven and Canvas could coexist within a single app, apart from the embedding feature. The original plans for launching custom pages as a “canvas” within Model-driven apps were removed from 2o20 release wave 2 and no new target dates have been announced for it.

    There is likely a wealth of moving parts in this puzzle that have dependencies between each other, so it’s no wonder this merging of Model and Canvas apps is taking time. Power Fx is a clear step towards making the new app building story consistent. Plenty of new functionality is likely to be needed, though, as you’re not simply replacing one programming language with another. Rather it’s the case of expanding bespoke GUI tools with a low-code programming language that can also be used to express the same logic.

    Exactly what the new Power Fx based “form and commanding customization in Power Apps” promised at Ignite will consist of remains to be seen. Calculated columns defined with Power Fx are much easier to grasp. The same goes for the conversion of Business Rules into Power Fx formulas, since already today the Text View in the editor looks a bit like these.

    Making that If-Then-Else box editable would be a simple step. However, turning the actions in Business Rules into something that you could define with Power Fx and apply on a Model-driven form will surely require some more platform unification work. Show/hide fields, set required, prompt for errors… They all sound straightforward from a logic perspective, but how will such concepts be extended to cover not just Canvas sceen controls but Model form controls is going to be interesting to watch.

    This is probably why Business Rules are in the same “future roadmap” bucket as the Power Automate flows for receiving Power Fx coverage. It’s not yet easy to envision how the declarative logic of Excel that Power Fx tries to follow wherever possible is going to be applicable to these more imperative scenarios. What would a flow written in Power Fx look like and is it really going to be easier for app makers to grasp than the current GUI?