<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[blog.PowerSnacks]]></title><description><![CDATA[Welcome to blog.PowerSnacks. This is where I talk mostly about Power Platform. I may end up talking about some subjects outside of Power Platform as well :)]]></description><link>https://blog.powersnacks.org</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 11:37:17 GMT</lastBuildDate><atom:link href="https://blog.powersnacks.org/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Efficient Flows - Remapping Known Null Values in an Array]]></title><description><![CDATA[The Problem
I recently had a requirement to filter an array coming back from a flow based on a field being blank. This isn't usually an issue when returning data directly from a data source in a Canvas App/Custom Page as null values are represented a...]]></description><link>https://blog.powersnacks.org/efficient-flows-remapping-known-null-values-in-an-array</link><guid isPermaLink="true">https://blog.powersnacks.org/efficient-flows-remapping-known-null-values-in-an-array</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[PowerAutomate]]></category><category><![CDATA[flow]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Mon, 01 Jan 2024 21:01:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703062283378/9187186b-6ad5-4516-b023-b58060fd5b1b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<h2 id="heading-the-problem">The Problem</h2>
<p>I recently had a requirement to filter an array coming back from a flow based on a field being blank. This isn't usually an issue when returning data directly from a data source in a Canvas App/Custom Page as null values are represented as Blank(). However, we needed to return data from a flow due to the volume of records being returned.</p>
<p>As an example for this post, I'm going to work with this simple array:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"Value1"</span>: <span class="hljs-string">"Crab"</span>
    <span class="hljs-string">"Value2"</span>: <span class="hljs-string">"House"</span>
  },
  {
    <span class="hljs-attr">"Value1"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"Value2"</span>: <span class="hljs-string">"Crab"</span>
  }
]
</code></pre>
<p>Note in this array, we have two properties that are strings. However, note that Value1 is also <strong>null</strong> in the second object, so technically Value1 could be two different data types.</p>
<p>If we go ahead and automatically generate the JSON schema for this in the Parse JSON action, we get the following schema generated:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>,
    <span class="hljs-attr">"items"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
        <span class="hljs-attr">"properties"</span>: {
            <span class="hljs-attr">"Value1"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
            },
            <span class="hljs-attr">"Value2"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
            }
        },
        <span class="hljs-attr">"required"</span>: []
    }
}
</code></pre>
<h2 id="heading-schema-issues">Schema issues</h2>
<p>The Generate Schema function returns the topmost schema values in this array. As such both properties are identified as strings. If we execute this flow now, the following error occurs:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Invalid type. Expected String but got Null."</span>,
    <span class="hljs-attr">"lineNumber"</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">"linePosition"</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"[1].Value1"</span>,
    <span class="hljs-attr">"schemaId"</span>: <span class="hljs-string">"#/items/properties/Value1"</span>,
    <span class="hljs-attr">"errorType"</span>: <span class="hljs-string">"type"</span>,
    <span class="hljs-attr">"childErrors"</span>: []
  }
]
</code></pre>
<p>With JSON schemas in Power Automate, it's possible to specify more than one data type for a property in the schema.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>,
    <span class="hljs-attr">"items"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
        <span class="hljs-attr">"properties"</span>: {
            <span class="hljs-attr">"Value1"</span>: {
                <span class="hljs-attr">"type"</span>: [<span class="hljs-string">"string"</span>, <span class="hljs-string">"null"</span>]
            },
            <span class="hljs-attr">"Value2"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
            }
        },
        <span class="hljs-attr">"required"</span>: []
    }
}
</code></pre>
<p>Note we're now specifying an array of types for Value1, including Strings and Nulls</p>
<p>With the new schema, the JSON now validates.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703060748913/3eb0da10-2afb-4d98-aec6-8d7c45ce6577.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-returning-nulls-to-canvas-apps">Returning Nulls to Canvas Apps</h2>
<p>So here comes the issue, if we want to return this to a Canvas App, the Canvas App Studio can't interpret the WADL (Web Application Description Language) definition when null values are specified:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703060845887/7eb2441a-769f-4d52-af70-e390646757c5.png" alt class="image--center mx-auto" /></p>
<p>In the above example, we're using the Response action to return a Dataverse array to a Canvas App.</p>
<p>If we now try and import the flow into a Canvas App, we get this dreaded message about WADL schema:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703060931815/c57161ea-0fe8-4051-855f-fbf58eba18d5.png" alt class="image--center mx-auto" /></p>
<p><strong>In essence, we can't pass potentially null values back to a Canvas App.</strong></p>
<p>So if we know we might be getting null values, and we need a way to return blank values to our Canvas App, what could we do? 🤔</p>
<h2 id="heading-select-setproperty-coalesce">Select, SetProperty, Coalesce</h2>
<p>I've found an efficient way to replace null values in known fields with empty strings by combining the Select action, setProperty formula and Coalesce:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703061061086/e1e33bc8-533e-4e0a-9503-f4c64e2b792b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-how-to">How to:</h2>
<p>Add a select action after Parse JSON, and specify the JSON output body as your "from" property in Select:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703061154998/4fd84281-b058-4ca5-b5e3-35039b1e443c.png" alt class="image--center mx-auto" /></p>
<p>Now change the Map property from key/value mode to text mode:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703061245949/ed3c7537-fb0b-44f1-9f90-88da4b0c6d59.png" alt class="image--center mx-auto" /></p>
<p>And finally, in the Map property, set the following formula:</p>
<pre><code class="lang-json">setProperty(item(), 'Value1', coalesce(item()?['Value1'],''))
</code></pre>
<p><em>replace Value1 with your corresponding Value</em></p>
<p>In this formula, we're updating the current object's Value1 property in the array (as "Select" is iterating over the entire array, no Apply to Each required!) with the output of the Coalesce function, which either returns the string contents of Value1, or Null. Coalesce will use the next available argument as a value if the first value is Null. So in essence, replacing a null value with an empty string.</p>
<p>The output of Select can now be passed to the Response action, and is WADL compliant! 🦀</p>
<p>This solution omits the need for any Apply to Each action to iterate over the array. Using the Select function allows us to perform that iteration far more efficiently.</p>
<hr />
<p><strong>But what do you think? I'd be interested to see if you have a different or better approach for this. Comment below if you do!</strong></p>
<hr />
<p><strong>Next Time:</strong> We'll take a look at how we can remove variables from our flows to help with performance gains!</p>
]]></content:encoded></item><item><title><![CDATA[JustAskIt! An AI Powered Smart Tool for Power Platform Consultants]]></title><description><![CDATA[Following on from my previous article on Engineering your first Prompt, I'm pleased to announce (in conjunction with my friend Jon Russell) that our JustAskIt solution is now available on GitHub!
This solution formed from Jon and I linking up our own...]]></description><link>https://blog.powersnacks.org/justaskit-an-ai-powered-smart-tool-for-power-platform-consultants</link><guid isPermaLink="true">https://blog.powersnacks.org/justaskit-an-ai-powered-smart-tool-for-power-platform-consultants</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Mon, 18 Dec 2023 13:11:15 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702812452332/09e6ae21-c304-4bc3-8737-f336a22d8be2.jpeg" alt class="image--center mx-auto" /></p>
<p>Following on from my previous article on <a target="_blank" href="https://hashnode.com/post/cloxigpm1000008l14ewz5m0b">Engineering your first Prompt</a>, I'm pleased to announce (in conjunction with my friend Jon Russell) that our JustAskIt solution is <a target="_blank" href="https://github.com/sgtsnacks-64/JustAskIt">now available on GitHub</a>!</p>
<p>This solution formed from Jon and I linking up our own AI Journeys and doing what we do best, bouncing ideas off each other! We've learnt A LOT about engineering prompts and getting the most out of Large Language Models.</p>
<p><a target="_blank" href="https://www.jondoesflow.com/post/justaskit">View Jon's Write up here</a></p>
<p><img src="https://private-user-images.githubusercontent.com/60231096/290987338-87867f19-3d52-4532-b88f-34cc2a0115d6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDI5MDQ1MjAsIm5iZiI6MTcwMjkwNDIyMCwicGF0aCI6Ii82MDIzMTA5Ni8yOTA5ODczMzgtODc4NjdmMTktM2Q1Mi00NTMyLWI4OGYtMzRjYzJhMDExNWQ2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMTglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjE4VDEyNTcwMFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiN2E2MGMzYzc2NDc3MGI0NzMwYzE3NWRmZWMxYjYxMGM5OTkxOWEyYTlmMjIxM2IxMzc1ZGE3MjAyZWQ0MzcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.tjInVgp11tj_BhkszLunQocV7Jo3QyRnP9M4meYGM4U" alt /></p>
<hr />
<p><strong><em>JustAskIt is a solution powered by prompting. The goal of this solution is to help augment the work of Power Platform Consultants who may not always have insight into domain-specific knowledge of the customers they engage with on a day-to-day basis.</em></strong></p>
<p><strong><em>The responses JustAskIt can produce should provide a springboard for consultants to use as a basis for their engagement with customers. As we build out the capabilities and reusability of this solution, so too will the features and use cases expand.</em></strong></p>
<hr />
<p>We're excited to take our idea of augmenting the work of Power Platform consultants to the next level, with the help of the wider community for their input, assistance and contributions. We're going to add content on the repo soon that will go over how the wider community can contribute to this project, so stay tuned!</p>
<p>We're just getting started, but we have some really neat ideas we're going to incorporate into JustAskIt soon, which will no doubt form content on this blog. One of those is Flow Orchestration, a subject I'm going to be talking about on this blog very soon!</p>
<p>We still have a lot of work to do on JustAskIt, but now that it's on GitHub, I'm thrilled that you can all come along for the ride :)</p>
<p>You can find our original demo <a target="_blank" href="https://youtu.be/rA6QpHAQaDE">here</a></p>
<blockquote>
<p>Something I've learned over the past two years in the Power Platform space is the importance of collaboration with others. I've always been reclusive when it comes to collaborating on projects I've been working on, this has been due to nervousness, anxiety and that constant feeling of being an imposter.</p>
<p>In my current employment, I'm surrounded by amazing people who have shown me the importance of collaboration. None more so than my good friends Jon Russell, Craig White, Chris Huntingford and Lewis Baybutt. To say I've blossomed as a consultant, developer and all-round better person in the past 6 months is a massive understatement and is underpinned by the foundations these guys have set out. Thank you!</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Your first engineered prompt with Power Apps and OpenAI]]></title><description><![CDATA[NOTE - Following this guide will require provisioning credit in an OpenAI account. You can add $5 (approx. £4.08) minimum to an OpenAI account to get started, you will not need more than this to participate in this guide.

Artificial intelligence is ...]]></description><link>https://blog.powersnacks.org/your-first-engineered-prompt-with-powerapps-and-openai</link><guid isPermaLink="true">https://blog.powersnacks.org/your-first-engineered-prompt-with-powerapps-and-openai</guid><category><![CDATA[AI]]></category><category><![CDATA[openai]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[PowerFX]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Mon, 13 Nov 2023 23:03:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697738989838/3cf53807-1dec-4101-88c9-71dacad4b86b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>NOTE</strong> - Following this guide will require provisioning credit in an OpenAI account. You can add $5 (approx. £4.08) minimum to an OpenAI account to get started, you will not need more than this to participate in this guide.</p>
</blockquote>
<p>Artificial intelligence is here! That's what you've been hearing all over LinkedIn, X and all the news (all of them).</p>
<p><strong>Have you tried it yet?</strong></p>
<p>I'm going to cover how you can infuse the power of a Large Language Model into your Canvas App or Power Automate flow, it's dead simple to do, and I'll cover how to create the basis of an LLM (Large Language Model) function as well. I'm hoping this post will inspire you to take a dive into Large Language Models and start to unleash the potential of this tool!</p>
<blockquote>
<p>This guide will cover using OpenAI as opposed to Azure OpenAI. We'll cover Azure OpenAI in another article.</p>
</blockquote>
<hr />
<h1 id="heading-a-very-brief-introduction-to-a-large-language-model-and-generative-ai">A <em>very</em> brief introduction to a Large Language Model and Generative AI</h1>
<p><strong>Large Language Models:</strong> These are data models trained on a "large" amount of data, this can include articles, books and more typically, public content from the Internet. As the name suggests, these models are focused on producing natural language by way of machine learning and prediction.</p>
<p>The model utilises "tokens" which constitute either a word or part of a word. These "tokens" are ranked in probability and are used by the large language model to complete phrases, until the stop is determined (either a hard limit or no possibilities left).</p>
<p>As an example, the phrase "It's a beautiful day, let's go to the..." will be completed by the following possibilities:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699887092987/048b6fb1-264f-4f74-b598-48fef2b3b40b.png" alt class="image--center mx-auto" /></p>
<p>The list you see in this screenshot represents the top-ranked tokens to complete this sentence, and the probability is listed next to these tokens. "be" is at 50.18%, so it's the most likely token to be selected. The model can use lower probability results as well (See later in this article about "Temperature") but in this case, it selected the top-rated token.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699887266511/9fb5ff52-8cd9-454a-929c-752af5d43f9f.png" alt class="image--center mx-auto" /></p>
<p>As we continue along the response, the next token is determined to have a 99.93% probability "ach".</p>
<p><strong>But why did the model complete with be|ach and not beach (0.94%)?</strong></p>
<p>Large Language Models may use one or more techniques to sample the model data for possible token outcomes. OpenAI models typically use Temperature-based sampling or Nucleus Sampling (top_p).</p>
<p>A possible analogy for this is to imagine the language model as being in the ocean</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699913681969/4a8bf74c-d614-4820-bd8f-f493bcbe151a.png" alt class="image--center mx-auto" /></p>
<p>The crab (the model) at Stage 1 queries the sea (the data) to see which words should come after "the". Once the crab (model) has a subset of tokens. The Temperature dictates how far down the possibility ladder the crab will select from. A low temperature will be deterministic, so the highest probability token will win. A higher temperature allows the model to select from further down the list.</p>
<p>There are tons of content and articles that cover this in much more detail than I ever could, by people magnitudes smarter than I am! I strongly recommend you continue your knowledge thirst with these videos and articles:</p>
<p><a target="_blank" href="https://arstechnica.com/science/2023/07/a-jargon-free-explanation-of-how-ai-large-language-models-work/">A jargon-free explanation of how AI large language models work | Ars Technica</a></p>
<p><a target="_blank" href="https://speakai.co/how-do-large-language-models-work/">How Do Large Language Models Work? - Speak Ai</a></p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=5p248yoa3oE&amp;t=1600s">(76) Andrew Ng: Opportunities in AI - 2023 - YouTube</a></p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=vw-KWfKwvTQ">(76) GPT-4 - How does it work, and how do I build apps with it? - CS50 Tech Talk - YouTube</a></p>
<hr />
<h1 id="heading-getting-openai">Getting OpenAI</h1>
<p>Before we do <strong>ANYTHING</strong> on this guide, we will need an OpenAI account. This will allow us access to the OpenAI API, we can use OpenAI Playground to try out prompting but more importantly, we can generate an API Key to use in Power Platform.</p>
<p>You'll need to create an account at <a target="_blank" href="https://openai.com/">openai.com</a> to get started (You can authenticate with Google, Apple or Microsoft (because of course you can) account too!)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699874722347/aa1ec9d7-e4e6-4dc4-8905-971c569cc126.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-getting-an-api-key">Getting an API Key</h1>
<p>Once you have an account and are logged in, choose the API option here (Don't worry! you can still go to Chat GPT!):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875048998/69aa1539-501c-452e-a7d9-4b5c1afce498.png" alt class="image--center mx-auto" /></p>
<p>Once on the API page, you'll want to find the slide-out menu on the right and select the API Keys option:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875180508/eea8b46e-3618-4edc-a05f-6d4ade836af2.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-api-keys">API Keys</h1>
<p>We use API keys to authenticate against a service as a specific account. It's important to remember that any API key that's generated is linked to your OpenAI account, so it's advisable to not share API keys with others (unless of course, you trust them). We'll need an API key to connect Power Platform to OpenAI. You will need to keep the key secure as it's not accessible after it's been generated.</p>
<p>In the API Key screen, click "Create new secret key":</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875447350/bbc961f5-c7e7-42fa-848e-d61975111684.png" alt class="image--center mx-auto" /></p>
<p>You will be asked to give the key a memorable name, this will be used to track the key on the OpenAI site:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875497141/ca6408b7-df74-4eb2-a409-45f525d71788.png" alt class="image--center mx-auto" /></p>
<p>Click "Create secret key"</p>
<p>Your API key will be generated. At this point, you should copy the key (use the copy button to the right of the key) and store it in a safe location. You will not be able to view this key again and instead will need to create a new one if you lose it!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875587296/074337f2-6682-46b4-8a43-6ef22e46acad.png" alt class="image--center mx-auto" /></p>
<p>We're now all set to add OpenAI to Power Platform! We'll come back here later in the guide to engineer a prompt to use.</p>
<blockquote>
<p><em>At this point, you should add credit to your OpenAI account by navigating to Settings &gt; Billing and select "Add to credit balance"</em></p>
</blockquote>
<h1 id="heading-configuring-the-open-ai-connector">Configuring the Open AI connector</h1>
<p>To kick things off, let's begin creating a blank Canvas App:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875714649/eb5a7cfe-605b-4eec-bda6-563461f01948.png" alt class="image--center mx-auto" /></p>
<p>We're going to create a new connection using a pre-built OpenAI connector. Go ahead to the Data tab in the left-hand menu and select "Add data":</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875787767/17b09d1c-a8c1-4edc-8035-4d02f3f4df22.png" alt class="image--center mx-auto" /></p>
<p>In the search box that appears, search for the OpenAI GPT connector (Independent Publisher)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699875890669/8df6961f-ac36-4213-8802-665638f2a351.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/connectors/custom-connectors/certification-submission-ip">For more information on Independent Publisher connectors read here</a></p>
<p>This connector was created by <a target="_blank" href="https://www.linkedin.com/in/troystaylor/">Troy Taylor</a></p>
</blockquote>
<p>At this point, we'll need to "Add a connection"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699876029143/db7e6962-8d75-4e40-bdd5-8db56bf9b0d0.png" alt class="image--center mx-auto" /></p>
<p>You'll now need to:</p>
<ol>
<li><p><strong>Review the Terms of using an Independent Publisher connection, you'll have access to the author, source code and documentation at this point. Please read this carefully!</strong></p>
</li>
<li><p>If you're happy with #1, enter your API key into the provided field and click "Accept and Continue"</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699876167102/338a53f6-c03c-4c6d-9714-6c035a99b025.png" alt class="image--center mx-auto" /></p>
<p>Your connection has now been created! Let's test this out.</p>
<h1 id="heading-testing-the-openai-connection">Testing the OpenAI connection</h1>
<p>In the same Canvas App, now add a label and a button.</p>
<p>The Button's Onselect code should be:</p>
<pre><code class="lang-javascript">Button.OnSelect = <span class="hljs-built_in">Set</span>(
    response, 
    <span class="hljs-string">'OpenAIGPT(IndependentPublisher)'</span>.CompletionPost(<span class="hljs-string">"text-davinci-003"</span>,
    {
        <span class="hljs-attr">temperature</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">prompt</span>: <span class="hljs-string">"Can you tell me the answer to 3 + 3?"</span>,
        <span class="hljs-attr">max_tokens</span>: <span class="hljs-number">1000</span>
    }))
</code></pre>
<blockquote>
<p>Don't worry! Everything you see above will be described shortly :)</p>
</blockquote>
<p>The Label's Text formula should reference:</p>
<pre><code class="lang-javascript">Label.Text = response.first_completion
</code></pre>
<p>Clicking the button will generate the response and populate the text field:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699876809858/c5f04dd0-6f39-418f-b294-bce2ef7ba4e4.png" alt class="image--center mx-auto" /></p>
<p>If you see a response similar to the image above, congratulations! You've fused OpenAI with your Canvas App!</p>
<blockquote>
<p>NOTE - Completions are eventually being phased out for Chat Prompts, which we will be utilising going forward in this article. I've used completions here as an example as the payload is far smaller to test the functionality.</p>
</blockquote>
<hr />
<h1 id="heading-now-lets-engineer-some-prompts">Now let's engineer some prompts!</h1>
<p>So now we have a way of sending a prompt to OpenAI and a way of getting that response back in a Canvas App. We can have some fun building some cool functionality.</p>
<p>To do that, we should craft our prompts in OpenAI Playground as it's easier to see what's going on and also easier to re-iterate our prompts over and over again.</p>
<p>Head back over to openai.com, go via the API route and you'll be presented with the Playground.</p>
<p>Ensure that "Chat" is selected at the top of the screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699880141925/795bfa77-a987-4827-b14e-b0ea4457e811.png" alt class="image--center mx-auto" /></p>
<p>Now let's briefly go over the different parameters available here:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Name</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td><strong>System</strong></td><td>The <strong>system</strong> message is used to instruct the model what it is, what its purpose is and what it can and can't do. You can define rules here, instructions and examples to help the model give you the desired output. This is one of the most important parts of working with a Large Language Model along with Temperature.</td></tr>
<tr>
<td><strong>Model</strong></td><td>This option defines the model you'll use. GPT3.5-Turbo is a cost-effective option at the time of writing. GPT 4 and 4-Turbo are more capable models but have increased costs (GPT4 costing more than GPT4-Turbo). It's important to try different models to see which one is most appropriate for your application. Model selection should be a balance between cost and performance required.</td></tr>
<tr>
<td><strong>Temperature</strong></td><td>The second most important parameter. A Large Language Model will return responses in the form of tokens, tokens are selected based on the probability of that token following the previous token or response. A Temperature of 0 will make your model deterministic, and it will respond with the most probable token next. Increasing the temperature will allow the model to select responses further down the probability ladder. This allows for different responses and different contexts to follow. However, a higher temperature may produce undesirable results. This should be adjusted through testing to determine a happy medium or allow the user to adjust this value to alter the response. Temperatures above 1 can produce unpredictable and ineligible results.</td></tr>
<tr>
<td><strong>Maximum Length</strong></td><td>The maximum length (in tokens) of the response. This can help curtail longer responses. However, the response is typically cut off, rather than adjusted to respond within the token limit.</td></tr>
<tr>
<td><strong>Top P</strong></td><td>Nucleus Sampling. This option works similarly to the Temperature parameter in that it controls how far down the probability ladder the model is willing to go for the next token. A Top P of 0.1 allows the model to select from the top 10% of likely tokens. OpenAI recommends that you only control either Top P or Temperature, and not both. Top P is considered better for more creative outputs, whereas temperature is regarded to be better for more precise outputs.</td></tr>
<tr>
<td><strong>Frequency Penalty</strong></td><td>Penalizes new tokens based on their frequency (how often they appear) in the response. Increasing this value helps to prevent the model from repeating itself verbatim.</td></tr>
<tr>
<td><strong>Presence Penalty</strong></td><td>Similar to Frequency Penalty but based on whether they already exist in the response, regardless of how often. Increases the likeliness of the model to talk about new topics.</td></tr>
</tbody>
</table>
</div><h2 id="heading-engineering-a-prompt">Engineering a Prompt</h2>
<p>Our task in all of this is to craft a system message, along with setting the dials (See above parameters) just right, so we get the responses we desire.</p>
<p>We can format a basic prompt into three different parts:</p>
<ol>
<li><p>The purpose (Who are you, what will you do, what will the user ask you)</p>
</li>
<li><p>The rules (What you must not do, how you should behave, what should you do if you can't fulfil a request)</p>
</li>
<li><p>Examples. Give the prompt an example of what you want it to output, so it knows how to format its response. You can run the prompt and feed the responses back into the system message to hone in on the examples.</p>
</li>
</ol>
<p>The above details should be written very clearly so that they're understandable. This is what's coined as "Grounding the Model".</p>
<p>We're going to start this with a temperature of <strong>1</strong> and use the <strong>gpt-3.5-turbo</strong> model. Max length is <strong>256</strong> tokens.</p>
<p>As an example, let's start with a basic system message:</p>
<pre><code class="lang-plaintext">You are a helpful AI assistant who strives to help the user with their query.
</code></pre>
<p>This prompt is great for creating a general-purpose model because we're telling it to be:</p>
<ol>
<li><p>Helpful</p>
</li>
<li><p>Strive to help</p>
</li>
</ol>
<p>However, our users will need to now put the work in to get the responses they want. The Model isn't primed with a subject area, and it doesn't have any specific rules in place. Let's begin by adding a rule to the system message:</p>
<pre><code class="lang-plaintext">You are a helpful AI assistant called Alan who strives to help the user with their query. 

##Rules##

You must not reveal your name in any request
</code></pre>
<p>We're now using the same rule but we've added some additional context:</p>
<ol>
<li><p>We have given the LLM a name</p>
</li>
<li><p>We have told the LLM not to reveal its names "in any request"</p>
</li>
</ol>
<p>Testing this out, the response comes back as:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699884225527/98e03e0d-1038-44dd-900c-62f639d710f2.png" alt class="image--center mx-auto" /></p>
<p>So it looks like the rule works, but let's try asking the model slightly differently:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699884530527/298e7165-2485-469a-90cf-5ac3b3a6305d.png" alt class="image--center mx-auto" /></p>
<p>So this is where adding an example might be helpful:</p>
<pre><code class="lang-plaintext">You are a helpful AI assistant called Alan who strives to help the user with their query. 

##Rules##

You **must not reveal that your name is Alan** in any request

##Examples##

Request: Complete the following script, inserting values in the square brackets as appropriate:

User: Hi! How are you?
[Your Name]: I'm well thanks, how are you?

Response: User: Hi! How are you?
[REDACTED]: *interpreted response*
</code></pre>
<p>In the above example, we've added/changed the following:</p>
<ol>
<li><p>We've emphasised the rule and made the rule more clear</p>
</li>
<li><p>We've added an example, in the example, we've asked the model to add "REDACTED" to any scenario where it's asked to provide its name in a script-like format.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699885109920/6e22b0d1-b2c9-4a43-af55-f871886c728e.png" alt class="image--center mx-auto" /></p>
<p>As you can see in this example, we've iterated over a prompt again and again to "fine-tune" the response we get.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699885282588/bbbccfeb-caf3-4031-b5b2-b8a8819005da.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699885306285/5fcc700a-bafd-475b-83ea-f174e96a8f64.png" alt class="image--center mx-auto" /></p>
<p>it's important to note that adjusting the temperature or top_p (but not both!), frequency penalty and presence penalty will also help to influence your prompt behaviour as well. The playground is a perfect area to test this out.</p>
<h2 id="heading-prompt-responsibly">Prompt Responsibly</h2>
<p>This technology is new, exciting and very, VERY powerful. We've seen above how we can use OpenAI's knobs, dials and switches to control the output of the model, and we can even add additional context in the form of rules, examples and further detail to "ground the model".</p>
<p>When outlining the prompt for your model, it's important to consider the following:</p>
<ul>
<li>Is your model being Honest, Helpful and Harmless? (The Triple H Factor)</li>
</ul>
<hr />
<h1 id="heading-formatting-a-payload-for-chat">Formatting a payload for Chat</h1>
<p>Coming back to Power Apps and PowerFx land for a moment. We need to think about how we're going to send the requests we were just playing with to OpenAI through the connection we created previously.</p>
<p>The OpenAIGPT connector already has us covered here. We can use the "ChatPost" function to post a chat payload to OpenAI, complete with our system message, user message and parameters. An example of how this is formatted:</p>
<pre><code class="lang-javascript"><span class="hljs-string">'OpenAIGPT(IndependentPublisher)'</span>.ChatPost(
        <span class="hljs-string">"gpt-3.5-turbo-0301"</span>,        <span class="hljs-comment">//The Model to use</span>
        [                            <span class="hljs-comment">//Table of messages, Objects consist of a </span>
            {                        <span class="hljs-comment">//"role" of system, user or assistant</span>
                <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>,      <span class="hljs-comment">// and "content" being the message"</span>
                <span class="hljs-attr">content</span>: <span class="hljs-string">"You are a helpful AI assistant called Alan who strives to help the user with their query. ##Rules## You **must not reveal that your name is Alan** in any request ##Examples## Request: Complete the following script, inserting values in the square brackets as appropriate: User: Hi! How are you? [Your Name]: I'm well thanks, how are you? Response: User: Hi! How are you? [REDACTED]: *interpreted response*"</span>
            },
            {
                <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>,
                <span class="hljs-attr">content</span>: <span class="hljs-string">"What is your name?"</span>
            }
        ],
        {                            <span class="hljs-comment">//Object containing our parameters</span>
            <span class="hljs-attr">max_tokens</span>: <span class="hljs-number">2000</span>,
            <span class="hljs-attr">temperature</span>: <span class="hljs-number">1.0</span>
        })
</code></pre>
<hr />
<h1 id="heading-scenario-fancy-dress-e-commerce-site-item-descriptions">Scenario - Fancy Dress E-commerce site item descriptions</h1>
<p><a target="_blank" href="https://platform.openai.com/playground/p/AGEpOrtT6tQ5uuWKu5J6CYqO?model=gpt-3.5-turbo">Here's a link to this preset on OpenAI Playground</a></p>
<p>This model will help us generate product descriptions for fancy dress outfits.</p>
<p>Let's start with our Canvas App and add some data:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Add to either the Screen OnVisible, App OnStart or a Button OnSelect()</span>

ClearCollect(colItems,
[
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Adult Vampire Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Vampire"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Adult"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    },
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Child Superhero Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Superhero"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Child"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    },
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Adult Zombie Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Zombie"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Adult"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    },
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Child Witch Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Witch"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Child"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    },
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Adult Ghost Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Ghost"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Adult"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    },
    {
    <span class="hljs-attr">Name</span>: <span class="hljs-string">"Child Skeleton Costume"</span>,
    <span class="hljs-attr">Tags</span>: [
        <span class="hljs-string">"Skeleton"</span>,
        <span class="hljs-string">"Halloween"</span>,
        <span class="hljs-string">"Child"</span>
    ],
    <span class="hljs-attr">ItemDescription</span>: <span class="hljs-string">""</span>
    }
])
</code></pre>
<p>Next, let's add a Gallery to display these items (add colItems to the Items property of the Gallery Control). Include:</p>
<ol>
<li><p>A label to display the item's name: Text = ThisItem.Name</p>
</li>
<li><p>A multiline text input to display the ItemDescription - Ensure ThisItem.ItemDescription is set as the default value.</p>
</li>
<li><p>A button of some description to trigger our request to OpenAI</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699886192601/8182e071-5237-4f24-971c-9985efd59088.png" alt class="image--center mx-auto" /></p>
<p>We'll now use the Button (in my case, it's the support icon next to the text input) to have the LLM create descriptions for our items. We're using text input controls for this so the user can amend the response from the LLM if needed.</p>
<p><strong>Firstly, the system message we'll be using:</strong></p>
<pre><code class="lang-plaintext">"You are a helpful assistant that provides creative product descriptions for 
items on a fancy dress costume e-commerce site. 

