{
    "version": "https://jsonfeed.org/version/1",
    "title": "Fear of the 'undefined'",
    "home_page_url": "https://paulmfischer.dev/",
    "feed_url": "https://paulmfischer.dev/posts.json",
    "description": "My thoughts, experiences, and 'fun' issues I encounter on my journey as a developer.",
    "author": {
        "name": "Paul Fischer",
        "url": "https://paulmfischer.dev/"
    },
    "items": [
        {
            "id": "https://paulmfischer.dev/posts/more-thoughts-on-ai/",
            "content_html": "<p>In a <a href=\"https://paulmfischer.dev/posts/generate-todo-with-ai\">previous post</a> I went through a scenario of trying to build out a TODO website utilizing AI with Github Copilot. At that time, I was able to get a TODO app completed, albeit with some complications along the way as well as the app not fully working.  Since then, I have been utilizing Claude and Claude Code a lot more along side Github Copilot.  I am becoming even more impressed with what it is capable of doing since then, especially how it is changing how I work daily.</p>\n<p>I decided to try a more real-world example and utilize Claude/Copilot on my <a href=\"https://github.com/paulmfischer/MediaSet\">MediaSet</a> project.  This is a media collection app that I have been working on/off again for a long time and continually feel like I am not making a lot of progress.  It hasn't helped that I changed the underlying technology a few times which set everything back to the beginning until last year when I solidified those choices.  Then I started hitting a wall when it came to getting features done that I wanted added (like barcode lookup, image retrieval, etc.)  After trying AI to build a TODO app and seeing how it can help move along some feature work, even if not perfect, I thought why not try AI again but in a more complex scenario and see how it goes.</p>\n<p>As I started this work I also changed my approach on how I asked AI to make changes. In my TODO application, I was attempting what a lot of the industry has been calling vibe coding or the perfect prompt. I would give it a single prompt of the entire thing I would want and let it run, making tweaks after it as completed.  When it comes to working on MediaSet, I have been taking two different approaches when it comes to work. 1) Create an implementation document to be followed or 2) Create an Issue/Feature in Github with enough information to do the work.  The approaches do tend to bleed together at times but I have found this to work really well.</p>\n<p>The first approach of creating an implementation document typically stems from an issue or feature I have in mind (I typically add these to the project in Github as Issues to keep track of them). I then ask the AI to look at it and build out an implementation detail document.  This is a markdown file that I have been including in the code base to keep a record of initial approach.  This way, I can review what the changes will entail and then have a discussion with the AI on that document.  This discussion could take some time as we discuss different approaches, or use cases that were not thought of; basically spending this time polishing the implementation.  Once I feel comfortable with the implementation details I ask Claude/Copilot from a fresh session to look at the document and implement it. I tend to start a new session so that it doesn't keep any of the decisions we discarded in the context (I am not positive this is a good thing or not but it has been working).</p>\n<p>The second approach is basically the same as the first, but the Issue/Feature typically has enough information in it that an implementation document is not needed.  If AI looks at it and sees it is more complex, or raises more questions that are easily answerable then I will ask it to create an implementation document for review.</p>\n<p>I have found this approach to work really well. Instead of trying to write a perfect prompt, I spend time having a conversation with the AI coming up with an implementation to get the feature/issue resolved and then let the AI make the actual changes after. This way I am actively involving the AI in the process before any code changes are done and not trying to come up with a perfect prompt and hoping it gets it right. There are times where I still have to ask it for changes either during the work or once it has been completed, but this approach tends to be produce a much more polished output at the end.</p>\n<p>This approach has allowed me to get a lot of new features added to my MediaSet project.  These are features that I feel like I would have had to drag myself through and may or may not have enjoyed it. But by letting AI do the work, I have been able to take a more high level approach to the project which has let me see it in a different light. I am not spending all my time in the guts of the project trying to get something to work, I am focusing on getting full features working and polished which has been a lot more rewarding to me. It allows me to become more of a user that is driving features and not the developer. I can still dig into the guts if I need to or want to but I can really take a step back and let AI manage that while I manage the bigger picture.</p>\n",
            "url": "https://paulmfischer.dev/posts/more-thoughts-on-ai/",
            "title": "More Thoughts on AI",
            "summary": "In a previous post I went through a scenario of trying to build out a TODO website utilizing AI with Github Copilot. At that time, I was able to get a TODO app completed, albeit with some complications along the way as well as the app not fully working.  Since then, I have been utilizing Claude and Claude Code a lot more along side Github Copilot.  I am becoming even more impressed with what it is capable of doing since then, especially how it is changing how I work daily.",
            "date_modified": "2026-04-20T05:00:00.000Z",
            "date_published": "2026-04-20T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/text-versions-of-posts/",
            "content_html": "<p>I recently stumbled upon this blog post by <a href=\"https://shkspr.mobi/blog/2025/12/a-small-collection-of-text-only-websites/\">Terence Eden</a> about serving up blog posts not only as html but also as plain text files.  I like the idea of being able to serve up just text. I have been on this drive lately to try to reduce the size and complexity of pages I serve up lately with personal projects and what better way than just text! Basically serve up as little as possible and preferably little to no javascript.</p>\n<p>Yes, it kind of defeats the whole point of the web and serving up more stylish and dynamic pages to view.  And makes putting pictures in a blog post pointless since a text files cannot display an image but if anything, you can put a hyperlink to the image in the text file.</p>\n<p>So I have now made all my blog posts output as html as well as a text file.  Every post can be viewed by adding <code>index.txt</code> after the slash. For example, to view this as text you can view it at <a href=\"https://paulmfischer.dev/posts/text-versions-of-posts/index.txt\">posts/text-versions-of-posts/index.txt</a></p>\n<p>To be able to make this work with <a href=\"https://lume.land/\">Lume</a>, I created a plugin that converts the original markdown file of the blog post to plain text.  It keeps code blocks around to make things more readable and converts any images or links to just the link so it can still be viewed. The plugin that generates text from markdown can be viewed <a href=\"https://github.com/paulmfischer/fear-of-the-undefined/blob/main/_plugins/txt_output.ts\">here</a>  I have also update the blog to have a link to the text version at the top of every page for easy access.</p>\n",
            "url": "https://paulmfischer.dev/posts/text-versions-of-posts/",
            "title": "Text output of Blog posts",
            "summary": "I recently stumbled upon this blog post by Terence Eden about serving up blog posts not only as html but also as plain text files.  I like the idea of being able to serve up just text. I have been on this drive lately to try to reduce the size and complexity of pages I serve up lately with personal projects and what better way than just text! Basically serve up as little as possible and preferably little to no javascript.",
            "date_modified": "2026-01-19T09:35:00.000Z",
            "date_published": "2026-01-19T09:35:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/generate-todo-with-ai/",
            "content_html": "<p>I have tried to check out some of the AI tools every now and then to see how well they work when it comes to completing programming related tasks.  From my experience so far, they are very hit or miss.  Sometimes it gives me almost exactly what I am looking for, other times, they fail miserably and give me something that is completely wrong or doesn't even compile.</p>\n<p>So I thought why not give it another go with something (relatively?) simple, a todo application built with html, css, and vanilla js that is responsive and includes accessibility.  I utilized Github Copilot and tried to build a Todo application with Claude Sonnet 3.5 and GPT 4.1.  In both cases, I used roughly the same prompt and also did not modify any file by hand.  I asked the agent in both cases to make any changes requested.</p>\n<p>What follows is the conversation I had with GPT to build a Todo application.  I won't include the outputted file after every prompt but will link to the result of the file at the end.  I mostly want to focus on the conversation because a reoccurring theme happened when I tried to build a Todo application with GPT and Claude.</p>\n<p>Me:</p>\n<blockquote>\n<p>Using only latest html, javascript, and css, build a responsive and accessible todo application that saves state into local storage. Make a single todo.html file inside the copilot folder.</p>\n</blockquote>\n<p>GPT:</p>\n<blockquote>\n<p>I will create a single file, copilot/todo.html, containing a responsive and accessible todo app using HTML, CSS, and JavaScript. The app will save todos to localStorage and support adding, toggling, and deleting tasks.</p>\n<p>todo.html\ntodo.html</p>\n<p>has been created with a responsive, accessible todo app using HTML, CSS, and JavaScript. The app saves todos in localStorage and supports adding, toggling, and deleting tasks.</p>\n</blockquote>\n<p>Me:</p>\n<blockquote>\n<p>Update to allow editing of an the existing todo as well as allow the ability to toggle the state of the item.</p>\n</blockquote>\n<p>GPT:</p>\n<blockquote>\n<p>I will update the todo app to allow editing an existing todo item inline. Each todo will have an edit button that, when clicked, replaces the label with a text input and save/cancel buttons. Toggling the state will still be possible via the checkbox or label.</p>\n<p>I'll add the edit functionality to the JavaScript and update the rendering logic accordingly.</p>\n<p>todo.html</p>\n<p>The todo app now supports inline editing of existing items with edit, save, and cancel buttons. You can still toggle the completed state using the checkbox or label.</p>\n</blockquote>\n<p>Me:</p>\n<blockquote>\n<p>Make sure all the buttons are keyboard accessible as well as keeping focus on a button after rerender.</p>\n</blockquote>\n<p>GPT:</p>\n<blockquote>\n<p>I will update the todo app to ensure all buttons are keyboard accessible by setting tabIndex and using semantic &lt;button&gt; elements. After rerendering (e.g., after editing or deleting), I will restore focus to the relevant button (edit, save, cancel, or delete) using a focus management strategy with a global variable to track which button should receive focus.</p>\n<p>This will improve accessibility and usability for keyboard users.</p>\n<p>todo.html</p>\n<p>All buttons are now keyboard accessible, and focus is restored to the relevant button after rerendering to improve accessibility for keyboard users.</p>\n</blockquote>\n<p>Me:</p>\n<blockquote>\n<p>Add some padding between editing a todo name and the save and cancel buttons.  Also remove the crossed out text style when editing a todo item.</p>\n</blockquote>\n<p>GPT:</p>\n<blockquote>\n<p>I will add CSS to provide padding between the edit input and the save/cancel buttons, and update the rendering logic so the crossed-out style is removed when editing a todo item.</p>\n<p>todo.html</p>\n<p>Padding has been added between the edit input and the save/cancel buttons, and the crossed-out style is removed when editing a todo item.</p>\n</blockquote>\n<p>Me:</p>\n<blockquote>\n<p>Make sure that all actions are still keyboard accessible.  Also update the text color of editing a task to not be grayed out.</p>\n</blockquote>\n<p>GPT:</p>\n<blockquote>\n<p>I will ensure all actions remain keyboard accessible and update the edit input's text color to use the default color (not grayed out) when editing a task.</p>\n<p>todo.html</p>\n<p>All actions remain keyboard accessible, and the text color for editing a task is now set to the default (not grayed out).</p>\n</blockquote>\n<p>This conversation went on for a bit more but it was mostly me asking for a small tweak and then also asking to add back keyboard accessibility. But out of the gate, what GPT built was a mostly working Todo application which was pretty impressive.  I did have to ask for some tweaks to make the application work more in how I wanted it to. What was interesting is that I had to keep asking it to add back keyboard accessibility. It was like it kept forgetting I wanted that so any change would remove that ability.  This same behavior happened with Claude Sonnet.  Now granted, I could have included an AGENTS.md file to require all changes to be accessible as well as some other behavior to adhere to.</p>\n<p>After all of that, this is what GPT had built: <a href=\"https://paulmfischer.dev/posts/generate-todo-with-ai/gpt-todo.html\">gpt-todo.html</a></p>\n<p>There is still an issue with the result above in that the edit/delete buttons do not work.  The buttons were working at one point during the conversation but again, GPT would break something after I asked it to make a change to something else.</p>\n<p>I would say I am pretty impressed with what can be accomplished but this was a pretty simple example.  Will see if things continue to improve but it could still use some work, or I could use some more practice on prompting an agent to get closer to exactly what I want from the output.</p>\n",
            "url": "https://paulmfischer.dev/posts/generate-todo-with-ai/",
            "title": "Generate a Todo app using AI",
            "summary": "I have tried to check out some of the AI tools every now and then to see how well they work when it comes to completing programming related tasks.  From my experience so far, they are very hit or miss.  Sometimes it gives me almost exactly what I am looking for, other times, they fail miserably and give me something that is completely wrong or doesn't even compile.",
            "date_modified": "2025-09-14T05:00:00.000Z",
            "date_published": "2025-09-14T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/moving-to-cloudflare-pages/",
            "content_html": "<p>I have moved where my blog is hosted for what is the third time in about a year and a half. Originally my blog was hosted on Github pages, about a year ago (January 2024 I believe) I decided to move to Deno Deploy partially because I am using <a href=\"https://lume.land/\">Lume</a> and partially to just tinker around with Deno Deploy a bit and see how that works.  For what Deno Deploy does and for what I am looking for (a simple and hopefully free hosting location for a SSG blog) it works really well.  I get some analytics to see traffic, can deploy from Github using an actions and I could point my domain to it.</p>\n<p>Near the end of March though, all that changed when I was making some changes to my blog and the deployment kept failing due to a file not being valid utf8.</p>\n<pre><code class=\"hljs language-json\"><span class=\"hljs-punctuation\">{</span>\n  <span class=\"hljs-attr\">\"type\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"error\"</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">\"code\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"deploymentFailed\"</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">\"ctx\"</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">\"The deployment failed: content of 'file:///src/_vendor/npm/registry.npmjs.org/highlight.js/11.11.1/styles/brown-papersq.png' module is not valid utf8\"</span>\n<span class=\"hljs-punctuation\">}</span>\n</code></pre>\n<p>The problem is that this file is part of a library which is used to build my blog, but it shouldn't even be part of the output of my build.  But this is what caused me to decide to move on from Deno Deploy to Cloudflare Pages.</p>\n<p>This error was a bit confusing to me at first because the file in question shouldn't even be making it to Deno Deploy after the blog is built in Github actions.  I was assuming from the way I setup my Github action is that only the <code>_site</code> folder was being sent to Deno Deploy and then it would take over from there.  Looking at the logs though, my entire source code is being sent after the build so that <code>_site</code> exists and can be served using the <code>serve.js</code> file in Deno as the launch point.</p>\n<p>So first thought was to modify my build so it only sends exactly what is needed to run on Deno Deploy, <code>_site</code>, <code>serve.js</code>, and <code>deno.json</code>.  That didn't work though because it appears that Deno would still run through the install process when it ran <code>serve.js</code> which would again pull in <code>brown-papersq.png</code> file and I would get the utf8 is not valid error again.</p>\n<p>My next thought was to remove the dependency that was pulling that file in, which in this case is <a href=\"https://lume.land/plugins/code_highlight/\">code highlight</a> plugin in <a href=\"https://lume.land/\">Lume</a>.  As I started to really think about that tough it just seemed like a bad idea on a couple of fronts. One, I would lose code highlighting from a nice library and would have to find another or do it myself, which I frankly didn't want to do at the time. Second, what happens when another library I pull in has a file that Deno Deploy doesn't like because it isn't valid utf8? Then I am stuck again unable to deploy any updates to my blog unless I rip out the library which is a very annoying thing to do.</p>\n<p>While I was going through all this I also opened a <a href=\"https://github.com/denoland/deploy_feedback/issues/837\">bug with Deno</a> just to see if this could be fixed or if there was something wrong with how I was deploying.  It appears I am not the only one having this issue and no one else has a fix or getting any feedback on if there could be a fix.  I know it has only been about two months but I didn't want to wait around not being able to make changes to my blog because I am unable to properly deploy.  In fact, this <a href=\"https://github.com/denoland/deploy_feedback/issues?q=is%3Aissue%20state%3Aopen%20utf8\">issue appears</a> to have been around for more than a couple of months without any traction on it.</p>\n<p>So I finally decided it was time to find another host (in this case Cloudflare Pages) and come back to Deno Deploy for another project or another time when this is fixed.  I meant to make this post more about the move to Cloudflare Pages but it ended up being more about why I moved to them.  Might follow this up with how that move went because it was pretty smooth for the most part, just a small hiccup when it came to using a custom domain I wasn't super happy with but so far it has worked out.</p>\n",
            "url": "https://paulmfischer.dev/posts/moving-to-cloudflare-pages/",
            "title": "Moving to Cloudflare Pages from Deno Deploy",
            "summary": "I have moved where my blog is hosted for what is the third time in about a year and a half. Originally my blog was hosted on Github pages, about a year ago (January 2024 I believe) I decided to move to Deno Deploy partially because I am using Lume and partially to just tinker around with Deno Deploy a bit and see how that works.  For what Deno Deploy does and for what I am looking for (a simple and hopefully free hosting location for a SSG blog) it works really well.  I get some analytics to see traffic, can deploy from Github using an actions and I could point my domain to it.",
            "date_modified": "2025-05-20T05:00:00.000Z",
            "date_published": "2025-05-20T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/xunit-theory-data/",
            "content_html": "<p>I have been using <code>xUnit</code> for a while and just recently learned about a couple of other ways of supplying test data to <code>Theory</code> tests besides using the <code>InlineData</code> attribute. When using the <code>Theory</code> attribute, I will traditionally add several <code>InlineData</code> attributes to supply the test with test data, but this attribute is limited with the kind of data you can use with it.  Typically, anything beyond a primitive type will not work with <code>InlineData</code>, so you either have to write multiple tests that have the same Act/Assert sections but with different Arrange sections.  Or have the inline data supply a lot of extra piecemeal that is then used to build more objects as part of the Arrange section.  I recently found out about the <a href=\"https://paulmfischer.dev/posts/xunit-theory-data/#using-classdata-attribute\"><code>ClassData</code></a> and the <a href=\"https://paulmfischer.dev/posts/xunit-theory-data/#using-memberdata-attribute\"><code>MemberData</code></a> attributes that allow you to create much more complex test data setups. I will show examples of these but lets start with sample code and the initial way I would have written a test before using <code>InlineData</code>.</p>\n<blockquote>\n<p>Note: Below examples can also be viewed <a href=\"https://github.com/paulmfischer/fear-of-the-undefined-examples/tree/main/xunit-theory\">fear-of-the-undefined-examples repo</a>. All examples can be run with dotnet unless otherwise specified.</p>\n</blockquote>\n<p>Take for example this arbitrary <code>Add</code> method that allows nullable values to be passed.</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Math</span>\n{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-built_in\">int</span> <span class=\"hljs-title\">Add</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">int</span>? a, <span class=\"hljs-built_in\">int</span>? b, <span class=\"hljs-built_in\">int</span>? c</span>)</span>\n  {\n    <span class=\"hljs-keyword\">return</span> (a ?? <span class=\"hljs-number\">0</span>) + (b ?? <span class=\"hljs-number\">0</span>) + (c ?? <span class=\"hljs-number\">0</span>);\n  }\n}\n</code></pre>\n<h2 id=\"using-inlinedata-attribute\" tabindex=\"-1\">Using <code>InlineData</code> Attribute</h2>\n<p>With <code>InlineData</code>, you supply an additional attribute for each set of data you want to test.  Your test method then takes in a parameter for each argument passed.  This works well (especially in this case) where you are only passing primitive values into your test method.  If you need to pass more complex data, you will want to use <a href=\"https://paulmfischer.dev/posts/xunit-theory-data/#using-classdata-attribute\"><code>ClassData</code></a> or <a href=\"https://paulmfischer.dev/posts/xunit-theory-data/#using-memberdata-attribute\"><code>MemberData</code></a>, see the sections below.</p>\n<pre><code class=\"hljs language-csharp\">[<span class=\"hljs-meta\">Theory</span>]\n[<span class=\"hljs-meta\">InlineData(1, 2, 3, 6)</span>]\n[<span class=\"hljs-meta\">InlineData(1, null, 3, 4)</span>]\n[<span class=\"hljs-meta\">InlineData(1, null, null, 1)</span>]\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">Add_ReturnsCorrectValue</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">int</span>? a, <span class=\"hljs-built_in\">int</span>? b, <span class=\"hljs-built_in\">int</span>? c, <span class=\"hljs-built_in\">int</span> expected</span>)</span>\n{\n  <span class=\"hljs-keyword\">var</span> result = Math.Add(a, b, c);\n\n  Assert.Equal(expected, result);\n}\n</code></pre>\n<h2 id=\"using-classdata-attribute\" tabindex=\"-1\">Using <code>ClassData</code> Attribute</h2>\n<p>If you need to pass more complex data, you will want to use <code>ClassData</code> or <code>MemberData</code>. This is where the other attributes really shine! There are two ways to setup a test using <code>ClassData</code>, 1. building a class with test data that extends <code>IEnumerable&lt;object[]&gt;</code>, or 2. building a class with test data that extends <code>TheoryData&lt;T&gt;</code>.</p>\n<h3 id=\"1-test-data-extending-ienumerableobject\" tabindex=\"-1\">1. Test data extending <code>IEnumerable&lt;object[]&gt;</code></h3>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">ClassDataEnumerableGenerator</span> : <span class=\"hljs-title\">IEnumerable</span>&lt;<span class=\"hljs-title\">object</span>[]&gt;\n{\n  <span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">readonly</span> IList&lt;<span class=\"hljs-built_in\">object</span>[]&gt; data =\n  [\n    [<span class=\"hljs-meta\">1, 2, 3, 6</span>],\n    [<span class=\"hljs-meta\">1, null, 3, 4</span>],\n    [<span class=\"hljs-meta\">1, null, null, 1</span>],\n  ];\n    \n  <span class=\"hljs-keyword\">public</span> IEnumerator&lt;<span class=\"hljs-built_in\">object</span>[]&gt; GetEnumerator() =&gt; data.GetEnumerator();\n\n  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() =&gt; GetEnumerator();\n}\n\n[<span class=\"hljs-meta\">Theory</span>]\n[<span class=\"hljs-meta\">ClassData(typeof(ClassDataEnumerableGenerator))</span>]\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">ClassDataEnumerable_Add_ReturnsCorrectValue</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">int</span>? a, <span class=\"hljs-built_in\">int</span>? b, <span class=\"hljs-built_in\">int</span>? c, <span class=\"hljs-built_in\">int</span> expected</span>)</span>\n{\n  <span class=\"hljs-keyword\">var</span> result = Math.Add(a, b, c);\n\n  Assert.Equal(expected, result);\n}\n</code></pre>\n<p>The setup of the <code>data</code> inside the <code>ClassDataEnumerableGenerate</code> could be done in different ways to allow you to generate more complex data as well.  Each item in the list represents one run of the <code>ClassDataEnumerable_Add_ReturnsCorrectValue</code> test with the arguments of the array passed in.</p>\n<h3 id=\"2-test-data-extending-theorydatat\" tabindex=\"-1\">2. Test data extending <code>TheoryData&lt;T&gt;</code></h3>\n<p>The second way of setting up test data is to create a class of test data that extends <code>TheoryData&lt;T&gt;</code>. I find this method a little cleaner when it comes to setting up your test data. You can define a class the describes your test data that I find more readable.  There are some downsides though. One being that it does require you to create classes in your tests for this data. I found using a <code>record</code> helps to keep the bloat down to a minimum if need be.  Another downside is that the test method takes in the class instead of argument for each piece of data. But depending on what you are testing, this might be more useful.  So it really comes down to how you prefer to follow the code and setup tests.</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">record</span> <span class=\"hljs-title\">TestData</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">int</span>? a, <span class=\"hljs-built_in\">int</span>? b, <span class=\"hljs-built_in\">int</span>? c, <span class=\"hljs-built_in\">int</span> expected</span>)</span>;\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">ClassDataTheoryDataGenerator</span> : <span class=\"hljs-title\">TheoryData</span>&lt;<span class=\"hljs-title\">TestData</span>&gt;\n{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-title\">ClassDataTheoryDataGenerator</span>()</span>\n  {\n    Add(<span class=\"hljs-keyword\">new</span> TestData(<span class=\"hljs-number\">1</span>, <span class=\"hljs-number\">2</span>, <span class=\"hljs-number\">3</span>, <span class=\"hljs-number\">6</span>));\n    Add(<span class=\"hljs-keyword\">new</span> TestData(<span class=\"hljs-number\">1</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-number\">3</span>, <span class=\"hljs-number\">4</span>));\n    Add(<span class=\"hljs-keyword\">new</span> TestData(<span class=\"hljs-number\">1</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-number\">1</span>));\n  }\n}\n\n[<span class=\"hljs-meta\">Theory</span>]\n[<span class=\"hljs-meta\">ClassData(typeof(ClassDataTheoryDataGenerator))</span>]\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">ClassDataTheoryData_Add_ReturnsCorrectValue</span>(<span class=\"hljs-params\">TestData testData</span>)</span>\n{\n  <span class=\"hljs-keyword\">var</span> result = Math.Add(testData.a, testData.b, testData.c);\n\n  Assert.Equal(testData.expected, result);\n}\n</code></pre>\n<h2 id=\"using-memberdata-attribute\" tabindex=\"-1\">Using <code>MemberData</code> Attribute</h2>\n<p>Using the <code>MemberData</code> attribute is very similar to the <code>ClassData</code>.  The difference being that you can define different data sets inside the test generator class (<code>MemberDataGenerator</code> in this case) and reference those methods for each test that is needed (<code>[MemberData(nameof(MemberDataGenerator.TestData), MemberType = typeof(MemberDataGenerator))]</code>). In my data generator class I have another method (<code>OtherTestData</code>) that could be used for another test if need be, this allows you to kind of group your test data generator together. You could have one class that has a success data method and an error data method.</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">MemberDataGenerator</span>\n{\n  <span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> IEnumerable&lt;<span class=\"hljs-built_in\">object</span>[]&gt; TestData()\n  {\n    <span class=\"hljs-keyword\">yield</span> <span class=\"hljs-keyword\">return</span> [<span class=\"hljs-number\">1</span>, <span class=\"hljs-number\">2</span>, <span class=\"hljs-number\">3</span>, <span class=\"hljs-number\">6</span>];\n    <span class=\"hljs-keyword\">yield</span> <span class=\"hljs-keyword\">return</span> [<span class=\"hljs-number\">1</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-number\">3</span>, <span class=\"hljs-number\">4</span>];\n    <span class=\"hljs-keyword\">yield</span> <span class=\"hljs-keyword\">return</span> [<span class=\"hljs-number\">1</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-literal\">null</span>, <span class=\"hljs-number\">1</span>];\n  }\n  \n  <span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> IEnumerable&lt;<span class=\"hljs-built_in\">object</span>[]&gt; OtherTestData()\n  {\n    <span class=\"hljs-keyword\">yield</span> <span class=\"hljs-keyword\">return</span> [<span class=\"hljs-string\">\"something\"</span>];\n  }\n}\n\n[<span class=\"hljs-meta\">Theory</span>]\n[<span class=\"hljs-meta\">MemberData(nameof(MemberDataGenerator.TestData), MemberType = typeof(MemberDataGenerator))</span>]\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">MemberDataEnumerable_Add_ReturnsCorrectValue</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">int</span>? a, <span class=\"hljs-built_in\">int</span>? b, <span class=\"hljs-built_in\">int</span>? c, <span class=\"hljs-built_in\">int</span> expected</span>)</span>\n{\n  <span class=\"hljs-keyword\">var</span> result = Math.Add(a, b, c);\n\n  Assert.Equal(expected, result);\n}\n</code></pre>\n",
            "url": "https://paulmfischer.dev/posts/xunit-theory-data/",
            "title": "XUnit Testing with TheoryData",
            "summary": "I have been using xUnit for a while and just recently learned about a couple of other ways of supplying test data to Theory tests besides using the InlineData attribute. When using the Theory attribute, I will traditionally add several InlineData attributes to supply the test with test data, but this attribute is limited with the kind of data you can use with it.  Typically, anything beyond a primitive type will not work with InlineData, so you either have to write multiple tests that have the same Act/Assert sections but with different Arrange sections.  Or have the inline data supply a lot of extra piecemeal that is then used to build more objects as part of the Arrange section.  I recently found out about the ClassData and the MemberData attributes that allow you to create much more complex test data setups. I will show examples of these but lets start with sample code and the initial way I would have written a test before using InlineData.",
            "date_modified": "2025-02-13T05:00:00.000Z",
            "date_published": "2025-02-13T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/automapper-precondition/",
            "content_html": "<p>I recently had a use case where I needed to conditionally map a property when mapping an object.  In the past, I would have mapped the object as you typically do with AutoMapper like so <code>var barInstance = mapper.Map&lt;Bar&gt;(fooInstance)</code> and then set the property on <code>barInstance</code> depending on the state of <code>fooInstance</code>.  I found out that all this can be done in your map configuration with <a href=\"https://docs.automapper.org/en/stable/Conditional-mapping.html\">Conditional Mappings</a> in AutoMapper.</p>\n<p>For example, lets say we have the following two classes:</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Foo</span> {\n  <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">string</span> FirstName { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n  <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">string</span> LastName { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n}\n\n<span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Bar</span> {\n  <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">string</span> FullName { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n}\n</code></pre>\n<p>These objects are not going to map one-to-one, but we can use a PreCondition in our AutoMapper configuration to map <code>FirstName</code> and <code>LastName</code> to <code>FullName</code> only if both properties (<code>FirstName</code>, <code>LastName</code>) are set. (Yes, we lose data in the case of one of them not being set, this is really a contrived example to just talk about using a PreCondition.)</p>\n<p>To do that, we need to setup the PreCondition in our CreateMap like so:</p>\n<pre><code class=\"hljs language-csharp\">CreateMap&lt;Foo, Bar&gt;()\n  .ForMember(dest =&gt; dest.FullName, opt =&gt; {\n    opt.PreCondition(src =&gt; !<span class=\"hljs-built_in\">string</span>.IsNullOrWhiteSpace(src.FirstName) &amp;&amp; !<span class=\"hljs-built_in\">string</span>.IsNullOrWhiteSpace(src.LastName));\n    opt.MapFrom(src =&gt; <span class=\"hljs-built_in\">string</span>.Format(<span class=\"hljs-string\">\"{0} {1}\"</span>, src.FirstName, src.LastName));\n  });\n</code></pre>\n<p>When mapping from <code>Foo</code> to <code>Bar</code> objects now, the <code>FullName</code> property on <code>Bar</code> will only be set if <code>FirstName</code> and <code>LastName</code> are not null or whitespace on <code>Foo</code>.  This makes mapping objects a whole lot cleaner once you realize you can do this in your map configuration instead of in your code.</p>\n<p>There is also the ability to specify <a href=\"https://docs.automapper.org/en/stable/Conditional-mapping.html#conditional-mapping\">Conditional Mapping</a> similar to PreCondition that works if properties have the same name it will only map them if the Condition is true.</p>\n",
            "url": "https://paulmfischer.dev/posts/automapper-precondition/",
            "title": "Automapper PreCondition",
            "summary": "I recently had a use case where I needed to conditionally map a property when mapping an object.  In the past, I would have mapped the object as you typically do with AutoMapper like so var barInstance = mapper.Map<Bar>(fooInstance) and then set the property on barInstance depending on the state of fooInstance.  I found out that all this can be done in your map configuration with Conditional Mappings in AutoMapper.",
            "date_modified": "2024-10-17T05:00:00.000Z",
            "date_published": "2024-10-17T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/NO_ERRORS_SCHEMA/",
            "content_html": "<p>Have you ever been working on an Angular project and you are running tests and you get the following error:\n<img src=\"https://paulmfischer.dev/posts/NO_ERRORS_SCHEMA/known-element-error.png\" alt=\"known-element-error\" inline=\"\">\nThis error is Angular telling you that for that specific test, it found an element in the template that it doesn't know about. Instead of trying to render the element, it throws an error.  This error is due to the test module setup not including an import for that component or module that contains the unknown element.</p>\n<p>For the example error above, this is what my <code>app.component.html</code> file looks like (parts removed for brevity)</p>\n<pre><code class=\"hljs language-html\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1</span>&gt;</span>Hello, {{ title }}<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">h1</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p</span>&gt;</span>Congratulations! Your app is running. 🎉<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">p</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">profile</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">profile</span>&gt;</span>\n</code></pre>\n<p><code>&lt;profile&gt;</code> is another Angular component that I have created to contain some other functionality.  When the application runs, angular knows about this element due to it being registered to the main application. But the test module does not know about it so we get the error!</p>\n<p>Here is what my <code>app.component.spec.ts</code> file looks like (again, parts removed for brevity)</p>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-title function_\">describe</span>(<span class=\"hljs-string\">'AppComponent'</span>, <span class=\"hljs-function\">() =&gt;</span> {\n  <span class=\"hljs-title function_\">beforeEach</span>(<span class=\"hljs-title function_\">async</span> () =&gt; {\n    <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title class_\">TestBed</span>.<span class=\"hljs-title function_\">configureTestingModule</span>({\n      <span class=\"hljs-attr\">imports</span>: [<span class=\"hljs-title class_\">AppComponent</span>],\n    }).<span class=\"hljs-title function_\">compileComponents</span>();\n  });\n});\n</code></pre>\n<p>Typically you would fix this error by adding the component or module to the tests <code>TestBed.configureTestingModule</code> setup like so</p>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-title function_\">describe</span>(<span class=\"hljs-string\">'AppComponent'</span>, <span class=\"hljs-function\">() =&gt;</span> {\n  <span class=\"hljs-title function_\">beforeEach</span>(<span class=\"hljs-title function_\">async</span> () =&gt; {\n    <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title class_\">TestBed</span>.<span class=\"hljs-title function_\">configureTestingModule</span>({\n      <span class=\"hljs-attr\">imports</span>: [<span class=\"hljs-title class_\">AppComponent</span>, <span class=\"hljs-title class_\">ProfileComponent</span>],\n    }).<span class=\"hljs-title function_\">compileComponents</span>();\n  });\n});\n</code></pre>\n<p>or if the component lives in another module</p>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-title function_\">describe</span>(<span class=\"hljs-string\">'AppComponent'</span>, <span class=\"hljs-function\">() =&gt;</span> {\n  <span class=\"hljs-title function_\">beforeEach</span>(<span class=\"hljs-title function_\">async</span> () =&gt; {\n    <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title class_\">TestBed</span>.<span class=\"hljs-title function_\">configureTestingModule</span>({\n      <span class=\"hljs-attr\">imports</span>: [<span class=\"hljs-title class_\">AppComponent</span>, <span class=\"hljs-title class_\">ProfileModule</span>],\n    }).<span class=\"hljs-title function_\">compileComponents</span>();\n  });\n});\n</code></pre>\n<p>The main problem with this, is that you need to add an import for every component or module for every component that would be rendered down the render path, not just direct descendants.  So you testing module setup can become massive at times!</p>\n<p>I recently found a third option that makes your setup a little cleaner.  Specify <code>NO_ERRORS_SCHEME</code> in the test module setup like so</p>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-title function_\">describe</span>(<span class=\"hljs-string\">'AppComponent'</span>, <span class=\"hljs-function\">() =&gt;</span> {\n  <span class=\"hljs-title function_\">beforeEach</span>(<span class=\"hljs-title function_\">async</span> () =&gt; {\n    <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title class_\">TestBed</span>.<span class=\"hljs-title function_\">configureTestingModule</span>({\n      <span class=\"hljs-attr\">imports</span>: [<span class=\"hljs-title class_\">AppComponent</span>],\n      <span class=\"hljs-attr\">schemas</span>: [<span class=\"hljs-variable constant_\">NO_ERRORS_SCHEMA</span>],\n    }).<span class=\"hljs-title function_\">compileComponents</span>();\n  });\n});\n</code></pre>\n<p>So what is <code>NO_ERRORS_SCHEMA</code> doing here? Well if we look at the <a href=\"https://angular.io/api/core/NO_ERRORS_SCHEMA\">API documentation</a> it tells us that</p>\n<blockquote>\n<p>This schema allows you to ignore the errors related to any unknown elements or properties in a template. The usage of this schema is generally discouraged because it prevents useful validation and may hide real errors in your template. Consider using the CUSTOM_ELEMENTS_SCHEMA instead.</p>\n</blockquote>\n<p>Now when you run your tests, Angular will no longer throw an <code>...is not a known element</code> error.  But there is a bit more going on here, when Angular encounters an unknown element, it will render the element as an empty tag and move on.  So it allows you to write <code>shallow</code> component tests which is super powerful if you want your tests to only focus on the component in question and not all the child components it utilizes.</p>\n<p>Keep in mind that using <code>NO_ERRORS_SCHEMA</code> does have some trade-offs as listed in the <a href=\"https://angular.io/guide/testing-components-scenarios#no_errors_schema\">Developer Guide</a>. One of those trades-offs is</p>\n<blockquote>\n<p>The NO_ERRORS_SCHEMA also prevents the compiler from telling you about the missing components and attributes that you omitted inadvertently or misspelled. You could waste hours chasing phantom bugs that the compiler would have caught in an instant.\nwhich could lead to some headaches if you rely on the test framework to validate all your changes.</p>\n</blockquote>\n",
            "url": "https://paulmfischer.dev/posts/NO_ERRORS_SCHEMA/",
            "title": "Angular - Utilizing NO_ERRORS_SCHEMA in tests",
            "summary": "Have you ever been working on an Angular project and you are running tests and you get the following error:\n\nThis error is Angular telling you that for that specific test, it found an element in the template that it doesn't know about. Instead of trying to render the element, it throws an error.  This error is due to the test module setup not including an import for that component or module that contains the unknown element.",
            "date_modified": "2024-03-28T05:00:00.000Z",
            "date_published": "2024-03-28T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/github-actions%5Bbot%5D-denied/",
            "content_html": "<p>I have been working on setting up GitHub Pages for myself and trying out a few themes. Finally settled on the <a href=\"https://github.com/cotes2020/jekyll-theme-chirpy\">chirpy theme</a>. Chirpy has a <a href=\"https://github.com/cotes2020/chirpy-starter/generate\">starter project</a> that you can generate your repository from. So off I went creating a new repo based on the starter project, pulled my new repository down, made a few changes, committed the changes and then pushed my changes.</p>\n<p>Pretty painless process surprisingly which is awesome.</p>\n<p>The only issue is that chirpy requires the use of GitHub actions to do the build/deploy phase. No problem, Github Actions are enabled for repositories by default. Watching the actions run after pushing my change only to see that it failed!  Clicking into the failed continuous-delivery job to see the console logs and it has the following at the end.</p>\n<p><img src=\"https://paulmfischer.dev/posts/github-actions%5Bbot%5D-denied/actions-failed.png\" alt=\"cd-failed-deploy\" inline=\"\"></p>\n<p>My first thought is that I need to grant the <code>github-actions[bot]</code> access to my repository, but how? Did some quick googling and basically nothing on how to give the bot access to my repository so that it can push changes. Looked around in my repositories settings a bit and finally stumbled upon the <em>Workflow permissions</em> section under <em>Settings -&gt; Actions -&gt; General</em>.</p>\n<p><img src=\"https://paulmfischer.dev/posts/github-actions%5Bbot%5D-denied/setting-guide-1.png\" alt=\"settings-actions-general\" inline=\"\"></p>\n<p>Under the <em>Workflow permissions</em> section the value <em>Read repository contents permission</em> is selected which only allows read access for the <code>github-actions[bot]</code>. So simply changing the selection to <em>Read and write permissions</em> and saving to grant the <code>github-actions[bot]</code> read/write access to my repository.</p>\n<p><img src=\"https://paulmfischer.dev/posts/github-actions%5Bbot%5D-denied/setting-guide-2.png\" alt=\"settings-actions-general\" inline=\"\"></p>\n<p>From there, head back to my failed job, click the <em>Re-run jobs</em> dropdown, then <em>Re-run all jobs</em> for the job to run again.  This time the action completed without failure and I could complete the setup of the chirpy theme and I am off an running!</p>\n",
            "url": "https://paulmfischer.dev/posts/github-actions%5Bbot%5D-denied/",
            "title": "github-actions[bot] denied access to repo",
            "summary": "I have been working on setting up GitHub Pages for myself and trying out a few themes. Finally settled on the chirpy theme. Chirpy has a starter project that you can generate your repository from. So off I went creating a new repo based on the starter project, pulled my new repository down, made a few changes, committed the changes and then pushed my changes.",
            "date_modified": "2022-05-10T05:00:00.000Z",
            "date_published": "2022-05-10T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        },
        {
            "id": "https://paulmfischer.dev/posts/Generic-Component-Blazor/",
            "content_html": "<p>Continuing on my Blazor exploration, today I am exploring building a generic component.  I started out building an autocomplete component for a single entity.  It works great for the single entity <code>PublisherViewModel</code> but it does me no good if I want to have an autocomplete for another entity like <code>AuthorViewModel</code>.</p>\n<p>So lets first look at the non-generic autocomplete component (<code>Autocomplete.razor</code>):</p>\n<pre><code class=\"hljs language-csharp\">@using MediaSet.Shared\n\n&lt;div <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"autocomplete\"</span> @onblur=<span class=\"hljs-string\">\"@LostFocus\"</span>&gt;\n    &lt;label <span class=\"hljs-keyword\">for</span>=<span class=\"hljs-string\">\"search\"</span>&gt;Search Publishers&lt;/label&gt;\n    &lt;InputText id=<span class=\"hljs-string\">\"Search\"</span> Class=<span class=\"hljs-string\">\"form-control\"</span> @bind-Value=<span class=\"hljs-string\">\"@SearchValue\"</span> @oninput=<span class=\"hljs-string\">\"@FilterResults\"</span> @onfocus=<span class=\"hljs-string\">\"@Focused\"</span> /&gt;\n    @if (DisplayResults)\n    {\n        &lt;div <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"list-group\"</span>&gt;\n            @foreach (<span class=\"hljs-function\"><span class=\"hljs-keyword\">var</span> item <span class=\"hljs-keyword\">in</span> <span class=\"hljs-title\">FilteredCollection</span>(<span class=\"hljs-params\">SearchValue</span>))</span>\n            {\n                &lt;button type=<span class=\"hljs-string\">\"button\"</span> <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"list-group-item list-group-item-action\"</span> @onclick=<span class=\"hljs-string\">\"@(async (e) =&gt; await SetSelectedItem(item))\"</span>&gt;\n                    @item.Name\n                &lt;/button&gt;\n            }\n        &lt;/div&gt;\n    }\n&lt;/div&gt;\n\n@code {\n    [<span class=\"hljs-meta\">Parameter</span>]\n    <span class=\"hljs-keyword\">private</span> IEnumerable&lt;PublisherViewModel&gt; Publishers { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n\n    [<span class=\"hljs-meta\">Parameter</span>]\n    <span class=\"hljs-keyword\">private</span> EventCallback&lt;PublisherViewModel&gt; OnItemSelected { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-built_in\">string</span> SearchValue { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-built_in\">bool</span> DisplayResults { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; } = <span class=\"hljs-literal\">false</span>;\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">FilterResults</span>(<span class=\"hljs-params\">UIChangeEventArgs ev</span>)</span>\n    {\n        SearchValue = (<span class=\"hljs-built_in\">string</span>)ev.Value;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">Focused</span>()</span>\n    {\n        DisplayResults = <span class=\"hljs-literal\">true</span>;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">LostFocus</span>()</span>\n    {\n        DisplayResults = <span class=\"hljs-literal\">false</span>;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> IEnumerable&lt;PublisherViewModel&gt; <span class=\"hljs-title\">FilteredCollection</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">string</span> searchVal</span>)</span>\n    {\n        <span class=\"hljs-keyword\">return</span> Publishers.Select(x =&gt; <span class=\"hljs-keyword\">new</span> PublisherViewModel {\n            Id = x.Id,\n            Name = x.Name\n        }).Where(x =&gt; <span class=\"hljs-built_in\">string</span>.IsNullOrEmpty(searchVal) || x.Name.Contains(searchVal));\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">async</span> Task <span class=\"hljs-title\">SetSelectedItem</span>(<span class=\"hljs-params\">PublisherViewModel itemSelected</span>)</span>\n    {\n        SearchValue = itemSelected.Name;\n        DisplayResults = <span class=\"hljs-literal\">false</span>;\n        <span class=\"hljs-keyword\">await</span> OnItemSelected.InvokeAsync(itemSelected);\n        <span class=\"hljs-keyword\">base</span>.StateHasChanged();\n    }\n}\n</code></pre>\n<p>The non-generic part about this auto complete that we need to pull out is the <code>PublisherViewModel</code>.  To do that we need to specify a type parameter.  In Blazor, we specify a type parameter for the component at the top of the component like so:</p>\n<pre><code class=\"hljs language-csharp\">@typeparam TEntity\n</code></pre>\n<p>Now we can start to use that type parameter throughout the component and make the component a generic component.\nThere are a few things we need to change, for one, we need to update the signature of <code>FilteredCollection</code> like so</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> IEnumerable&lt;selectitem&gt; <span class=\"hljs-title\">FilteredCollection</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">string</span> searchVal</span>)\n</span></code></pre>\n<p>You will notice that it now returns <code>IEnumerable&lt;SelectItem&gt;</code> instead of <code>IEnumerable&lt;TEntity&gt;</code>. Internally to the autocomplete I am creating a new class like so:</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">SelectItem</span>\n{\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">int</span> Id { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">string</span> Name { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    <span class=\"hljs-keyword\">public</span> TItem Item { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n}\n</code></pre>\n<p>I am doing this mainly because I am doing the filtering inside the autocomplete. To truly make this generic, we could remove the filtering so the autocomplete cares nothing at all about the types but I wanted to make this a simple autocomplete that doesn't require a lot of extra code to make work. The other reason I created an internal class is because this version of Blazor that I am using (v3 preview 6) we cannot put a type constraint on our generic type. If we could use type constraints then we could enforce the collections of objects would have to be of an interface or abstract class that defines a few properties to be used in the autocomplete like so:</p>\n<pre><code class=\"hljs language-csharp\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">IEntity</span>\n{\n    <span class=\"hljs-built_in\">int</span> Id { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    <span class=\"hljs-built_in\">string</span> Name { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n}\n</code></pre>\n<p>This would then make our filter logic easier to work with since we wouldn't need an internal class and we also would not have to use reflection to get to the properties.  There are a few other adjustments to be made such as making the search label an input <code>Parameter</code>, using a <code>RenderFragment</code> for the text in the autocomplete result list.  And then the last thing is to return the actual <code>TEntity</code> item on selection.</p>\n<p>So our new generic autocomplete component looks like the following:</p>\n<pre><code class=\"hljs language-csharp\">@typeparam TEntity\n\n&lt;div <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"autocomplete\"</span> @onblur=<span class=\"hljs-string\">\"@LostFocus\"</span>&gt;\n    &lt;label <span class=\"hljs-keyword\">for</span>=<span class=\"hljs-string\">\"search\"</span>&gt;@Title&lt;/label&gt;\n    &lt;InputText id=<span class=\"hljs-string\">\"Search\"</span> Class=<span class=\"hljs-string\">\"form-control\"</span> @bind-Value=<span class=\"hljs-string\">\"@SearchValue\"</span> @oninput=<span class=\"hljs-string\">\"@FilterResults\"</span> @onfocus=<span class=\"hljs-string\">\"@Focused\"</span> /&gt;\n    @if (DisplayResults)\n    {\n        &lt;div <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"list-group\"</span>&gt;\n            @foreach (<span class=\"hljs-function\"><span class=\"hljs-keyword\">var</span> item <span class=\"hljs-keyword\">in</span> <span class=\"hljs-title\">FilteredCollection</span>(<span class=\"hljs-params\">SearchValue</span>))</span>\n            {\n                &lt;button type=<span class=\"hljs-string\">\"button\"</span> <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"list-group-item list-group-item-action\"</span> @onclick=<span class=\"hljs-string\">\"@(async (e) =&gt; await SetSelectedItem(item))\"</span>&gt;@ItemTemplate(item.Item)&lt;/button&gt;\n            }\n        &lt;/div&gt;\n    }\n&lt;/div&gt;\n\n@code {\n    [<span class=\"hljs-meta\">Parameter</span>]\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-built_in\">string</span> Title { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; } = <span class=\"hljs-string\">\"Search:\"</span>;\n\n    [<span class=\"hljs-meta\">Parameter</span>]\n    RenderFragment&lt;TEntity&gt; ItemTemplate { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n\n    [<span class=\"hljs-meta\">Parameter</span>]\n    <span class=\"hljs-keyword\">private</span> IEnumerable&lt;TEntity&gt; Collection { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n\n    [<span class=\"hljs-meta\">Parameter</span>]\n    <span class=\"hljs-keyword\">private</span> EventCallback&lt;TEntity&gt; OnItemSelected { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-built_in\">string</span> SearchValue { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-built_in\">bool</span> DisplayResults { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; } = <span class=\"hljs-literal\">false</span>;\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">FilterResults</span>(<span class=\"hljs-params\">UIChangeEventArgs ev</span>)</span>\n    {\n        SearchValue = (<span class=\"hljs-built_in\">string</span>)ev.Value;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">Focused</span>()</span>\n    {\n        DisplayResults = <span class=\"hljs-literal\">true</span>;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">LostFocus</span>()</span>\n    {\n        DisplayResults = <span class=\"hljs-literal\">false</span>;\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> IEnumerable&lt;SelectItem&gt; <span class=\"hljs-title\">FilteredCollection</span>(<span class=\"hljs-params\"><span class=\"hljs-built_in\">string</span> searchVal</span>)</span>\n    {\n        <span class=\"hljs-keyword\">return</span> Collection.Select(x =&gt; <span class=\"hljs-keyword\">new</span> SelectItem {\n            Id = (<span class=\"hljs-built_in\">int</span>)x.GetType().GetProperty(<span class=\"hljs-string\">\"Id\"</span>)?.GetValue(x, <span class=\"hljs-literal\">null</span>),\n            Name = (<span class=\"hljs-built_in\">string</span>)x.GetType().GetProperty(<span class=\"hljs-string\">\"Name\"</span>)?.GetValue(x, <span class=\"hljs-literal\">null</span>),\n            Item = x\n        }).Where(x =&gt; <span class=\"hljs-built_in\">string</span>.IsNullOrEmpty(searchVal) || x.Name.Contains(searchVal));\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">async</span> Task <span class=\"hljs-title\">SetSelectedItem</span>(<span class=\"hljs-params\">SelectItem itemSelected</span>)</span>\n    {\n        SearchValue = itemSelected.Name;\n        Value = itemSelected.Item;\n        DisplayResults = <span class=\"hljs-literal\">false</span>;\n        <span class=\"hljs-keyword\">await</span> OnItemSelected.InvokeAsync(itemSelected.Item);\n        <span class=\"hljs-keyword\">base</span>.StateHasChanged();\n    }\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">SelectItem</span>\n    {\n        <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">int</span> Id { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n        <span class=\"hljs-keyword\">public</span> <span class=\"hljs-built_in\">string</span> Name { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n        <span class=\"hljs-keyword\">public</span> TEntity Item { <span class=\"hljs-keyword\">get</span>; <span class=\"hljs-keyword\">set</span>; }\n    }\n}\n</code></pre>\n<p>So that is the transformation I made to a component built for a single entity to a generic component.  There are some additional things I could of added like server side filtering, the ability to add a new value that is not in the results list.  But I will leave that for another day.</p>\n",
            "url": "https://paulmfischer.dev/posts/Generic-Component-Blazor/",
            "title": "Generic Components in Blazor",
            "summary": "Continuing on my Blazor exploration, today I am exploring building a generic component.  I started out building an autocomplete component for a single entity.  It works great for the single entity PublisherViewModel but it does me no good if I want to have an autocomplete for another entity like AuthorViewModel.",
            "date_modified": "2019-06-21T05:00:00.000Z",
            "date_published": "2019-06-21T05:00:00.000Z",
            "author": {
                "name": "Paul Fischer"
            }
        }
    ]
}