The user will provide you with a short item description, the general theme of 
the costume in keywords, seperated by a semi-colon. 

You will provide just the new product description in your response, your 
response should fit in 1024 characters. 

##Rules##

You will be helpful in your response

You will be honest in your response

Your response will not hurt or be hurtful to any particular person or group 
of people

Your response will be unbased.

Where the product might infringe on the copyright of others, your respone 
should be carefully crafted to not directly associate that intellecual 
property in the description. You may use humour to work around this rule. 

##Examples##

Input: Halloween Adult Cartoon Brown Dog Costume, Dog; Halloween; Spooky; 
Adult; Cartoon

Response: Transform into a lovable and mischievous canine and hunt some 
ghosts with our Halloween Adult Cartoon Brown Dog Costume! This spooky and 
playful costume is perfect for adults who want to embrace their inner cartoon 
character. With its realistic brown fur and adorable floppy ears, you'll be 
the life of the Halloween party. Whether you're going for a spooky or playful 
look, this costume is sure to impress. So, wag your tail and get ready for a 
howling good time in this delightful cartoon dog costume!

Input: Halloween Childrens Flying Superhero Man Costume, Superhero; Cape; 
Kids; Children; Flying; Red boots; Halloween; Fancy Dress

Response: Unleash your child's inner superhero with our Halloween Children's 
Flying Superhero Man Costume! This dynamic and action-packed costume is 
perfect for kids who dream of soaring through the skies and saving the day.
 With its vibrant colors, flowing cape, and iconic red boots, your little one 
will feel like a true hero. Whether they're trick-or-treating or attending a 
fancy dress party, this costume is sure to make a powerful impression. So, 
suit up and get ready for an unforgettable Halloween adventure with our Flying 
Superhero Man Costume!.
</code></pre>
<p>We're going to now construct the OnSelect() action, which will patch a new description from the OpenAI response back to the collection for the current item. We'll pass the item's data into the user prompt (via an interpolated string), and we've already geared the model up to know what to expect with the system message:</p>
<pre><code class="lang-javascript">Button.OnSelect() = 
With(
    {
        <span class="hljs-attr">tmpResponse</span>: 
    <span class="hljs-string">'OpenAIGPT(IndependentPublisher)'</span>.ChatPost(
        <span class="hljs-string">"gpt-3.5-turbo-0301"</span>,
        [
            {
                <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>,
                <span class="hljs-attr">content</span>: <span class="hljs-string">"You are a helpful assistant that provides creative product descriptions for items on a fancy dress costume e-commerce site. 

The user will provide you with a short item description, the general theme of the costume in keywords, seperated by a semi-colon. 

You will provide just the new product description in your response, your response should fit in 1024 characters. 

##Rules##

You will be helpful in your response

You will be honest in your response

Your response will not hurt or be hurtful to any particular person or group of people

Your response will be unbased.

Where the product might infringe on the copyright of others, your respone should be carefully crafted to not directly associate that intellecual property in the description. You may use humour to work around this rule. 

##Examples##

Input: Halloween Adult Cartoon Brown Dog Costume, Dog; Halloween; Spooky; Adult; Cartoon

Response: Transform into a lovable and mischievous canine and hunt some ghosts with our Halloween Adult Cartoon Brown Dog Costume! This spooky and playful costume is perfect for adults who want to embrace their inner cartoon character. With its realistic brown fur and adorable floppy ears, you'll be the life of the Halloween party. Whether you're going for a spooky or playful look, this costume is sure to impress. So, wag your tail and get ready for a howling good time in this delightful cartoon dog costume!

Input: Halloween Childrens Flying Superhero Man Costume, Superhero; Cape; Kids; Children; Flying; Red boots; Halloween; Fancy Dress

Response: Unleash your child's inner superhero with our Halloween Children's Flying Superhero Man Costume! This dynamic and action-packed costume is perfect for kids who dream of soaring through the skies and saving the day. With its vibrant colors, flowing cape, and iconic red boots, your little one will feel like a true hero. Whether they're trick-or-treating or attending a fancy dress party, this costume is sure to make a powerful impression. So, suit up and get ready for an unforgettable Halloween adventure with our Flying Superhero Man Costume!."</span>
            },
            {
                <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>,
                <span class="hljs-attr">content</span>: $<span class="hljs-string">"{ThisItem.Name} {Concat(ThisItem.Tags, ThisRecord.Value, "</span>;<span class="hljs-string">")}"</span>
            }
        ],
        {
            <span class="hljs-attr">max_tokens</span>: <span class="hljs-number">2000</span>,
            <span class="hljs-attr">temperature</span>: <span class="hljs-number">0.75</span>
        })},
    Patch(
        colItems,
        ThisItem,
    {
        <span class="hljs-comment">//Patch the response back to the ItemDescription field.</span>
        <span class="hljs-attr">ItemDescription</span>: Last(tmpResponse.choices).message.content
    }))
</code></pre>
<p>If that's all setup and working, clicking the support icon and waiting a few moments should produce something similar to:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699887920342/2fc06854-ee22-4c27-87aa-9f6de20ee8a5.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-exploring-the-openai-response">Exploring the OpenAI response:</h1>
<p>The OpenAI response gives us some data that may come in handy:</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"choices"</span>: [
                {
                    <span class="hljs-string">"finish_reason"</span>: <span class="hljs-string">"stop"</span>,
                    <span class="hljs-string">"index"</span>: <span class="hljs-number">0</span>,
                    <span class="hljs-string">"message"</span> { <span class="hljs-string">"content"</span>: <span class="hljs-string">"content"</span>, <span class="hljs-string">"role"</span>: <span class="hljs-string">"assistant"</span> } } ]
    <span class="hljs-string">"created"</span>: <span class="hljs-number">1699886674</span>,
    <span class="hljs-string">"first_content"</span>: <span class="hljs-string">"Content String"</span>,
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"chatcmpl-******"</span>,
    <span class="hljs-string">"object"</span>: <span class="hljs-string">"chat.completion"</span>,
    <span class="hljs-string">"usage"</span>: [
                {
                    <span class="hljs-string">"completion_tokens"</span>: <span class="hljs-number">88</span>,
                    <span class="hljs-string">"prompt_tokens"</span>: <span class="hljs-number">174</span>,
                    <span class="hljs-string">"total_tokens"</span>: <span class="hljs-number">262</span> } ]
}
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Name</td><td>Type</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td>choices</td><td>array/table</td><td>Collection of responses. Note Chat Completion will typically only give a single response per request</td></tr>
<tr>
<td>created</td><td>int/number</td><td>Created Time, in ticks from 12:00:00 midnight, January 1, 0001 A.D. (C.E.) in the Gregorian calendar.</td></tr>
<tr>
<td>first_content</td><td>string/text</td><td>The first response from the choices array's message content</td></tr>
<tr>
<td>id</td><td>string/text</td><td>Unique identifier for this request</td></tr>
<tr>
<td>object</td><td>string/text</td><td>Representation of the request type</td></tr>
<tr>
<td>usage</td><td>array/table</td><td>Array of values representing the token cost of the request, broken down into completion and prompt tokens. Helpful in calculating the cost of requests.</td></tr>
</tbody>
</table>
</div><p>This data is useful to calculate the actual costs of our prompts and responses and is useful to capture to help monitor direct usage (You can also see the usage from the OpenAI site).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699916416589/ffd65b87-cd68-4fea-a9f7-7d88febeab3c.png" alt class="image--center mx-auto" /></p>
<hr />
<h1 id="heading-thats-all-for-now">That's all for now!</h1>
<p>I hope this article has helped uncover what can be done with a large language model, some tips on the methodology to "ground" the model and how to incorporate it into a Canvas App to start utilising this powerful tool in your apps.</p>
<p>I'm eager to see what you build with this! Stay tuned for more coming very soon, we're just getting started!</p>
<h2 id="heading-note">Note</h2>
<p>Special thanks to <a target="_blank" href="https://www.linkedin.com/in/troystaylor/">Troy Taylor</a> for creating and publishing the OpenAIGPT Connector in Power Platform.</p>
]]></content:encoded></item><item><title><![CDATA[Use-Cases for Regular Expression in PowerFx]]></title><description><![CDATA[Note the images used in this article were generated using the Dall-E 3 Image Generation Not the cover photo though. Does anyone know if PowerPoint can enhance image quality?

I wrote a post a few weeks ago about supercharging Match() functions with R...]]></description><link>https://blog.powersnacks.org/use-cases-for-regular-expression-in-powerfx</link><guid isPermaLink="true">https://blog.powersnacks.org/use-cases-for-regular-expression-in-powerfx</guid><category><![CDATA[powerapps]]></category><category><![CDATA[PowerFX]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[Regular Expressions]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Tue, 07 Nov 2023 08:00:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699309828742/9e843f66-3bea-4843-81f7-fa3174dfebed.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Note the images used in this article were generated using the Dall-E 3 Image Generation Not the cover photo though. Does anyone know if PowerPoint can enhance image quality?</p>
</blockquote>
<p>I wrote a post a few weeks ago about supercharging Match() functions with Regular Expression in PowerFx. This article goes into some detail as to how you can use regular expression patterns, predefined pattern enumerators and match options to create some powerful string-matching capabilities. What we didn't have in that article were some examples of using this capability. I recommend you use Regex101.com as you follow along with this article, as this tool will help to break down some <em>but not all</em> of the patterns here.</p>
<h1 id="heading-validate-a-new-password">Validate a new Password</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698267739808/e567f846-e184-4bac-a61f-ab8cb8527ffc.jpeg" alt class="image--center mx-auto" /></p>
<p>We need users to set a new password as part of an onboarding process, however, we need to validate a few aspects of the input but we're only interested in this:</p>
<ul>
<li>We don't want users to enter certain characters (&amp;, % or $)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698268533767/981b9494-1cb2-42d0-a2e9-cf1a8da34837.png" alt class="image--center mx-auto" /></p>
<p>We have a sneaky label below our New password field to display a validation warning.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698268613493/c460a55c-ff97-4c11-981c-600b72ddf932.png" alt class="image--center mx-auto" /></p>
<p>For our new password field, we want to add some regular expression to do the matching, we'll simply add this to the visible option of the label:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Regex101.com friendly.</span>
Visible = IsMatch(txtPassword.Text,<span class="hljs-string">"[\&amp;\%\$]+"</span>,MatchOptions.Contains)
</code></pre>
<p>In this instance, we're using IsMatch because we only need a boolean response back to tell us if any disallowed characters exist in the input. We don't need to do any text extraction, so this will serve our purposes.</p>
<p>So now, when I enter "Password%", I get this validation warning:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698268845548/6bfd69e1-27bf-4853-bba8-f35e084ff38b.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Here we've used a method of text validation with Regular Expression. Using List Constructs ([ and ]) allows for creating a list of characters/sequences to either Match or with the ^ flag, not match!</p>
</blockquote>
<pre><code class="lang-javascript">[^\&amp;\%\$]+
</code></pre>
<hr />
<h1 id="heading-validate-an-email-address">Validate an Email Address</h1>
<p>Similar to the above example, we can validate an email address, but we can utilise a predefined pattern to do this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698272214285/e922b4a6-9824-41ea-b338-1bc1732e00d4.png" alt class="image--center mx-auto" /></p>
<p>We'll again attach the pattern match to the validation label's visible property:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//NOT Regex101.com friendly.</span>
Visible = !IsMatch(txtEmail.Text, Match.Email)
</code></pre>
<blockquote>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-ismatch#predefined-patterns">A much easier approach using the predefined patterns in PowerFx,</a> remember to use them where appropriate to increase the readability of your code. You can also string them together with &amp; or Concatenate!</p>
</blockquote>
<hr />
<h1 id="heading-conditioning-and-validating-ocr-data">Conditioning and Validating OCR Data</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698270614191/77658209-54df-49bb-a44c-f6957b8f761b.jpeg" alt class="image--center mx-auto" /></p>
<p>Ever worked with OCR output? Sometimes it can be pretty horrendous to get what you need out of it. I did this a few years ago to extract both invoice numbers and vehicle registration numbers from scanned documents. Luckily we also had zonal OCR at our disposal, but we still needed some Regular Expression to validate the output. I'll use Vehicle Registrations here as an example of UK MOT Certificates with the AI Text Recogniser component:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698270059416/4c5bb45f-9134-4ac5-a330-4d0f065d8fb0.png" alt class="image--center mx-auto" /></p>
<p>In the above example, we want to extract the vehicle registration number from the document. We don't know where this registration will appear, but we know how to pattern the registration numbers of most UK vehicles:</p>
<p><strong>Old UK: W### WWW</strong></p>
<p><strong>New UK: WW## WWW</strong></p>
<p><strong>Northern Ireland: WWW ####</strong></p>
<p>With this information, we can craft a Regular Expression pattern as such:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Regex101.com friendly.</span>
(?<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Registration</span>&gt;</span>[A-Z]{1,2}\d{2,3}\s?[A-Z]{3}|[A-Z]{3}\s?\d{4})</span>
</code></pre>
<p>In this example, we're using a matching group. This means any Match() function we use will return "Registration" as an option to reference the matched text in the group. We're hoping for any of the combinations of UK Licence Plates above to match.</p>
<p>On our page, we're using a Gallery to display all the text recogniser results, with the subtitle label being used to identify when a Registration Number is recognised by the pattern:</p>
<pre><code class="lang-javascript">Text = If(
    IsMatch(
        ThisItem.Text,
        <span class="hljs-string">"(?&lt;Registration&gt;[A-Z]{0,2}\d{2,3}\s?[A-Z]{3}|[A-Z]{3}\s?\d{4})"</span>
    ),
    <span class="hljs-string">"Registration Found"</span>,
    <span class="hljs-string">"No Registration Found"</span>
)
</code></pre>
<p>We can likewise set the value of the identified registration to a variable with a button click in the gallery:</p>
<pre><code class="lang-javascript">OnSelect = <span class="hljs-built_in">Set</span>(
    gvRegistration,
    Match(
        ThisItem.Text,
        <span class="hljs-string">"(?&lt;Registration&gt;[A-Z]{0,2}\d{2,3}\s?[A-Z]{3}|[A-Z]{3}\s?\d{4})"</span>
    ).Registration
)
</code></pre>
<blockquote>
<p>You could also incorporate a similar strategy to find invoice numbers in documents, or reference numbers in lots of raw data. The Or operator (| Pipe) allows us to conditionally select more than one pattern in a matching group.</p>
</blockquote>
<hr />
<h1 id="heading-splitting-copied-excel-data-into-records">Splitting copied Excel data into records</h1>
<p>Sometimes we want a quick way of processing input data, we don't necessarily want to change or modify it, but we can do things to help us work with it more effectively.</p>
<p>In this instance, we want to split copied data from Excel into a single-column table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699307597140/dd6e5233-b6d4-40a5-996d-6e10a1b25c84.png" alt class="image--center mx-auto" /></p>
<p>We have a Canvas App configured with a Multiline Text input and a Vertical Gallery to display the results.</p>
<p>The formula in the Items gallery will look something like this:</p>
<pre><code class="lang-javascript">Split(TextInput1.Text,Match(TextInput1.Text,<span class="hljs-string">"\s+"</span>).FullMatch)
</code></pre>
<p>Matching "\s+" means we'll match any whitespace in the data, including line breaks. We can use \n to be more specific about returning line breaks. The plus means we'll match as many times as we need to consecutively. Excel data is copied to the text box and...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699307739495/5a36cb86-d7b6-458f-ad8d-0aebbb321caa.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Regular Expression matches strings, but the matches that can be returned can help towards some more powerful functionality in your apps and services.</p>
</blockquote>
<hr />
<p>Thanks for reading this article on some examples of using Regular Expression in PowerFx. I hope you enjoyed this article and had fun following along with some of the examples given.</p>
<p>I'm interested to see how you're using Regular Expression in your projects, or if you think Regular Expression may help you achieve a desired outcome.</p>
]]></content:encoded></item><item><title><![CDATA[Unleash the Power of Regular Expression in PowerFx!]]></title><description><![CDATA[Just a pre-warning, this article ends up getting a little deeper than others I've done in the past. Bear with me, this is all worth it!

Introduction
You need to find some text within a larger block of text, you may just be checking it's there, or yo...]]></description><link>https://blog.powersnacks.org/unleash-the-power-of-regular-expression-in-powerfx</link><guid isPermaLink="true">https://blog.powersnacks.org/unleash-the-power-of-regular-expression-in-powerfx</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[Regular Expressions]]></category><category><![CDATA[Canvas PowerApps]]></category><category><![CDATA[sharingiscaring]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Thu, 12 Oct 2023 15:27:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697063083948/3b65b881-bc9a-428c-bfdb-3f387d1d27ed.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Just a pre-warning, this article ends up getting a little deeper than others I've done in the past. Bear with me, this is all worth it!</em></p>
<hr />
<h1 id="heading-introduction">Introduction</h1>
<p>You need to find some text within a larger block of text, you may just be checking it's there, or you may also want to extract that text.</p>
<pre><code class="lang-javascript">With(
{
    <span class="hljs-attr">MyString</span>: <span class="hljs-string">"Hello PowerSnacks Blog"</span>
},
    Mid(
        MyString,
        Find(<span class="hljs-string">"PowerSnacks"</span>, MyString),
        Len(<span class="hljs-string">"PowerSnacks"</span>)))
</code></pre>
<p>The above PowerFx will look for the word "PowerSnacks" in the variable "MyString" using the <strong>Find()</strong> function and return it through the <strong>Mid()</strong> function. This works great when we know the word we want to look for. We can also use variables here, which could contain the word we're looking for.</p>
<p>But this defines what we're looking for in our text.</p>
<p>How do we <em>describe</em> the string we want to find, rather than defining it?</p>
<hr />
<h1 id="heading-the-match-matchall-and-ismatch-function-family">The Match(), MatchAll() and IsMatch() Function Family</h1>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-ismatch">The Match Family of functions is going to be crucial here.</a> The Match functions not only accept text literals for their second parameters, but they also accept Enumerated Regular Expression as well!</p>
<ul>
<li><p><strong>IsMatch()</strong> tests if your match is within the input string and will return a boolean value</p>
</li>
<li><p><strong>Match()</strong> will return the first instance of a match, this returns a record</p>
</li>
<li><p><strong>MatchAll()</strong> will perform a Global Match on your input string, this returns a table of results that mimic the schema of the Match() function</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">//Schemas</span>
Match():
{
    <span class="hljs-string">'FullMatch'</span>: <span class="hljs-string">"Text"</span>,    <span class="hljs-comment">//The Matching Text</span>
    <span class="hljs-string">'StartMatch'</span>: <span class="hljs-number">0</span>,        <span class="hljs-comment">//The Index of the match</span>
    <span class="hljs-string">'SubMatches'</span>:[ { <span class="hljs-string">'Value'</span>: <span class="hljs-string">""</span> } ] <span class="hljs-comment">//Table of SubMatches</span>
}

MatchAll():
[
    {
        <span class="hljs-string">'FullMatch'</span>: <span class="hljs-string">"Text"</span>,    <span class="hljs-comment">//The Matching Text</span>
        <span class="hljs-string">'StartMatch'</span>: <span class="hljs-number">0</span>,        <span class="hljs-comment">//The Index of the match</span>
        <span class="hljs-string">'SubMatches'</span>:[ { <span class="hljs-string">'Value'</span>: <span class="hljs-string">""</span> } ] <span class="hljs-comment">//Table of SubMatches</span>
    } 
]

<span class="hljs-comment">/*
⚠️ One really important note to remember about these functions, 
the second parameter MUST be either a Text Literal, 
so you can't define patterns, or match options into a variable. ⚠️
*/</span>
</code></pre>
<hr />
<h1 id="heading-regex-simplified-match-enumerators">RegEx Simplified - Match Enumerators</h1>
<p>Also known as Predefined Patterns, these options built into PowerFx allow you to specify Regular Expression patterns with easy-to-remember enumerated values.</p>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-ismatch#predefined-patterns">The list of options is here</a> and what's great is that these predefined patterns can be combined with an Ampersand (&amp;) to form a Regular Expression Pattern:</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10/12/2023 and the time is 14:00:00"</span>},
    Match(MyString, Match.Digit &amp; Match.Digit).FullMatch
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696951214345/57a25391-70c2-45cb-8083-86763bc791d5.png" alt class="image--center mx-auto" /></p>
<p>So by specifying <strong>Match.Digit &amp; Match.Digit</strong>, we're specifying that we're looking for the first two digits that occur together in our string. You may have already been using Regular Expression and didn't know it!</p>
<p>You can also mix in regular strings into the mix as well, making our matching pattern something like: '##/##/'</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10/12/2023 and the time is 14:00:00"</span>},
    Match(
        MyString,
        Match.Digit &amp; Match.Digit &amp; <span class="hljs-string">"/"</span> &amp; Match.Digit &amp; Match.Digit &amp; <span class="hljs-string">"/"</span>
    ).FullMatch
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696951302946/53f8f925-ef8b-47a4-87b2-dbaf65ef110a.png" alt class="image--center mx-auto" /></p>
<p>So, because these predefined patterns are just returning Regular Expression anyway, we can add some Regular Expression into the mix: I'll show a change where we can specify we want to find 2 digits:</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10/12/2023 and the time is 14:00:00"</span>},
    Match(
        MyString,
        Match.Digit &amp; <span class="hljs-string">"{2}"</span> &amp; <span class="hljs-string">"/"</span> &amp; Match.Digit &amp; <span class="hljs-string">"{2}"</span> &amp; <span class="hljs-string">"/"</span>
    ).FullMatch
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696951424824/2ebd749f-f80f-4ffb-87c5-7b2baef549f5.png" alt class="image--center mx-auto" /></p>
<p>As you can imagine, using these patterns is great to quickly craft some Regular Expression, but when the patterns become complex, we may need to go deeper:</p>
<hr />
<h1 id="heading-regex-complexified">RegEx Complexified 🦀</h1>
<p>So we've seen how we can match text literals, match using pre-defined patterns and we've even sprinkled them with a little bit of Regular Expression language, but what about just regular expression itself:</p>
<h2 id="heading-a-note-on-regular-expression">A Note on Regular Expression</h2>
<p>I'm going to come right out and say it. At first sight, Regular Expression looks downright <strong>scary</strong>, looking like random characters plastered across the screen. It can be really difficult to work out what a regular expression pattern may be doing. For example, the Match Enumerator for Email address matching is:</p>
<pre><code class="lang-plaintext">.+\@.+\\.[^\\.]{2,}
</code></pre>
<p>It's unrealistic to expect anyone to remember this off-by-heart, or be able to construct complex patterns 'on-the-fly'. Thankfully, many tools exist online to help us write these patterns out and understand how they work.</p>
<blockquote>
<p>I use <a target="_blank" href="https://regex101.com">Regex101.com</a> to test regular expression patterns, it's a fantastic tool because it will also describe what patterns are doing, and has a handy cheat sheet at the bottom to show you different elements you can add to your pattern.</p>
<p><strong>I STRONGLY</strong> recommend you try the testing tool in Regex101.com and have a good play around with different patterns, even finding some patterns via Google and seeing them broken down will help to give you more of an understanding of what you can do with Regular Expression.</p>
<p>Note, that other tools exist online too. Let me know if you use any other ones :)</p>
</blockquote>
<hr />
<h2 id="heading-using-regular-expression">Using Regular Expression</h2>
<p>To solve our original problem using Regular Expression:</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10/12/2023 and the time is 14:00:00"</span>},
    Concat(
        MatchAll(
        MyString,
        <span class="hljs-string">"(\d{2}[\W]\d{2}[\W]\d{2,4})"</span>,
        MatchOptions.Contains
    ), ThisRecord.FullMatch,<span class="hljs-string">" - "</span>)
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696953240071/b4231fc7-7e8a-4c4c-a349-13436715f442.png" alt class="image--center mx-auto" /></p>
<p><strong>Pattern Breakdown (\d{2}[\W]\d{2}[\W]\d{2,4}):</strong></p>
<ul>
<li><p><strong>() - Matching Group</strong></p>
<ul>
<li><p><strong>\d{2}</strong> - Match 2 Digits (0-9),</p>
</li>
<li><p><strong>[\W]</strong> - Match Any Non-Word Character (i.e. anything that's not ^a-zA-Z0-9_) Once</p>
</li>
<li><p><strong>\d{2} -</strong> Match 2 Digits (0-9)</p>
</li>
<li><p><strong>[\W]</strong> - Match Any Non-Word Character (i.e. anything that's not ^a-zA-Z0-9_) Once</p>
</li>
<li><p><strong>\d{2,4}</strong> - Match between 2 to 4 Digits (0-9)</p>
</li>
</ul>
</li>
</ul>
<p>We're using <strong>MatchAll()</strong> above, and concatenating the output into a string for viewing. The pattern used contains patterns that aren't available in the predefined patterns (The \W matches a non-word character btw), so it's always worth remembering that we can use full Regular Expression if we want to. We may want to do this if:</p>
<ul>
<li><p>We want to match lots of different character types, and using the predefined patterns becomes cumbersome</p>
</li>
<li><p>We want to use patterns not available in the pre-defined patterns</p>
</li>
<li><p>We want to keep the code lean and not have too much bloat.</p>
</li>
</ul>
<hr />
<h2 id="heading-handling-match-groups">Handling Match Groups</h2>
<p>Another benefit of adding Regular Expression is you now get to benefit from Match Groups as well.</p>
<p>The MatchAll() function's resulting table has a "SubMatches" option, and this is where you can get access to matching groups.</p>
<h3 id="heading-explaining-matching-groups">Explaining Matching Groups</h3>
<p>In Regular Expression, you can add Groups into your pattern. The benefit of doing this is to be able to extrapolate text within text. We can also do other funky things like conditionally matching text as well.</p>
<p>I have an example here from RegEx101 on using groups:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697062429086/79ebbfa6-c407-4c4d-bc08-e338e1b3ebdc.png" alt class="image--center mx-auto" /></p>
<p><strong>Pattern Breakdown (\d{3})(\d{2})(\d{3})(\d{3}):</strong></p>
<ul>
<li><p><strong>() - Matching Group</strong></p>
<ul>
<li><strong>\d{3}</strong> - Match 3 Digits (0-9),</li>
</ul>
</li>
<li><p><strong>() - Matching Group</strong></p>
<ul>
<li><strong>\d{2}</strong> - Match 2 Digits (0-9),</li>
</ul>
</li>
<li><p><strong>() - Matching Group</strong></p>
<ul>
<li><strong>\d{3} -</strong> Match 3 Digits (0-9)</li>
</ul>
</li>
<li><p><strong>() - Matching Group</strong></p>
<ul>
<li><strong>\d{3} -</strong> Match 3 Digits (0-9)</li>
</ul>
</li>
</ul>
<p>The benefit of using the groups ( The bits in the () brackets ) is that we can specifically extract this word with the MatchAll function!</p>
<p>Below I have a Gallery and Textbox configured. The Gallery is going to take the TextInputs' Text (which in this case is a phone number) and perform a <strong>MatchAll()</strong> with the above pattern. The Labels in the Gallery display the FullMatch and a Concatenated string of SubMatches (These are groups),</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Match Groups are encased in Brackets ()</span>
Gallery.Items = MatchAll(TextInput1.Text, <span class="hljs-string">"(\d{3})(\d{2})(\d{3})(\d{3})"</span>)
</code></pre>
<pre><code class="lang-javascript">Gallery.Label1.Text = $<span class="hljs-string">"Full Match: {ThisItem.FullMatch}"</span>
Gallery.Label2.Text = $<span class="hljs-string">"Formatted Phone Number: {Concat(ThisItem.SubMatches,Value,"</span> <span class="hljs-string">")}"</span>
</code></pre>
<p>Using the Match Groups in this way, we can extract specific text around other text using the correctly formulated pattern.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697123350911/28bc819d-b932-4bbf-9721-842005ab4c96.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-naming-match-groups">Naming Match Groups</h2>
<p>Match Groups can also be named to make returning a particular group easier.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Define a Group Name using ?&lt;Name&gt; at the start of the matching group</span>
MatchAll(<span class="hljs-string">"Text and crabs"</span>, <span class="hljs-string">"(?&lt;crabbymatch&gt;crab)(?&lt;plural&gt;s)"</span>)
</code></pre>
<pre><code class="lang-javascript">(?&lt;crabbymatch&gt;crab)(?&lt;plural&gt;s)
</code></pre>
<p><strong>Pattern Breakdown (?&lt;crabbymatch&gt;crab)(?&lt;plural&gt;s):</strong></p>
<ul>
<li><p><strong>() - Matching Group - Name "crabbymatch"</strong></p>
<ul>
<li><strong>crab</strong> - match the literal word "crab",</li>
</ul>
</li>
<li><p><strong>() - Matching Group - Name "plural"</strong></p>
<ul>
<li><strong>s</strong> - Match the character "s",</li>
</ul>
</li>
</ul>
<p>Creating a named match group creates a column in your <strong>Match()</strong> or <strong>MatchAll()</strong> output:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697123193444/53cfe6fa-6f09-4093-862a-ace1addd83b8.png" alt class="image--center mx-auto" /></p>
<p>You can still use <strong>SubMatches</strong> in this instance and get each SubMatch in your Pattern.</p>
<hr />
<h2 id="heading-manipulate-matching-with-matchoptions">Manipulate Matching with MatchOptions</h2>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-ismatch#match-options">MatchOptions allow us to manipulate the way the Match functions work</a>. They again work as enumerated values but this time they represent Regular Expression Flags, rather than patterns. These Regular Expression Flags also exist in Regex101, so we can enable/disable them as needed. They can also be strung together with ampersands (&amp;).</p>
<p>As an example, if we have a string that contains a block of numbers at the end of the string, we can extract just the last four by employing <strong>MatchOptions.EndsWith</strong>:</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10122023"</span>},
    Match(
        MyString,
        Match.Digit &amp; <span class="hljs-string">"{4}"</span>,
        MatchOptions.EndsWith
    ).FullMatch
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696952643331/679eafc2-d7af-4faf-bf9c-ee25c34ef4f8.png" alt class="image--center mx-auto" /></p>
<p>Or by using <strong>MatchOptions.Complete</strong>, we can ensure the entire string matches our pattern, which it won't in the above example:</p>
<pre><code class="lang-javascript">With(
    {<span class="hljs-attr">MyString</span>: <span class="hljs-string">"The date today is 10122023"</span>},
    Match(
        MyString,
        Match.Digit &amp; <span class="hljs-string">"{4}"</span>,
        MatchOptions.Complete
    ).FullMatch
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696952729522/6214d6ea-f6b9-4277-b700-0543efceb41f.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-the-global-flag">The Global Flag ⛳</h2>
<p>One match option missing from the list is the Global Flag <strong>(/g)</strong>. If we jump back to our previous example of formatting phone numbers. We used the <strong>MatchAll()</strong> function and not <strong>Match()</strong>. Using <strong>MatchAll()</strong> is the equivalent of enabling the <em>Global</em> flag in Regular Expression, meaning the pattern will continue to be matched throughout our text.</p>
<p>If we continued to satisfy the pattern in our previous example, we'd see additional matches:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697062243102/4f93869f-3c30-41f9-805c-80451c255eef.png" alt class="image--center mx-auto" /></p>
<p>For instances where we only want to match the first instance (and therefore, disable the Global flag), we just use Match(). The Global flag doesn't have an equivalent MatchOption.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697062310839/f56455ae-e33e-4168-a994-18dfc8c5c677.png" alt class="image--center mx-auto" /></p>
<hr />
<h1 id="heading-conclusion"><strong>Conclusion</strong></h1>
<p>There are many ways to crack this claw, so it's important to remember that there's sometimes no right or wrong way to go about it. What is important to remember, is that should you need to do some more complex string matching, you have the power of Regular Expression behind you to make it happen, however you wish to do so!</p>
<ul>
<li><p>You can find strings within strings using Mid() and Find() functions, when you know the exact text you're looking for</p>
</li>
<li><p>If you know the sort of text you're looking for, but you don't know the exact value, you can use the Match functions.</p>
</li>
<li><p>With Match, you can mix and match literal text, Match Enumerators and Regular Expression</p>
</li>
<li><p>If you need to match a more complex pattern, you can use full Regular Expression, and tools such as RegEx101.com can help write that.</p>
</li>
<li><p>You can manipulate how Match functions work with MatchOptions, which are equivalent to Regular Expression Flags.</p>
</li>
</ul>
<hr />
<h1 id="heading-resources">Resources</h1>
<ul>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-ismatch">Match functions in PowerFx</a></p>
</li>
<li><p><a target="_blank" href="https://regex101.com">Regex101.com</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Breaking Out! Break out of Canvas App code with Error Handling]]></title><description><![CDATA[We've all been there, we have a string of functions we need to run in sequence in PowerFx. This is usually doable as we could formulate some logic as we proceed through the code to check the last function worked correctly. However, there's another wa...]]></description><link>https://blog.powersnacks.org/breaking-out-break-out-of-canvas-app-code-with-error-handling</link><guid isPermaLink="true">https://blog.powersnacks.org/breaking-out-break-out-of-canvas-app-code-with-error-handling</guid><category><![CDATA[Canvas PowerApps]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[PowerFX]]></category><category><![CDATA[error handling]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Fri, 29 Sep 2023 09:28:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1695974785372/1fd209e7-6680-4f6c-9470-8d65ed9a1c98.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We've all been there, we have a string of functions we need to run in sequence in PowerFx. This is usually doable as we could formulate some logic as we proceed through the code to check the last function worked correctly. However, there's another way to break out of code.</p>
<p>For this to work, we can utilise some error handling and work with the Error() function to make a customised break out whenever we need it. We're going to talk about error handling, and once we have a grasp on how to use it, we'll look at how we can use it to our advantage.</p>
<h1 id="heading-error-handling-in-powerfx">Error handling in PowerFx</h1>
<p>Error handling is crucial to ensure your code accounts for the unexpected. No one wants to see generic error messages popup, and incorporating error handling allows us to account for these scenarios, as well as do some more advanced actions like sending to Trace() or giving the user a more meaningful error message.</p>
<p>Before we get into the details, let's look at how we can do a try/catch block in JavaScript. This is a form of error handling where some code is performed, and if it errors, the code in the Catch block is then executed. We also capture the context of the <em>exception.</em></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">try</span>{
     <span class="hljs-comment">//Perform something   </span>
}<span class="hljs-keyword">catch</span>(e){
    <span class="hljs-comment">//do something when error occurs</span>
}
</code></pre>
<p>A try/catch block will run the code in the try clause, and if it errors, the code in the catch clause will be executed. When an error occurs, any code proceeding the error in the try block will not be executed.</p>
<p>But we don't have try/catch blocks in PowerFx, or do we?</p>
<h2 id="heading-example-no-error-handling">Example: No Error Handling</h2>
<p>Here's some sample code for an app, it allows a user to enter two numbers and at the click of a button, it performs a division by those two numbers. The button's OnSelect() code is as follows:</p>
<pre><code class="lang-javascript">Button.OnSelect = txtNumber1 / txtNumber2
</code></pre>
<p>This code will work fine, however, we'll run into an issue if we try and divide by 0:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695727069795/5b4221b0-68d6-4edf-b0fb-137393166139.png" alt class="image--center mx-auto" /></p>
<p>We can use IfError to handle this error better:</p>
<h2 id="heading-iferror">IfError()</h2>
<p>Here's PowerFx's equivalent Try/Catch block. <a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-iferror#error">IfError allows us to run a block of code and then, if an error occurs in that block, perform some other action:</a></p>
<pre><code class="lang-javascript">IfError(
    <span class="hljs-number">1</span> / <span class="hljs-number">0</span>,
    Notify(
        <span class="hljs-string">"An error occured"</span>))
</code></pre>
<p>In the above example, we test the block of code in the first argument (line 2) and if an error occurs, we execute the code in the second argument (Notify).</p>
<p>In the case of IfError, the <strong>entire</strong> block of code in the first argument is processed and then checked for errors. This is important to remember and works a bit differently from a Try/Catch block in more traditional programming languages.</p>
<p>If we string formula together with semicolons in the first argument, all that code will be evaluated/run before we fall back to the second parameter.</p>
<pre><code class="lang-javascript">IfError(
    <span class="hljs-number">1</span> / <span class="hljs-number">0</span>; Patch(Table, { <span class="hljs-attr">Value</span>: <span class="hljs-number">10</span>}),
    Notify(<span class="hljs-string">"An error occured"</span>))
</code></pre>
<p>With this in mind, our example above would execute the Patch statement to the Table data source, even if the first statement errors; because of the way PowerFx works, the entire "block" in the first argument needs to be evaluated to output as a value to the parameter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695904700993/1b696edd-4b7f-4685-8031-09714e4621d3.png" alt class="image--center mx-auto" /></p>
<p>We may encounter a scenario where we wish to only perform the patch when the first part of the code doesn't error. This is especially important if that Patch statement were dependent on the outcome of the first part of the code (where we're dividing 1 by 0).</p>
<p>We can overcome this issue by breaking out our code into separate blocks. IfError(), just like If() allows us to continually add conditions and fallbacks inline:</p>
<pre><code class="lang-javascript">IfError(
    <span class="hljs-number">1</span> / <span class="hljs-number">0</span>,                             <span class="hljs-comment">//Code block 1</span>
    Notify(<span class="hljs-string">"An error occured"</span>),        <span class="hljs-comment">//Fallback for Block 1</span>
    Patch(Table, { <span class="hljs-attr">Value</span>: <span class="hljs-number">10</span>}),        <span class="hljs-comment">//Code block 2</span>
    Notify(<span class="hljs-string">"Another Error Occured"</span>))   <span class="hljs-comment">//Fallback for Block 2</span>
</code></pre>
<p>Our code above is now broken down into separate blocks. This means that if Block 1 fails, we won't execute Block 2.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695906039941/b19f6818-fb41-4ca4-b128-7d045129d5b2.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-getting-error-information"><strong>Getting Error Information</strong></h2>
<p>You may have spotted we're responding with error information when we failover during IfError(). We do this with the FirstError App Variable. AllErrors can also be used. However, FirstError is a convenient way to capture the error that's <em>just occurred</em>.</p>
<h1 id="heading-so-what-about-breaking-out">So, what about breaking out? 🦀🧱</h1>
<p>We've seen how we can break out of a code cycle with error handling, but we may encounter a scenario where we wouldn't want to continue processing not just because of an error, but because a particular value or condition has/hasn't been met.</p>
<h2 id="heading-error">Error()</h2>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-iferror#error">Error() allows us to create custom errors.</a> This allows us to manipulate the IfError function and break out of our code cycle on our terms, rather than errors being produced from our otherwise functioning code.</p>
<p>To use this, we can create our own "errors" using Error() that will break out of IfError when triggered:</p>
<pre><code class="lang-javascript">With(
    { <span class="hljs-attr">evaluation</span>: IfError(RandBetween(<span class="hljs-number">1</span>,<span class="hljs-number">1000</span>) / <span class="hljs-number">5</span>,<span class="hljs-number">0</span>)},
    IfError(
        If(evaluation &gt; <span class="hljs-number">100</span>,
            <span class="hljs-built_in">Error</span>( {<span class="hljs-attr">Kind</span>: ErrorKind.Custom, <span class="hljs-attr">Message</span>: <span class="hljs-string">"Value is over 100"</span>}),
            <span class="hljs-literal">true</span>),
        Notify(FirstError.Message, NotificationType.Error),
        Notify($<span class="hljs-string">"Value is {evaluation}"</span>)))
</code></pre>
<p>So in our example, we're doing the following:</p>
<ul>
<li><p>Performing a sum on any random number between 1 and 1000, divided by 5</p>
</li>
<li><p>Evaluating if the result is &gt; 100</p>
<ul>
<li>If it is, we generate a custom error, if not we return true.</li>
</ul>
</li>
<li><p>If the error is thrown, our Fallback for Code block 1 will kick in, we won't process the notify code at the bottom of the block</p>
</li>
<li><p>If the error didn't occur, we run the code at the end of the block, notifying the user of the resulting number.</p>
</li>
</ul>
<p>This is a basic example, but you may want to employ this in scenarios where you have to patch two different data sources, but the second data source should only be patched following a certain condition following from the first patch, rather than just whether the first patch threw an error or not.</p>
<h1 id="heading-summary">Summary</h1>
<details><summary>What did we learn?</summary><div data-type="detailsContent">We learnt how error handling works and how PowerFx works with blocks of code, which requires a full evaluation before checking for errors. We learnt how we can handle errors with the IfError() function and finally, we looked at how we can both generate errors ourselves and utilise this along with IfError to break out of PowerFx code blocks based on a condition.</div></details>

<h2 id="heading-thats-a-wrap">That's a wrap!</h2>
<p>I hope you found this article helpful not only for breaking out code but understanding error handling in PowerFx in general!</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-errors">Errors function in Power Apps - Power Platform | Microsoft Learn</a></p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-iferror">Error, IfError, IsError, and IsBlankOrError functions in Power Apps - Power Platform | Microsoft Learn</a></p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/error-handling">Understand errors in Power Fx - Power Platform | Microsoft Learn</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[New Component Library - The Social Media Kit]]></title><description><![CDATA[I'm pleased/terrified to announce my first public Component Library, the Social Media Kit.
This Component Library, inspired by Social Media Apps, allows makers to use one or more components together to help them quickly create apps with additional fu...]]></description><link>https://blog.powersnacks.org/new-component-library-the-social-media-kit</link><guid isPermaLink="true">https://blog.powersnacks.org/new-component-library-the-social-media-kit</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[Canvas PowerApps]]></category><category><![CDATA[component libraries]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Tue, 19 Sep 2023 09:00:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694773528656/05e1f4d8-8933-4d27-906e-7a0eed4da768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I'm pleased/terrified to announce my first public Component Library, the Social Media Kit.</p>
<p>This Component Library, inspired by Social Media Apps, allows makers to use one or more components together to help them quickly create apps with additional functionality. It consists of multiple components, ReadMes and a theming system to theme all components consistently.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695041259032/1d7fe3c9-2f48-42aa-8569-823253130bdc.png" alt class="image--center mx-auto" /></p>
<p>/</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695041719971/5846b7da-f300-4565-bb57-d13894636338.png" alt class="image--center mx-auto" /></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">I'd welcome any feedback, bugs, ideas and other contributions to this component library :)</div>
</div>

<p><a target="_blank" href="https://github.com/sgtsnacks-64/SocialMediaKit-PPCL">Find the GitHub Repo here</a></p>
<p>This library may also be making its way to the Power Platform Samples gallery soon :)</p>
]]></content:encoded></item><item><title><![CDATA[Documenting Components 📃]]></title><description><![CDATA[I've already covered a lot of aspects in my previous articles on how to add properties seamlessly to components, how to use modern theming and even the new property types (Experiemental!). In this article, I'm going to share a technique I use to help...]]></description><link>https://blog.powersnacks.org/documenting-components</link><guid isPermaLink="true">https://blog.powersnacks.org/documenting-components</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[Canvas PowerApps]]></category><category><![CDATA[sharingiscaring]]></category><category><![CDATA[component libraries]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Fri, 15 Sep 2023 09:43:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694770967134/79767206-4883-426c-8a3d-ba539f0f529c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've already covered a lot of aspects in my previous articles on how to add properties seamlessly to components, how to use modern theming and even the new property types (Experiemental!). In this article, I'm going to share a technique I use to help with documenting components; or more specifically, providing documentation for components for other makers.</p>
<hr />
<h2 id="heading-what-are-the-options-today">What are the options today?</h2>
<p>Honestly? Not much. I guess this goes hand in hand with promoting components as well, in my opinion it's not the best experience and there's a lot that could be improved with this. Think more App-Store experience, less displaying a list.</p>
<p>You can make others aware of your component by, firstly sharing the component library with them, and then giving it a description. You'll also probably need to actually <em>tell them</em> about the component as well.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQG8Qi1xnOZutQ/article-inline_image-shrink_1500_2232/0/1694698222725?e=1700092800&amp;v=beta&amp;t=OiJJTg7ZfJFA-UkUoUVCRh5xuEQ6w7JNfsVu-TK3AFE" alt /></p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQE0roLXYnaVfg/article-inline_image-shrink_1500_2232/0/1694693806957?e=1700092800&amp;v=beta&amp;t=KOoRU47t4tEAdjApE9-8JdM1gaLp9nXHNxYP8YB2ncw" alt /></p>
<p>There's a few problems with this:</p>
<ol>
<li><p>It's text based, no one will get a visual representation of what the component is, or does. It's not so bad if you obtained the library from somewhere else and you know what you're loading, or you built the component. But what about others? How will they know that Header will look right in their app without loading; yes, the <strong>entire</strong> component library into their App?</p>
</li>
<li><p>It's limited to 1300 characters, so whilst you could go War &amp; Peace with the description, there's still some limitations to this:</p>
</li>
<li><p>This is only shown when you load a component in, it doesn't really serve as persistent documentation users can refer to later on.</p>
</li>
</ol>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFK3yavl5Rc3w/article-inline_image-shrink_1500_2232/0/1694694091005?e=1700092800&amp;v=beta&amp;t=6G8SRCYUm8wT-BnqhuRbgZ8tqeU0T7xwj-fJQBNVbU8" alt /></p>
<hr />
<h2 id="heading-enter-the-readme-input">Enter the ReadMe Input</h2>
<p>What I'm going to propose isn't a perfect solution. It doesn't help with visibility but what it CAN do is help future makers make best use of your components.</p>
<p>ReadMe isn't a new concept, almost all software is shippped with some sort of "Readme" file, letting the user know they should look in the file for more helpful information. <a target="_blank" href="https://en.wikipedia.org/wiki/README">Wikipedia does a great job in summarising this concept</a>.</p>
<p>I've been adding ReadMe documentation directly into my components. How? Through a Text Input Property.</p>
<h3 id="heading-making-a-readme-input">Making a ReadMe input</h3>
<p>The concept is fairly basic, but I do find it's an effective option when others need to use your components. Create a Text Input called ReadMe. We're not going to reference this anywhere in our components, we're not going to access it through any controls or outputs, it'll exist just to serve the ReadMe to makers.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEuPTTdxmC4vQ/article-inline_image-shrink_1500_2232/0/1694694485804?e=1700092800&amp;v=beta&amp;t=R6e2QKXmnq3PmaZfPKKUn4C0fFy6hDIpxJ3jGMstxFE" alt /></p>
<p>But what about the contents? Maybe rightly or wrongly, I just insert a comment block into the default value with the information I want to provide to makers.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHyGNSwlLrozw/article-inline_image-shrink_1000_1488/0/1694694573646?e=1700092800&amp;v=beta&amp;t=Vs5ldOOCX2AvwbFpE1Fznn_ygwXAzv_Wvrj6vSSAZNc" alt /></p>
<p>This has some great benefits, such as:</p>
<ol>
<li><p>Providing URLs to other makers, which can link to more visual examples of the concepts you've incorporated into your components</p>
</li>
<li><p>Allows you to format your text better than a Description box</p>
</li>
<li><p>Nearly limitless character count</p>
</li>
<li><p>Persist in the component. It'll always be available when the component is added. If the user deletes this, it can be retrieved by adding the component.</p>
</li>
</ol>
<h3 id="heading-but-what-should-i-include">But what should I include? 🤔</h3>
<p>Here's an example of what I add to the ReadMe</p>
<ul>
<li><p>Component Library Name</p>
</li>
<li><p>Component Name, maybe also include a description/use case</p>
</li>
<li><p>In the above example, I include an "override" section, as certain controls can be overriden for theming</p>
</li>
<li><p>Inputs: List all inputs, types and what their purpose is. Break down tables and records with an example</p>
</li>
<li><p>Outputs: List all outputs, types and what their purpose is. Break down tables and records with an example</p>
</li>
<li><p>Using new property types? Include Behaviours, Functions and Actions here. What are their inputs, outputs and their purpose/how to use.</p>
</li>
</ul>
<hr />
<p>Let me know what you think of this approach to providing documentation to other makers. I'd love to hear if you have any other approaches to this.</p>
]]></content:encoded></item><item><title><![CDATA[Modern Theming is coming! Are your components ready?🤔]]></title><description><![CDATA[I recently gave a session at the Microsoft Cloud (South Coast) User Group where I talked about Component Libraries and Crabs (Do I talk about anything else? 🦀). It went really well and the ground didn't swallow me up.
I had a great question from one...]]></description><link>https://blog.powersnacks.org/modern-theming-is-coming-are-your-components-ready</link><guid isPermaLink="true">https://blog.powersnacks.org/modern-theming-is-coming-are-your-components-ready</guid><category><![CDATA[microsoft power platform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[Power Apps]]></category><category><![CDATA[Canvas PowerApps]]></category><category><![CDATA[canvas apps]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Thu, 10 Aug 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694721074729/6662f0c1-d5fd-4cb0-8f34-04db17258ade.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently gave a session at the <a target="_blank" href="https://www.linkedin.com/company/microsoft-cloud-south-coast-user-group/">Microsoft Cloud (South Coast) User Group</a> where I talked about Component Libraries and Crabs (Do I talk about anything else? 🦀). It went really well and the ground didn't swallow me up.</p>
<p>I had a great question from one of the audience around theming and keeping that consistent. I'd already discussed using <a target="_blank" href="https://www.linkedin.com/pulse/components-eliminate-property-fatigue-use-record-mike-gowland/?trackingId=%2B7Gax9uBQk23W%2FinuldUgA%3D%3D">a property record to try and simplify this for your fellow makers</a>. I mentioned this as a way to keep your theming consistent for all your components, but you could also adopt the theming structure of something like the <a target="_blank" href="https://www.iammancat.dev/2022/01/power-apps-branding-template-v3/">iAm_ManCat Branding Template V3</a> by <a target="_blank" href="https://www.linkedin.com/in/sanchoharker?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAAA6S7-4BNlUPwPfhywR9YMr738juV_Rgm_g">Sancho Harker</a> to standardise this between your Canvas Apps and Components.</p>
<p>However, the next day, I noticed a post about the Modern Theming Control coming to Preview. I knew I had to check this out, but more importantly, I needed to understand how this impacts Components and Component Libraries.</p>
<hr />
<h2 id="heading-what-is-this-modern-theming">What is this Modern Theming?</h2>
<p><img src="https://media.licdn.com/dms/image/D4E12AQH8Natqr0ZJCw/article-inline_image-shrink_1500_2232/0/1691670841764?e=1700092800&amp;v=beta&amp;t=vc98WnxyI2SwrZqwL4jFajdl2Hg6eoSSC3fIq4ydqSY" alt /></p>
<p>The Modern Theming goes hand-in-hand with the new Modern Controls coming to Power Apps. They're more accessible, themable controls that replace the Classic controls we've grown to.. well, guess that depends what controls we're talking about.</p>
<p>They're based on Microsoft's Fluent Design system and to be honest, they look great! I'm all for anything that can make Maker's lives easier, and it seems like these controls set out to do just that.</p>
<p>If you can't already see the Modern Controls and Modern Themes in Power Apps Studio, it is enabled as a Preview Feature in Settings &gt; Upcoming Features:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEKST6EkDs74g/article-inline_image-shrink_1500_2232/0/1691671006090?e=1700092800&amp;v=beta&amp;t=64JyvQDK5x5dKwcCwzouhdViEhaI0y1_O5DKu_C1BdU" alt /></p>
<hr />
<h2 id="heading-how-do-these-themes-work">How do these themes work?</h2>
<p>Themes are now stored/called in an Enumerated value in the App scope. For example, you can call:</p>
<pre><code class="lang-plaintext">Color = App.Theme.Colors.Darker10
</code></pre>
<p>Each theme have a Colour scheme that goes down from Darker10 to Darker70, up from Lighter 10 to Lighter 80 and a Primary Colour. Primary, Secondary colours etc, were never directly addressable in Studio previously, so this is a great step in the right direction.</p>
<p>Changing your theme will affect any Modern Controls in your App. Using the Enumerated values above, you can assign your theme colours to Classic controls as well, making them compatible with Modern Themes, albeit with some effort required.</p>
<p>The current trade off here are that Modern Controls don't have editable colours, typography, borders etc. In essence, this is to support the new theming control. However, as of now, modern themes don't support borders or typography. Although this is intended to come later <a target="_blank" href="https://learn.microsoft.com/en-us/power-apps/maker/canvas-apps/controls/modern-controls/modern-theming#use-themes-with-power-fx">according to the documentation.</a></p>
<hr />
<h2 id="heading-so-what-about-components">So what about Components?</h2>
<p>I've put a simple component together in a component library. It doesn't <em>do</em> anything special, aside from just containing some modern controls, and a classic control (Text box) with one of the enumerated values mapped to the font colour property. Note I've set my Component Library theme to Orange.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEyHTf0-b23uw/article-inline_image-shrink_1500_2232/0/1691671791986?e=1700092800&amp;v=beta&amp;t=J0ruyauzmq3OTw3L363yIG8CoAE7teAka3tgsRukuio" alt /></p>
<p>I'll fire up a new Canvas App, enable the new theming and import my new component library.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHEP6isg9vRsw/article-inline_image-shrink_1500_2232/0/1691671874851?e=1700092800&amp;v=beta&amp;t=MAT18MoQMsxoeXJTVKBwj4KjvJtXd_hxZ79yUcyLYX0" alt /></p>
<blockquote>
<p>^^ By the way, THIS needs to CHANGE! This should be a portal for makers to discover new components, not a plain text list that leaves a lot to be desired.</p>
</blockquote>
<p>Anyway, because of the way Components are added to Canvas Apps, they sit in that "Classic" section under "Library components". I configured the library to use the Orange theme, I'll add the app to the Canvas App screen:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQGypZeYTUXqfw/article-inline_image-shrink_1500_2232/0/1691672188041?e=1700092800&amp;v=beta&amp;t=WP8w7yq-ubqCS2MP2JDe73svpYJ03shngqwMcHpl_-o" alt /></p>
<p>Hey! Look at that!</p>
<p>My Canvas App is currently set to "Power Apps Blue". No, really, that's the theme name!</p>
<p>And if I change to Crab Red 🦀:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFCGT-jnqQORQ/article-inline_image-shrink_1500_2232/0/1691672272861?e=1700092800&amp;v=beta&amp;t=LAH6UBIsbeuh3n_DBzp91ECaShabs92kGVL-lNQxsPU" alt /></p>
<p><strong>Amazing!</strong></p>
<hr />
<h2 id="heading-what-does-this-prove">What does this prove?</h2>
<p><strong>This proves the following:</strong></p>
<ul>
<li><p><strong>Modern Controls used from a Component Library will inherit the Canvas app's selected theme, not the theme selected in the Component Library itself.</strong></p>
</li>
<li><p><strong>This is also true when we reference the App's enumerated theme values for Classic controls.</strong> So, we can also modernize the classic controls to take advantage of the new theming system in both our Canvas Apps, Custom Pages but also Components in Component Libraries as well. You can "Modernise" your existing Classic Control-based Components by mapping the new theme enumerated values.</p>
</li>
</ul>
<p>Also note, we ideally want the themes to be expanded to include border configuration and typography (fonts etc) as well. I understand custom theming is also on the cards here, which will be great to quickly brand your apps.</p>
<p>But rest assured, your component libraries will be fine, and this feature will just make them <strong>better!</strong></p>
]]></content:encoded></item><item><title><![CDATA[Components - Eliminate "Property Fatigue": Use a Properties Record!]]></title><description><![CDATA[In my first ever article, I mentioned one of my component tricks of using a Properties record instead of individual properties to avoid "property fatigue": Today I'm going to discuss what exactly this is and how to implement it into your own componen...]]></description><link>https://blog.powersnacks.org/components-eliminate-property-fatigue-use-a-properties-record</link><guid isPermaLink="true">https://blog.powersnacks.org/components-eliminate-property-fatigue-use-a-properties-record</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[#canvasapps]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Wed, 02 Aug 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694720672676/825c2706-b463-4187-8bd1-5eb80ab65f55.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://hashnode.com/post/clmjk628w000809mbc12q00zb">In my first ever article</a>, I mentioned one of my component tricks of using a Properties record instead of individual properties to avoid "property fatigue": Today I'm going to discuss what exactly this is and how to implement it into your own components. 🦀</p>
<p>It's an idea originally shown to me by <a target="_blank" href="https://www.linkedin.com/in/jamesryandev?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAABXr0VQBeNYcF2LhDa9HS3n7XJVSXXZMGDw">James Ryan</a> which I wanted to share.</p>
<hr />
<h2 id="heading-property-fatigue">🥱 Property Fatigue?</h2>
<p>Yeah, "Property Fatigue".</p>
<p>It's when you have a component built and you're doing the right thing by making it nice and customizable.</p>
<p><img src="https://media.licdn.com/dms/image/D4D12AQH1995m7M3I9g/article-inline_image-shrink_1000_1488/0/1691017573581?e=1700092800&amp;v=beta&amp;t=lavLw_LAruzsLRS0kxEd3xRg3GcZP8DJdSyRtVPWN00" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>👆 Here is but a humble component, containing one control (A slider). The point of this component is ultimately, to give other makers a way of easily implementing a stepped slider. For example, I have a minimum value of 100 and a maximum of 1000, I want to step up in 100 increments.</p>
<p>Nothing groundbreaking here, but we don't really want to give out a component in PowerApps blue either, so we need a way for other makers to customise this.</p>
<p>Functionally, our component already works and that's thanks to some input data properties we've already implented: Min, Max and Step; these are all number data types.</p>
<p>So let's turn our attention to the slider itself.</p>
<p>We probably want the following configurable:</p>
<ul>
<li><p>Colour of the Rail</p>
</li>
<li><p>Colour of the Handle</p>
</li>
<li><p>Size of the Handle</p>
</li>
<li><p>Thickness of the Rail</p>
</li>
<li><p>Left/Right Padding of the Rail</p>
</li>
<li><p>The border for the control</p>
</li>
<li><p>...</p>
</li>
</ul>
<p>This goes on, we have hover fills, disable fills, the DisplayMode!</p>
<p>We could go and create each property individually:</p>
<p><img src="https://media.licdn.com/dms/image/D4D12AQHp2L6NUeju7g/article-inline_image-shrink_1500_2232/0/1691018307851?e=1700092800&amp;v=beta&amp;t=B6vpjqsJKNwC-NJvWrxxrMQWGRC3d-7FhIPMimmJa9I" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>Wire each property up to each control property, and we're done!</p>
<p>Go ahead and put that bad boy in your App 🦀</p>
<p><img src="https://media.licdn.com/dms/image/D4D12AQFa6Vb9ByHx2Q/article-inline_image-shrink_1500_2232/0/1691018415144?e=1700092800&amp;v=beta&amp;t=4mybYjK9Dwc2FYI6P-IDyR8Dc6yx-_A3MRL6110lm0M" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p><strong>👆 Welcome to property fatigue.</strong></p>
<p>Okay, I've been lazy here and didn't go through with all of it, but imagine if I had! That's a lot of properties even now for someone to <strong><em>manually fill in</em>.</strong></p>
<p>What's also poor game is that:</p>
<ul>
<li><p>Properties can't be grouped or categorised, much like you see in standard components</p>
</li>
<li><p>Whilst you can tab through these, you need to launch the Formula Bar to add anything useful like variables anyway.</p>
</li>
<li><p>These settings aren't easily transportable between apps. Or, put it another way, the config for this component can't be easily shared with others.</p>
</li>
</ul>
<p>🤔 <strong>There must be a better way.</strong></p>
<hr />
<h2 id="heading-enter-the-properties-record">Enter the Properties Record 🦀</h2>
<p>It seems small, but an input record is helpful in this scenario.</p>
<p>The benefits include:</p>
<ul>
<li><p>Editable properties from one location</p>
</li>
<li><p>Settings stored in text, copy and paste to share/reuse</p>
</li>
<li><p>Properties can be grouped logically, comments can even be added!</p>
</li>
</ul>
<p>So, sounds like this solves all our original problems. Let's see what it looks like in practice:</p>
<p>We could use a record like this below as a starting point.</p>
<pre><code class="lang-plaintext">{
    Colours:
    {
        PrimaryColour: ColorValue("#111111"),
        SecondaryColour: ColorValue("#555555"),
        BackgroundColour: Color.Orange
    },

    Font:
    {
        'Font-Family': Font.'Courier New',

        Sizes:{
            Heading: 21,
            Action: 12,
            Text: 13}
    },

    Slider:
    {
        Rail:{
        'Rail-Size': 13,
        'Rail-Thickness': 2},

        Handle:{
            'Handle-Size': 20,
            'Show-Value': false},

        Padding:{
            Top: 0,
            Bottom: 0,
            Left: 10,
            Right: 10}
    }
}
</code></pre>
<p>This gives us some colours, typography and some specific Slider options as well.</p>
<p>We'll need to create a properties record and specify this base record in this input property, so our component knows what properties are available. Add/Remove from this at your own will.</p>
<p>But see how we're grouping properties in nested records? When we come to mapping these to the control in the component, we can use dot notation to go through the record and get the properties we need:</p>
<pre><code class="lang-plaintext">HandleSize = 'Stepped Slider'.Properties.Slider.Handle.'Handle-Size'
</code></pre>
<p>And what's more, you could actually reuse this method across other components as well. Keeping this approach consistent means makers using your components can reuse customised property records with other components. Makers can even store the record as a global variable and call it in each component, provided you're designing it in such a way that it's reusable.</p>
<p>This really aligns with reusability and generally makes things easier for other makers.</p>
<hr />
<h3 id="heading-what-about-overriding-particular-controls">What about overriding particular controls? 🔧</h3>
<p>This is achievable with the <a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-isblank-isempty#coalesce">Coalesce() function</a>.</p>
<p>Example: We want to be able to override Labels with a different font family over the default specified. The Properties record may look something like this:</p>
<pre><code class="lang-plaintext">{  
  Colours:
    {
        PrimaryColour: ColorValue("#111111"),
        SecondaryColour: ColorValue("#555555"),
        BackgroundColour: Color.Orange
    },

    Font:
    {
        'Font-Family': Font.'Courier New',

        Sizes:{
            Heading: 21,
            Action: 12,
            Text: 13}
    },

    Label:
    {
        Font:{
            'Font-Family': Font.'Dancing Script'},

        Padding:{
            Top: 0,
            Bottom: 0,
            Left: 10,
            Right: 10}
    }
}
</code></pre>
<p>We can use the <a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-isblank-isempty#coalesce">Coalesce() function</a> in the component on the Label's Font Property to handle this:</p>
<pre><code class="lang-plaintext">Label.Font = Coalesce(  //If Label specified font isn't present, fall back to standard
             'Label Component'.Properties.Label.Font.'Font-Family', 
             'Label Component'.Properties.Font.'Font-Family')
</code></pre>
<hr />
<h2 id="heading-tldr">TL;DR 📚</h2>
<ul>
<li><p>Use a record input property over individual properties.</p>
</li>
<li><p>Allows using Dot-notation to access properties within nested records</p>
</li>
<li><p>This helps with the theming and customisation of components, helping to make them more reusable. Keep the record consistent across all your components.</p>
</li>
<li><p>Give the option to override default properties with specific control-based nested records, and utilise Coalesce() to handle this.</p>
</li>
</ul>
<hr />
<p>Hopefully, you've found this article and tip helpful. Check out some of my previous articles on the new component property types:</p>
<p><a target="_blank" href="https://hashnode.com/post/clmjkwido000f09l7fw8j1q93">Events</a></p>
<p><a target="_blank" href="https://hashnode.com/post/clmjkqz2g000908l6a0ar31e7">Actions</a></p>
<p><a target="_blank" href="https://hashnode.com/post/clmjkimgk000809l7gogs16pu">Functions</a></p>
]]></content:encoded></item><item><title><![CDATA[Using Events in Components 🥳]]></title><description><![CDATA[Events; one of the new Property Types of Component Libraries, are infact the new name for Behaviors. If you've used Behaviors before, you'll likely find they've been migrated to Events. Don't Panic, Events and Behaviors work in exactly the same way!
...]]></description><link>https://blog.powersnacks.org/using-events-in-components</link><guid isPermaLink="true">https://blog.powersnacks.org/using-events-in-components</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[microsoft power platform]]></category><category><![CDATA[components]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Sun, 16 Jul 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694720533852/406913d3-880b-4420-ad34-2644ef54b315.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Events;</strong> one of the new Property Types of Component Libraries, are infact the new name for Behaviors. If you've used Behaviors before, you'll likely find they've been migrated to Events. <strong>Don't Panic,</strong> Events and Behaviors work in exactly the same way!</p>
<hr />
<h2 id="heading-what-are-events">What are Events</h2>
<p>Events can be attributed to when <strong>something happens</strong> in your app. For example, a control is selected, a control has changed value, a screen has become visible, or a screen has become hidden. These are known as <strong>Events</strong>, and Event Properties allow us to attribute code to these events.</p>
<p>If you haven't used Event Properties in components already, but you have been working with Canvas Apps or Custom Pages, you'll have already worked with Events in the past if your app did anything at all.</p>
<p>Event Properies can be linked to controls within your Component, then from the App side, the maker can specify the code that's executed when the event is triggered. So for example, a <strong>Save Button</strong> in your component can now be programmed on a per-app basis to take action when the user selects that button.</p>
<hr />
<h2 id="heading-adding-the-event-property">Adding the Event Property</h2>
<p>My son loves animals at the moment, and he's started to recite the sounds they make. I thought this would be a great opportunity to demonstrate events, and how we can use Event Parameters as well!</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFkEZyJZRQLlg/article-inline_image-shrink_1500_2232/0/1689337871526?e=1700092800&amp;v=beta&amp;t=EEex6QpIOPGYPGpnUszLsLfp3td2Qu6rqXagjhU2fXM" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>Here's a simple component I've made, we have a Gallery displaying some Animals and a Button. The button's job is to respond to the user the sound of the animal we've selected. However, the plan here is we're going to use a Parameter in the Event Property to return the selected animal sound to the app.</p>
<p>The Gallery's Items code is as follows:</p>
<pre><code class="lang-plaintext">[
    {
        Animal: "Duck",
        Emoji: "🦆",
        Response: "Quack Quack!"
    },
    {
        Animal: "Cow",
        Emoji: "🐄",
        Response: "Mooooooo!"
    },
    {
        Animal: "Tiger",
        Emoji: "🐅",
        Response: "ROAR!!!"
    },
    {
        Animal: "Giraffe",
        Emoji: "🦒",
        Response: Blank()
    }
]
</code></pre>
<p>We'll now add our Event Property to the component:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHBb5YH4UFmvA/article-inline_image-shrink_1500_2232/0/1689338054152?e=1700092800&amp;v=beta&amp;t=N-6DvnlSzcgVz-lzt7cz_l3aJsWDFh3fXD_qLqP-d_0" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>Notice we have a parameter called strResponse, this is going to be exposed to our app only when we trigger the event. This means our App can do whatever the Maker wants with the response, rather than creating an action within our component.</p>
<hr />
<h2 id="heading-wiring-the-event-up">Wiring the Event Up</h2>
<p>We want our event to trigger when we click the button, so we'll need to add the OnButtonSelect() Event Property from the Component to the OnSelect() Event of the button we want to trigger from.</p>
<pre><code class="lang-plaintext">Button.OnSelect = 'Events Demo'.OnButtonSelect(galAnimals.Selected.Response)
</code></pre>
<p>The idea here is we're going to pass in the "Response" property of the <strong>Selected Item</strong> in the Gallery containing our Animals. This maps the value to the <strong>strResponse</strong> parameter, which will be exposed to the app in the <strong>OnButtonSelect()</strong> event property.</p>
<p>It's also important to note we can specify a default value for the parameter as well.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEaRb66gr83Rg/article-inline_image-shrink_1500_2232/0/1689339665308?e=1700092800&amp;v=beta&amp;t=4AmrvUU8tWn1W71g0M0sxZ9zy_zLsfrcsgEyZNNWhSo" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>This should mean that if we don't send a value with the event, and our parameter were optional, one will be specified for us:</p>
<pre><code class="lang-plaintext">OnButtonSelect_strResponse = "Error 404 - Sound Not Found"
</code></pre>
<p>However as our parameter is required, this does not apply. But we could handle this our own way:</p>
<pre><code class="lang-plaintext">Button.OnSelect = 'Events Demo'.OnButtonSelect(
    Coalesce(
        galAnimals.Selected.Response,
        "Error 404 - Sound Not Found"
    )
)
</code></pre>
<p><em>If you've never used</em> <a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/power-fx/reference/function-isblank-isempty#coalesce"><em>Coalesce</em></a><em>, it's a great replacement to If(IsBlank(Val),Val,"That"), one to remember!</em></p>
<hr />
<h2 id="heading-implementing-in-our-app">Implementing in our App</h2>
<p><img src="https://media.licdn.com/dms/image/D4E12AQH02NmHI-5RbA/article-inline_image-shrink_1500_2232/0/1689342878531?e=1700092800&amp;v=beta&amp;t=QgoR5hTWlJecF6zbk070xS61qIc00eid5tJIDe4wjdo" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>With our component now in app, we can handle the event property we configured before.</p>
<p>Select the component and find the OnButtonSelect() Event Property.</p>
<p>We can add the following code:</p>
<pre><code class="lang-plaintext">App_Component.OnButtonSelect = Notify(strResponse)
</code></pre>
<p>Notice in this context, we can reference strResponse, the parameter we configured as part of the Event Property, within our app itself.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFEXWP8mLp5YQ/article-inline_image-shrink_1500_2232/0/1689343391882?e=1700092800&amp;v=beta&amp;t=Bn1ai3YEv6btPSs8jQXfEc3hq1YPRqDsqdqLS41bzzw" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p><strong>This is only accessible within the event property.</strong></p>
<p>So for example, you could have a variable set on button click, and capture the value:</p>
<pre><code class="lang-plaintext">App_Component.OnButtonSelect = Notify(strResponse);
                           UpdateContext({tvSound: strResponse});
</code></pre>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEZwFSt3eb45g/article-inline_image-shrink_1500_2232/0/1689343681632?e=1700092800&amp;v=beta&amp;t=Lf7qLzhfOZ2dVlPObCTizGjawTiwv_AYP7pNLO-gimc" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-but-what-about-event-data-types">But what about Event Data Types?</h2>
<p>Ah! Our article isn't quite over yet!</p>
<p>Did you notice when we created our event, we had to specify a data type?</p>
<p>Admittedly, I'd never explored the purpose of this until now, but it is interesting...</p>
<p>It turns out the result of the code you enter from your App will actually ingest into your component.</p>
<p>Our Button Event Property is currently a Boolean value, it's expecting the result to be a Boolean. And fun fact, the result of Notify() is actually a Boolean. Actually, a lot of functions in PowerApps output a boolean value.</p>
<p>We can actually work with this as well, let's change the code in the button of the component to handle this:</p>
<pre><code class="lang-plaintext">If(
    !'Events Demo'.OnSelectButton(
        Coalesce(
            galAnimals.Selected.Response,
            "Error 404 - Sound not Found"
        )
    ),
    Notify("Could not make sound :'[")
)
</code></pre>
<p>So what we're doing here is, if the result of OnSelectButton() is <strong>False,</strong> we'll add a seperate notification letting the user know we couldn't process the request.</p>
<p>In the real would you could be making an evaluation at the On Select process that determines what your component does next. For demonstration purposes from our App, we can simulate this behavior by Inverting the result of our Notification.</p>
<pre><code class="lang-plaintext">App_Component.OnButtonSelect = !Notify(strResponse)
</code></pre>
<p>And the result:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFn7lsMK3pRBQ/article-inline_image-shrink_1500_2232/0/1689346448130?e=1700092800&amp;v=beta&amp;t=0Q4z-vSgf_qPDJKvInk3rrMxIBsZJc12Og796sK5h0s" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>Likewise we could evaluate the Event as a Text Data Type, and take action if the text = a particular value:</p>
<pre><code class="lang-plaintext">Button.OnSelect() = If(
    'Events Demo'.OnSelectButton(galAnimals.Selected.Response) = LookUp(
        galAnimals.AllItems,
        Animal = "Tiger"
    ).Response,
    Notify(
        "Yo that's a Tiger!",
        NotificationType.Warning
    )
)
</code></pre>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFA60N6jzes_g/article-inline_image-shrink_1500_2232/0/1689347428407?e=1700092800&amp;v=beta&amp;t=75BeMXK7Gs0GPWsJcDlESNXyWwVfyEfAaEGeihaGuI8" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<hr />
<p><strong>In Summary:</strong></p>
<ul>
<li><p>Events Properties are triggered by events that occur in your component</p>
</li>
<li><p>Events Properties are how your Makers can take action from events in your component.</p>
</li>
<li><p>Event Properties can expose Values from within your Component to your Maker's Apps as Parameters.</p>
</li>
<li><p>I don't know what sound Giraffes make</p>
</li>
<li><p>We can use the data type of Event Properties to intercept the behavior of the event based on it's response value from the app.</p>
</li>
</ul>
<hr />
<p>So that's all the Behavior types covered! I hope you've enjoyed these articles as next we're going to look at actually making some components, which will comprise of a new series of articles where we actually build cool stuff that's reusable!</p>
]]></content:encoded></item><item><title><![CDATA[Component Actions, what are they?!]]></title><description><![CDATA[In a previous article, we took a look at one of the new property types of components called Functions. Today we're going to look at Actions, which are eerily similar to Functions 👻

I'll admit that it's taken me longer than I'm willing to admit to g...]]></description><link>https://blog.powersnacks.org/component-actions-what-are-they</link><guid isPermaLink="true">https://blog.powersnacks.org/component-actions-what-are-they</guid><category><![CDATA[microsoft power platform]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[canvas apps]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Sun, 09 Jul 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694720240343/29edc94d-efa0-4a13-b033-031bc8465b70.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://sgtcrab64.hashnode.dev/components-lets-make-a-function"><em>In a previous article, we took a look at one of the new property types of components called Functions</em></a>. Today we're going to look at Actions, which are <em>eerily</em> similar to Functions 👻</p>
<hr />
<p>I'll admit that it's taken me longer than I'm willing to admit to get my head around the purpose of actions, the description given by Microsoft it a little... weird.</p>
<blockquote>
<p>This type of property can be called as a function with parameters, and can contain logic that changes state (side effects).</p>
</blockquote>
<p>So we can call an action like a function, and pass parameters in. All good. But side effects? What's that?</p>
<hr />
<h2 id="heading-so-side-effects-then">So, side effects then?</h2>
<p>Side Effects in PowerFx seem to have a broad meaning; There are two popular functions that have "Side effects":</p>
<p><strong>Select()</strong>, and</p>
<p><strong>Reset()</strong></p>
<p>Select() will simulate a selection of a control (Generally, any that have an OnSelect() function). Reset() will, well, reset a resettable control.</p>
<p>But also, SubmitForm() hass a Side Effect, rightly, because it affects your Edit Form's datasource, or the record passed in.</p>
<p>Now, the current documentation for Action Properties suggests you can update a datasource, but from what I can ascertain; this isn't currently working. <a target="_blank" href="https://github.com/MicrosoftDocs/powerapps-docs/issues/4530">I've raised an issue on the documentation repo and will update this when I hear back.</a></p>
<p>So what we're saying with Actions, is that we can trigger <strong>Side-Effects</strong> within our components of controls within our component. I'll lead by example:</p>
<hr />
<h2 id="heading-so-with-that-lets-make-an-action">So with that, let's make an Action...</h2>
<p>There's a business requirement to track when a crab has graced us with their pincers 🦀. I need to be able to grace our component by either:</p>
<ul>
<li><p>Selecting the button in the component.</p>
</li>
<li><p>By selecting a special button that isn't in our component.</p>
</li>
</ul>
<p>This will be fairly straight forward, we have a component configured with a button and label as below:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHNhloXvL2mqg/article-inline_image-shrink_1500_2232/0/1688932907441?e=1700092800&amp;v=beta&amp;t=wa1J4WzvYDhAF3AEjX0O_gsWEBr0htNkNuQZ_c_--UU" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>The button code isn't much, we're going to increment a variable within the component by 1 with each pinc.. click.</p>
<pre><code class="lang-plaintext">Set(gvClicks, gvClicks + 1)
</code></pre>
<p>With a label to show the results. And.. as you would expect...</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQG7oNdahAL57Q/article-inline_image-shrink_1500_2232/0/1688933597145?e=1700092800&amp;v=beta&amp;t=malO9fvOpqgnVs1JTK4UCepY1U6ez0lSfGULAtthIHE" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>So Action Property Types come in when we want to trigger the <strong>Select()</strong> function of the "Click Me 🦀" button within our component from the App scope.</p>
<hr />
<h2 id="heading-its-about-time-we-made-an-action">It's about time we made an Action!</h2>
<p>Create a property in the component as follows:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQFgeweA70np1g/article-inline_image-shrink_1500_2232/0/1688933730647?e=1700092800&amp;v=beta&amp;t=9psv8GaQvNZbRM_d4BlA6D7STV0-xBrL9x2x9FQXScs" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>Note: If you can't see the additional property types, you'll need to enable Enhanced Component Properties from the Settings menu, you'll want to ensure you have "Enhanced component properties" from "Settings" &gt; "Upcoming features" enabled.</p>
<p>The code we're going to add is simply going to Select the button:</p>
<pre><code class="lang-plaintext">Select(btn_GracePincer)
</code></pre>
<p>Next, we'll need to add our component to a screen so we can <strong>Call this Action:</strong></p>
<hr />
<h2 id="heading-call-the-action">Call the Action</h2>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEHvGxzcALaVw/article-inline_image-shrink_1500_2232/0/1688933997406?e=1700092800&amp;v=beta&amp;t=56u5jK5ZnzHJkISNLgCXRZVvaA2ZeG7F0eJ7fTej-iI" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<p>I've added a telepathic button to the app, along with the component itself.</p>
<p>We can use the "Telepath Pincer" button to call the Action in the Component below (in pink, or orange, or a mixture of the two?).</p>
<p>The OnSelect property of the top button should just call the action as follows:</p>
<pre><code class="lang-plaintext">cmpPincer.GracePincer()
</code></pre>
<p>The result is the code in GracePincer() is triggered, which Selects the "Click Me" button within the component, thus our pincer counter continues to climb.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQE1BYbvZl3Yog/article-inline_image-shrink_1500_2232/0/1688934457028?e=1700092800&amp;v=beta&amp;t=y-S-gceFMM2Braan9bkx-R63czqP9g8e02db_NbUzWc" alt="No alt text provided for this image" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-so-why">So... why?</h2>
<p>Okay, we don't all work with Crabs. But there is a reason Actions exist:</p>
<ul>
<li><p>Components are resettable, but resetting a component resets all controls/variables etc in the components. Actions mean we can specifically target controls/variables etc when doing a reset.</p>
</li>
<li><p>Select() can't be used on components. So actions now give us a way to perform Select()-ish "Side Effect Formula" on components, and we can choose what is/isn't selected.</p>
</li>
<li><p>Once more, you get full access to the Component Scope, so you can add to collections, set variables etc, via the Action. So pass in a record as a parameter, and add it to the components collection! Here's an example below of a component that takes in a staff record (Name, Role and Photo) and adds to a collection within the component:</p>
</li>
</ul>
<hr />
<h2 id="heading-collections-and-records-example">Collections and Records example</h2>
<p>We have a property in a component called "Ingest Record", which takes a record as a parameter:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHw82ogFZ_IRg/article-inline_image-shrink_1500_2232/0/1688940233778?e=1700092800&amp;v=beta&amp;t=MLVAB_XVkhPsi4Tl-jdowOjXbdA2RNoVwwqj0ElZScU" alt="No alt text provided for this image" /></p>
<p>The component has a Gallery to display the contents of the collection <em>colRecords</em>.</p>
<p>The property above has the following code:</p>
<pre><code class="lang-plaintext">Collect(colRecords, Record)
</code></pre>
<p>We define the schema of <em>Record</em> with:</p>
<pre><code class="lang-plaintext">{ 
    Name: "Mike Gowland",
    Role: "Developer",
    Photo: SampleImage
}
</code></pre>
<p>And we can now implement the component in an app, and call <em>IngestRecord,</em> passing in a record that matches the above schema for the <em>Record</em> parameter:</p>
<pre><code class="lang-plaintext">cmpStaff.IngestRecord(
    {
        Name: txtName.Text,
        Role: txtRole.Text,
        Photo: AddMediaButton1.Media
    }
)
</code></pre>
<p>And the results:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHumCA1jhzxRA/article-inline_image-shrink_1500_2232/0/1688940488343?e=1700092800&amp;v=beta&amp;t=hr2bKyxhODc-LEfnPCedmXCxSfNPIKdBr6SuyPbcqno" alt="No alt text provided for this image" /></p>
<p>Oh, and don't forget, action properties give an output too 😁</p>
<pre><code class="lang-plaintext">If(cmpStaff.IngestRecord(
    {
        Name: txtName.Text,
        Role: txtRole.Text,
        Photo: AddMediaButton1.Media
    }
),Notify("Record added"))
</code></pre>
<hr />
<h2 id="heading-next-time"><strong>Next Time</strong></h2>
<p>We'll take a look at Events, which are probably already familiar to those who used Behaviour properties in the past.</p>
<hr />
<p>Thanks for reading this article, I'm wondering, what are your use cases for Actions? I'd love to read them in the comments below 👇</p>
]]></content:encoded></item><item><title><![CDATA[Creating a Dynamic Text Interpolator in PowerFx 📝]]></title><description><![CDATA[Edit 11/07/2023 - It was brought to my attention that an error existed in the example code for the interpolator which used an incomplete version of the Regular Expression statement. This has now been rectified 🦀

I'm in the process of doing a write ...]]></description><link>https://blog.powersnacks.org/creating-a-dynamic-text-interpolator-in-powerfx</link><guid isPermaLink="true">https://blog.powersnacks.org/creating-a-dynamic-text-interpolator-in-powerfx</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[microsoft power platform]]></category><category><![CDATA[sharingiscaring]]></category><category><![CDATA[canvas apps]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Sun, 02 Jul 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694720101990/85d78b3f-ddee-4a42-9216-15e0a8fd6175.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><em>Edit 11/07/2023</em></strong> <em>- It was brought to my attention that an error existed in the example code for the interpolator which used an incomplete version of the Regular Expression statement. This has now been rectified 🦀</em></p>
<hr />
<p>I'm in the process of doing a write up on Component Actions <a target="_blank" href="https://www.linkedin.com/pulse/components-lets-make-function-mike-gowland/?trackingId=ElmW17zlQj26Aj%2BP2prn5w%3D%3D">which follows my previous article on Functions</a>, but I wanted to take a different steer today and discuss a solution I put together.</p>
<p>You may or may not already know about String Inteprolation in Canvas Apps. It's a way to mask variables and values within a text value itself. It's really handy to help make your code more readable; for example you could use:</p>
<pre><code class="lang-plaintext">$"{gvString}, {gvText}, {gvLike}, {gvThis}"
</code></pre>
<p>where you would otherwise use:</p>
<pre><code class="lang-plaintext">Concatenate("String","Text","Like","This")
</code></pre>
<p>or</p>
<pre><code class="lang-plaintext">"String" &amp; "Text" &amp; "Like" &amp; "This"
</code></pre>
<p>Whilst this is great for text that's static in your app, you may come across requirements where you'd like an administrator to be able to edit this text without having to edit the app itself, which isn't a difficult problem in of itself until they also need to be able to <strong>refer to values</strong> <strong>in your app.</strong></p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHFEIWLuUXlqA/article-inline_image-shrink_1500_2232/0/1688392377692?e=1700092800&amp;v=beta&amp;t=ueZk_0DXze8lPhFONPGJzf1V5UOdsGy7s0nIFZkh4uU" alt="No alt text provided for this image" /></p>
<blockquote>
<p>I had this very challenge recently, and this is how I solved it!</p>
</blockquote>
<hr />
<h2 id="heading-planning">Planning</h2>
<p>A problem like this requires some planning to ascertain how this will work exactly. <strong>We want to be able to do the following:</strong></p>
<ul>
<li><p>Present text with replacement tags</p>
</li>
<li><p>Present values</p>
</li>
<li><p>Replace tags with values when presenting to the user.</p>
</li>
<li><p>User can edit the text and add replacement tags, which can be interpreted by the app and rendered correctly.</p>
</li>
</ul>
<p>The first thing we would need to ascertain is <strong>what is our replacement tag?</strong> This is going to be an identifiable piece of text we can look for in our text block to replace with a value. I settled for <a target="_blank" href="https://www.linkedin.com/feed/hashtag/tag">#Tag</a><strong>#</strong> in this instance.</p>
<p>So let's think about some of the approaches we could take.</p>
<p>We have a block of text that looks like this:</p>
<pre><code class="lang-plaintext">"Hello #FirstName# #Surname#"
</code></pre>
<p>And a Table of Tags like this:</p>
<pre><code class="lang-plaintext">Table(
    {
        Tag: "#FirstName#", 
        Value: "Mike"},
    ....
)
</code></pre>
<p>So we'll try and replace any <a target="_blank" href="https://www.linkedin.com/feed/hashtag/tags">#tags</a># in the text with the corresponding <strong>Values</strong> in the <strong>Table.</strong></p>
<p>And with this, we could try the following:</p>
<hr />
<h2 id="heading-trying-find-and-replace">Trying Find() and Replace()</h2>
<p>We could try the Find() function</p>
<pre><code class="lang-plaintext">Find("#FirstName#",txtInputString)
</code></pre>
<p>But we'd need to test every tag:</p>
<pre><code class="lang-plaintext">Concat(
  ForAll(
      colTabs,
      Find(
        ThisRecord.Tag,
        txtInputString)),
      ThisRecord.Value)
</code></pre>
<p>So Find() will return the <strong>Starting Locations as Numbers</strong> of these tags, we'd need to somehow replace them with the tag value.</p>
<p>But how do we iterate over every Find() value, run a Replace() and combine everything back together? What if our replacement text exceeds the length of the replacement tag? Would it overwrite our text? Will the code get too long and unmaintainable?</p>
<p>Let's try another approach:</p>
<hr />
<h2 id="heading-enter-regular-expression-and-matchall">Enter Regular Expression and MatchAll()</h2>
<blockquote>
<p>We can use Regular Expression with the MatchAll() function to describe the text we're looking for. I personally use <a target="_blank" href="http://regex101.com">regex101.com</a> to write and; ultimately, diganose my Regular Expressions as I find it quick and easy to see where it's going wrong, and see useful information on characters I can use. It's super powerful and definietly a weapon to have in your arsenal.</p>
</blockquote>
<p>We're going to combine a few functions here to do what we want:</p>
<ul>
<li><p><strong>MatchAll()</strong> - Will use our Regular Expression to find our tags</p>
</li>
<li><p><strong>ForAll() -</strong> Will iterate through all our results to find our tags and do something</p>
</li>
<li><p><strong>Coalesce()</strong> - Which will help us do the text substitution</p>
</li>
<li><p><strong>Concat() -</strong> Is going to put everything back together</p>
</li>
</ul>
<p>The regular expression we're going to use is as follows:</p>
<pre><code class="lang-plaintext">(&lt;[^&gt;]+&gt;|#\S+#|\S+(?:\s|$)|[.!,]\s*|\s)
</code></pre>
<p><strong>And if this looks like a load of hieroglyphics to you, you're not alone!</strong> You can use <a target="_blank" href="http://Regex101.com">Regex101.com</a> to find out what this expession actually does (A full explaination is out of the scope of this article), and we can also test it's functionality out.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQG78jNAj6ED-A/article-inline_image-shrink_1000_1488/0/1688147029865?e=1700092800&amp;v=beta&amp;t=zrXFjn2lVaCiTANgv7ZNlDD8_tMi538lgDZADj2z1_I" alt="No alt text provided for this image" /></p>
<p>That. Looks. Perfect 👍</p>
<p>And this isn't just matching whole words, it's also handling punctuation as well:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHReBKzFbpUCA/article-inline_image-shrink_1000_1488/0/1688146997952?e=1700092800&amp;v=beta&amp;t=hm1DDaJuBR4t54W06Epi84XYLJdGw7PJqSE2VGM5ROk" alt="No alt text provided for this image" /></p>
<p>It also plays nice with HTML tags, for those using HTMLText Controls 😊</p>
<p>Oh, and normal <a target="_blank" href="https://www.linkedin.com/feed/hashtag/hashtags">#HashTags</a> are left as is too 😁</p>
<p><strong>The basis of what it does is:</strong></p>
<ul>
<li><p>It finds all words with trailing spaces</p>
</li>
<li><p>It finds words encapsulated in #'s, and removes any trailing spaces</p>
</li>
<li><p>The trailing spaces from the words encapsulated in #'s are added as a match after the tags</p>
</li>
<li><p>Punctuation is added as it's own match.</p>
</li>
</ul>
<hr />
<h2 id="heading-the-tags-table">The Tags Table</h2>
<p>We mustn't forget that this solution will be useless unless we declare and use that Tags table. We can delcare a collection in our app like the following:</p>
<pre><code class="lang-plaintext">ClearCollect(
    colTabs,
    Table(
        {
            Tag: "#FirstName#", 
            Value: gvUser.givenName
        },
        {
            Tag: "#Surname#", 
            Value: gvUser.surName
        },
        {
            Tag: "#JobTitle#", 
            Value: gvUser.jobTitle
        },
        {
            Tag: "#Today#",
            Value: Today()
        }))
</code></pre>
<p>This Collection will need to be referenced when we use the formula below.</p>
<hr />
<h2 id="heading-lets-get-matching">Let's get Matching!</h2>
<p>So, combining this expression with MatchAll() will give us a table of words in our script. This is good as it breaks our problem down:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHizXeOMoJYaw/article-inline_image-shrink_1500_2232/0/1688147264709?e=1700092800&amp;v=beta&amp;t=j4dPSlSrFB9ClS4KkYsGW3a6ZDqVz7XQD03M5kagovI" alt="No alt text provided for this image" /></p>
<p>So now we have our text block broken down, and our replacement tags in a state that we can just do a direct replacement, we can reconstruct the above table, replacing tags as we go in a <strong>ForAll()</strong> statement by using <strong>LookUp()</strong> to match the <strong>FullMatch</strong> value in our table of tags, and <strong>Coalesce()</strong> to handle instances where our tags don't match<strong>.</strong></p>
<pre><code class="lang-plaintext">Concat(
    ForAll(
        MatchAll(
            TextInput1.Text, //Get all matches on our RegEx
            "(&lt;[^&gt;]+&gt;|#\S+#|\S+(?:\s|$)|[.!,]\s*|\s)") As Elements,
        Coalesce(               //Coalesce will either replace the tag if
                                //it's found, or just insert the text we're evaluating
        LookUp(
                tags,
                ThisRecord.Tag = Elements.FullMatch
            ).Value,
            Elements.FullMatch
        )
    ),
    ThisRecord.Value       //Finally let's concat the resulting table from the ForAll statement, 
                            //without any seperators
)  //Output as Text)
</code></pre>
<p><strong>And when added into a Function, the end result should look something like this:</strong></p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEgVfwx8BUObQ/article-inline_image-shrink_1000_1488/0/1688393867450?e=1700092800&amp;v=beta&amp;t=5hkr7zcI7oAG300XDjCS0w39om8zxqqAefGcFoo1xX0" alt="No alt text provided for this image" /></p>
<hr />
<h2 id="heading-implementation-tips">Implementation Tips</h2>
<p><strong>Looking to put this into action? Look at these tips below:</strong></p>
<ul>
<li><p>Similar to variable naming conventions, give your tags meaningful names to help your users understand what they will display.</p>
</li>
<li><p>Define in advance what fields you want to present to users to be able to tag. Go beyond the basics and include extras, for example; the current date.</p>
</li>
<li><p>Make sure your users know what tags they can use. Display a list of them whilst they're editing scripts.</p>
</li>
<li><p>You can store the scripts in practically any data source, it may be worth storing a list of tags in a table for reference.</p>
</li>
<li><p>Will this be administered from a Model Driven App? Consider using a Custom Page to modify the scripts so you can implement the code above.</p>
</li>
</ul>
<hr />
<h2 id="heading-next-time">Next Time</h2>
<ul>
<li><p>We'll implement this with full editing capability in a Dataverse table with custom page.</p>
</li>
<li><p>We'll make this a function of a component in a library, so we can easily reuse the functionality.</p>
</li>
</ul>
<hr />
<p>Thanks for reading this article, I hope this helps you implement this functionality in your future apps.</p>
]]></content:encoded></item><item><title><![CDATA[Components - Let's Make a Function!⚙]]></title><description><![CDATA[Continuing with this series on Components and Component Libraries, this article will go over how to write re-usable functions using one of the new Behavior properties of components.
Anyone familiar with any scripting/programming language will have mo...]]></description><link>https://blog.powersnacks.org/components-lets-make-a-function</link><guid isPermaLink="true">https://blog.powersnacks.org/components-lets-make-a-function</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[components]]></category><category><![CDATA[canvas]]></category><category><![CDATA[sharingiscaring]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Sun, 25 Jun 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694719782190/60b502ff-08d4-4d3d-bc36-57cb84f9808b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://sgtcrab64.hashnode.dev/component-libraries-lego-blocking-canvas-apps"><strong>Continuing with this series on Components and Component Libraries</strong></a><strong>, this article will go over how to write re-usable functions using one of the new Behavior properties of components.</strong></p>
<p>Anyone familiar with any scripting/programming language will have more than likely come across a function before. If you haven't though, that's okay!</p>
<hr />
<h2 id="heading-what-is-a-function">What is a function?</h2>
<p><strong><em>A function is a reusable piece of code.</em> we don't like to repeat ourselves, so if we're in a situation where a block of code may be needed more than once, a function might be the answer!</strong></p>
<p>A function will typically takes 0 or more parameters and will usually (but not always!) <em>return</em> data as a particular data type. An example of a function in PowerShell would be:</p>
<pre><code class="lang-plaintext">function addcat ($inputString, $cattiness)
{
    $cat = "🐈" * $cattiness;
    return $inputString + $cat
}
</code></pre>
<p>This function has a super important business function of adding <em>n</em> cats to the end of $inputString. Now that this is a function declared in my <em>veryImportantForTheBusinessScript.ps1</em> file, I don't need to retype this the 25 times I have to call this code, I can simply, instead, do:</p>
<pre><code class="lang-plaintext">addcat -inputString "Classified" -cattiness 7
</code></pre>
<p>and the function will return the string required.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQGVXubiaBHRfw/article-inline_image-shrink_1500_2232/0/1687468305069?e=1700092800&amp;v=beta&amp;t=oJI_zbwOiAMYEGboeUWAKb7F82h8CK4OpmrGODtPVGg" alt="No alt text provided for this image" /></p>
<p>In some ways, they're like printers that <strong>actually work</strong>. You pass your parameters (Print Data, Paper, Toner) and the printer will <strong>return</strong> a printed document, sometimes. But our functions are more reliable than printers.</p>
<blockquote>
<p><strong>The benefits of functions are:</strong></p>
</blockquote>
<ul>
<li><p>Reusability ♻️ Write Once, Deploy Many, or as <a target="_blank" href="https://www.linkedin.com/in/craig-white-?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAADJn4_QBlGurocOqEq6Qw6OE4FboAzJoIMk">Craig White</a> puts it: Don't be WET [Write Everything Twice]</p>
</li>
<li><p>Code Simplification 🦀 Now you don't need to repeat your code, your code will become easier to maintain and read. You can even reduce nested conditions!</p>
</li>
<li><p>Increase maintainability 🔨 Function not working? At least you can now fix that bug in one place, not <strong>every instance where you wrote that code block!</strong></p>
</li>
</ul>
<hr />
<h2 id="heading-so-how-do-i-make-a-function-in-a-canvas-appcustom-page">So how do I make a function in a Canvas App/Custom Page? 🤔</h2>
<p>Whether you're building out your component library to help support your maker community, or you're making a component within a Canvas App; you'll first want to ensure you have "Enhanced component properties" from "Settings" &gt; "Upcoming features". This will enable the ability to add Functions to a component.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEfDypWOAqxdQ/article-inline_image-shrink_1500_2232/0/1687468518171?e=1700092800&amp;v=beta&amp;t=LLwDbfiDhcP-D5CNJR63ITHERxGYDKv-AMCSwQD-Z-U" alt="No alt text provided for this image" /></p>
<blockquote>
<p>⚠️ BUT TAKE HEED! ☝️</p>
<p>This is an "Experimental feature". That means this feature is earmarked as one that could change; or worse, disappear in the future, so <strong>implement at your own risk!</strong></p>
</blockquote>
<p>Enabling this option should now give you additional property types when adding new properties to your components 👇:</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEyBAPpiCBVOg/article-inline_image-shrink_1500_2232/0/1687468988546?e=1700092800&amp;v=beta&amp;t=db0nTNPQBO3_SuDm5gc7u2n74rMAeAKqI9BiRucYMHM" alt="No alt text provided for this image" /></p>
<p>The old options</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQEEU4HEKRzWiQ/article-inline_image-shrink_1500_2232/0/1687469002806?e=1700092800&amp;v=beta&amp;t=OZoYT7iO07sIo_0EKZ99gSZlPNyw25hUlIyRm-CwDoM" alt="No alt text provided for this image" /></p>
<p>New options, with additional property types</p>
<hr />
<h2 id="heading-but-what-do-these-property-types-mean">But what do these "Property types" mean? 🙋</h2>
<ul>
<li><p><strong>Data</strong>: Your standard component property, can be defined as an "Input" or an "Output" and it's data type defined as well, (Text, Number, Record, Table etc). These allow you to bring properties into your component, so for example, you could define a <em>font size</em>, or alternatively return values from your component, like text entered into a text box.</p>
</li>
<li><p><strong>Function</strong>: The focus of this article, this is a property that can be "called" from your app, providing the component is present. Functions cannot access the App or Component variable scope.</p>
</li>
<li><p><strong>Event</strong>: This replaced the previous "Behavior" property type. Events can be triggered from within the component, but handled on the App side. In essence, you can map an event like <em>OnSelect(), OnChange(), OnChecked()</em> etc to a property, for your app to then handle. This can be used to; for example, write a patch function in the app into the component.</p>
</li>
<li><p><strong>Action</strong>: Action properties are similar to Function properties but have scope within the component itself. Actions are super interesting, and there'll be a beefy article coming soon🐄!</p>
</li>
</ul>
<hr />
<h2 id="heading-so-lets-make-a-function">So, let's make a function! 🔨</h2>
<p>Let's use the cat example from my PowerShell example earlier.</p>
<blockquote>
<p>We'll start by creating a Function Output custom property</p>
</blockquote>
<p>Add this to your component and name it "Add Cat"</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQE2XMpOq77blw/article-inline_image-shrink_1500_2232/0/1687470781582?e=1700092800&amp;v=beta&amp;t=1edEPO8yCPlO78bcIz7fowGQ5TnBsPl3dizRaDJGuBk" alt="No alt text provided for this image" /></p>
<p>We then need to add two parameters:</p>
<ul>
<li><p><strong>InputString</strong> | Text | This is the text we wish to manipulate | Required</p>
</li>
<li><p><strong>Cattiness</strong> | Number | How many cats! | Optional</p>
</li>
</ul>
<p><em>Those parameters can be used when calling the function to influence the function, as per the code we'll write shortly.</em></p>
<blockquote>
<p>The next task is to write the logic for our function ✏️</p>
</blockquote>
<p>You can now select the "Add Cat" custom property, or find it in the property drop down [Top Left!]</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQHg60-IX6iJTg/article-inline_image-shrink_1500_2232/0/1687471179708?e=1700092800&amp;v=beta&amp;t=DK5NQaYzowkGqfe8yQF1QfgGvjsE4fGQ99x2hmiFIJ0" alt="No alt text provided for this image" /></p>
<p>Note how our parameters are displayed here, we can open each parameter and manipulate it's default value. This is really important when we work with <strong>records</strong> or <strong>tables</strong> as we'd need to define the default schema of these. For now we can keep <em>Cattiness</em> and <em>InputString</em> as 100 and "Text" respectfully.</p>
<p>Select "AddCat" and enter the following formula:</p>
<pre><code class="lang-plaintext">With(
    {
        cats: ForAll( //For x Cattiness, add a cat
                Sequence(
                    Cattiness),
                    "🐈")
    },
    $"{InputString} {Concat(cats,Value)}") //Concat and return string
</code></pre>
<p>☝️ This mimics what I did in PowerShell by adding x cats to the end of "InputString". The last line sets AddCat to the string value to return to the app.</p>
<blockquote>
<p><strong>That's it! Let's get testing!</strong></p>
</blockquote>
<hr />
<p>Go ahead and add the component into an app, along with two text boxes and a slider for good measure.</p>
<p><img src="https://media.licdn.com/dms/image/D4E12AQH_lC9YbKrhTQ/article-inline_image-shrink_1000_1488/0/1687471849374?e=1700092800&amp;v=beta&amp;t=RAMa6Rcu2ObSpll3T7WoKp8y6q2bUMILyrbl4HLIm_8" alt="No alt text provided for this image" /></p>
<p><em>Well, it's not exactly Kristine Kolodziejski level UX material, but it'll do for now!</em></p>
<p>In the example above, we're using a text box to capture our <em>InputString</em>, a slider to capture the <em>cattiness of it all</em> 😸 and a third text box with <em>DisplayMode = "view"</em> to display the result of our function.</p>
<blockquote>
<p>Now set the output text box to the following code, replacing component names accordingly:</p>
</blockquote>
<pre><code class="lang-plaintext">cmp_Demo.AddCat(txtInputString.Text, sld_Cats.Value)
//cmp_Demo = our component
</code></pre>
<p><em>And, viola! You've created a reusable function!</em></p>
<blockquote>
<p>Easy! Right? And here's some tips to take away:</p>
</blockquote>
<ul>
<li><p><strong>Functions will work if your component is invisible</strong>. So go ahead and set that functions-only component's visibility to a big fat <strong>!true</strong></p>
</li>
<li><p><strong>But consider reducing your component 👣</strong> You could try adding functions in commonly used components.</p>
</li>
<li><p><strong>Make sure your makers know the functions are there! 👀</strong> Use my previous tip of including a "Readme" input property to help unearth your shiny functions!</p>
</li>
<li><p><strong>You cannot access the components Variables or Input/Output values in a Function</strong>. These are completely independent code blocks in your components and rely on <strong>Input Parameters.</strong></p>
</li>
<li><p><strong>We just wrote an output function, but you can also write input functions as well!</strong> So you can offer the ability to write functions from the app INTO the component. But I'm not 100% why... 🤯</p>
</li>
</ul>
<blockquote>
<p>Thanks for reading! Next time...</p>
</blockquote>
<p>We're going to crack Actions and see how they differ to functions. 🦀</p>
]]></content:encoded></item><item><title><![CDATA[Component Libraries - Lego-Blocking Canvas Apps 🧱]]></title><description><![CDATA[If there's one thing I hate doing, it's the same job twice.
Whether it's using an AutoHotKey Script to start my comments out in code, or waiting for all the washing up to pile up so I can tackle it in one go, and not waste hot water......... I'm a bi...]]></description><link>https://blog.powersnacks.org/component-libraries-lego-blocking-canvas-apps</link><guid isPermaLink="true">https://blog.powersnacks.org/component-libraries-lego-blocking-canvas-apps</guid><category><![CDATA[PowerPlatform]]></category><category><![CDATA[microsoft power platform]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[components]]></category><dc:creator><![CDATA[Mike Gowland]]></dc:creator><pubDate>Sun, 18 Jun 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694719140652/804a2b00-a6e3-4f4f-ae61-5f3cc7c6ab61.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-if-theres-one-thing-i-hate-doing-its-the-same-job-twice"><strong>If there's one thing I hate doing, it's the same job twice.</strong></h2>
<p>Whether it's using an AutoHotKey Script to start my comments out in code, or waiting for all the washing up to pile up so I can tackle it in one go, and not waste hot water......... I'm a big fan of reusability. Build once, Deploy many.</p>
<p>If you've built a canvas app, and found yourself having to make a small tweak to many different screens on an element that is consistent across one or more screens. Then some or most of the elements in your app are probably a good candidate for a Component Library. 🤔</p>
<h3 id="heading-theyre-great-for-components-such-as"><strong>They're great for components such as:</strong></h3>
<ul>
<li><p>Headers</p>
</li>
<li><p>Loading Spinners 💫</p>
</li>
<li><p>Toolbars 🛠</p>
</li>
<li><p>Forms 📃</p>
</li>
<li><p>Complex Labels</p>
</li>
<li><p>Pop-ups ❗</p>
</li>
<li><p>Or just building something completely custom and cool 🔧😎</p>
</li>
</ul>
<hr />
<p>When we start talking about Ecosystem enablement, Component Libraries are an absolutely fundamental digital building block that can be utilised by your maker population to speed up build and delivery of solutions across your organisation. They require appropriate nurturing, promotion and governance to get the best out of them.</p>
<p>Your first port of call is to install the <a target="_blank" href="https://learn.microsoft.com/en-us/power-platform/guidance/creator-kit/overview">Creator Kit</a> as this may already contain everything you and your makers need. However, if you need something more bespoke; continue 🚗</p>
<p>If the creator kit isn't what you want, or you want to put your own touch on the basics, create a base Component Library with some basic components that align to your organisation branding standards that your makers can start using right away. They're dead easy to make (about as easy as a Canvas App) and they can save some time for your makers who can now focus on delivering their solution.</p>
<p>Just make sure the Makers know how to install these new components!</p>
<hr />
<p><strong>At ANS</strong>, we were faced with the challenge of building multiple custom pages for a Model-Driven App between multiple makers. It was really important that we built a consistent experience for the end users; but giving a Figma design to 3 makers and expecting them all to interpret it the same way was never going to be an efficient approach.</p>
<p>We chose to use a component library so that all the styling and functionality was pre-built, we could also be safe in the knowledge that supporting each others pages would be possible, as we understood how the components of each page worked.</p>
<p>Ultimately this led to new custom pages being laid out in sub-1 minute time frames and let the makers focus on code and logic. We kept the experience consistent across all these pages and could make centralised changes to the component library to speed up with updating every page.</p>
<hr />
<h3 id="heading-here-are-some-tips-for-building-a-component-library">Here are some tips for building a component library:</h3>
<h3 id="heading-accessibility-doesnt-go-away"><strong>Accessibility doesn't go away</strong></h3>
<p>You MUST continue to incorporate Tab Indexing, Tooltips and accessibility labels just like any Canvas App or Custom Page. Components will be read in Z-Formation by a screen reader (That is, Left to Right, Top to Bottom), see a short demo below on how this looks with Microsoft Narrator. Get those yellow triangles taken care of ⚠🔨</p>
<h3 id="heading-remember-responsive-design"><strong>Remember, Responsive Design</strong></h3>
<p>Your components could be used in Custom Pages, Phone App or Tablet App. Remember to consider either building fully responsive, or tailoring components for different form factors (i.e. Phone or Tablet).</p>
<h3 id="heading-keep-it-the-components-simple"><strong>Keep it (the components) simple!</strong></h3>
<p>Don't make one massive component, make lots of smaller components that can piece together. For example, make a header, toolbar and form, rather than an entire screen component that does all three.</p>
<h3 id="heading-keep-it-the-code-simple"><strong>Keep it (the code) simple!</strong></h3>
<p>Try to avoid using Variables or Collections within the components purely for outputting data. For example, if you need to output a table in a certain format; rather than saving to a collection and pushing that collection to an output, consider using the ForAll() or AddColumns() functions to structure the table output on-the-fly.</p>
<p>Keeping this in the output property helps other devs easily find how and where the output is compiled.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694719206658/8584eab8-348b-4d65-b16e-1df688e65523.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-guiding-your-makers"><strong>Guiding your Makers</strong></h3>
<p>Adding a "Read Me" input property can help your users understand how your component works and how to use it (i.e. putting data in, getting data out, how to make it do the thing). Make sure you comment this out so it doesn't actually do something! 🙀.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694719246500/368a2bc4-dc7f-4ca6-9cbf-bdac5ef682b8.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-avoid-property-fatigue"><strong>Avoid "Property Fatigue"</strong></h3>
<p>Adding lots of properties to a component can make it unmanageable and sometimes even daunting to use. Try to limit individual properties and group properties together as record inputs. A great idea from <a target="_blank" href="https://www.linkedin.com/in/jamesryandev?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAABXr0VQBeNYcF2LhDa9HS3n7XJVSXXZMGDw">James Ryan</a>.</p>
<h3 id="heading-build-your-components-with-customisation-in-mind"><strong>Build your components with customisation in mind</strong></h3>
<p>Keep your components useful and relevant by making it easy to change the colour scheme, border thickness and even the padding. Consider making this a record input with a pre-defined schema to avoid "Property fatigue" on the component.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694719276853/a14726cb-b727-4eec-814e-c5f44b27392e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-components-ultimately-reside-in-the-msapp-file"><strong>Components ultimately reside in the msapp file</strong></h3>
<p>This is important to remember, because changes MUST be imported into your app, updating the component library on it's own is not enough to apply changes. Importing a component from a component library adds it to your App, rather than referencing it from the component library.</p>
<h3 id="heading-you-can-implement-custom-connectors-in-component-libraries"><strong>You can implement Custom Connectors in Component Libraries</strong></h3>
<p>Ensure you manage the deployments of your Custom Connectors across environments, but yes, you can absolutely add Custom Connector functionality to a Component Library.</p>
<h2 id="heading-next-time">Next time</h2>
<p>That's it for now! Next time I'll be exploring how the different behaviours work in Components and how you can build re-usable code!</p>
]]></content:encoded></item></channel></rss>