<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Jørgen Tonvang</title><link href="http://world.optimizely.com" /><updated>2022-09-12T10:36:46.0000000Z</updated><id>https://world.optimizely.com/blogs/jorgen-tonvang/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>How to implement On Page Editing in Blazor</title><link href="https://world.optimizely.com/blogs/jorgen-tonvang/dates/2022/9/on-page-editing-in-blazor/" /><id>&lt;p&gt;We already have some sites up and running on .NET 6/CMS 12 and with Blazor steadily improving with each release, we&#39;re now at a point where we&#39;re looking for an opportunity to use it in a customer project. We&#39;ve evaluated potential issues that need to be adressed for this to happen, and one of the bigger ones that we identified is the lack of On Page Editing compatibility when a property is rendered within a Blazor Component.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;When rendering a property in a component in a front-end framework, you&#39;re responsible for the rendering of the property - and you also need to make sure the property is updated when an editor changes the value in On Page Editing-mode. In Javascript frameworks, like Vue and ReactJs, this is rather straightforward: you add an attribute to the containing html-element of your property, like this:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;&amp;lt;h1 data-epi-edit=&quot;Heading&quot;&amp;gt;
&amp;lt;!-- Render logic --&amp;gt;
&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You then add Javascript code to subscribe to the event &quot;contentSaved&quot; and then update your component as needed.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Since Blazor can&#39;t really subscribe to a javascript-event directly, we need to add a little trickery to get it working - this example is for an implementation of AlloyTech on Blazor Server, but you could use the same principle for Blazor WebAssembly (you&#39;d need to call Content Delivery API instead of using ContentLoader directly). To follow the steps outlined here, you should already have an Optimizely-site with Blazor up and running.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;First, we need to create a file with a JS-event-listener that we&#39;re going to invoke through the JS-interoperability feature of Blazor. I created a file called opeListener.js with this little function:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;function opeListener(dotnethelper) {
    epi.subscribe(&quot;contentSaved&quot;, function (details) {
        dotnethelper.invokeMethodAsync(&#39;RefreshOnSave&#39;, details);
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The parameter &quot;dotnethelper&quot; here actually contains a reference to the .NET object that invoked the JS-function, which in turn allows the event listener to invoke a function on that object (&quot;RefreshOnSave&quot; in this case).&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I then refered the script in _Root.cshtml, just below the inclusion of the Blazor Server-JS:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;_framework/blazor.server.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;~/js/opeListener.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need a way for our .NET-code to hook on to this event listener, so what I did was to create a class for this purpose:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class OnPageEditingService
{
    private IJSRuntime js = null;
    public event EventHandler Refresh;

    public async void Init(IJSRuntime jsRuntime)
    {
        if (js == null)
        {
            js = jsRuntime;
            await js.InvokeAsync&amp;lt;string&amp;gt;(&quot;opeListener&quot;, DotNetObjectReference.Create(this));
        }
    }

    [JSInvokable]
    public void RefreshOnSave()
    {            
         Refresh?.Invoke(this, new EventArgs());
    }                
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the Init method we use JS-interoperability to call the listener we created in the previous step. We also have a method (invokable from JS through the [JSInvokable]-attribute) that triggers an event that will tell our Blazor component to refresh.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The Blazor component itself looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@inject IContentLoader contentLoader;
@inject IContentVersionRepository contentVersionRepository;
@inject IJSRuntime js;
@inject OnPageEditingService opeService;

@if(currentPage?.Heading != null)
{
    &amp;lt;h1 data-epi-edit=&quot;Heading&quot;&amp;gt;@currentPage.Heading&amp;lt;/h1&amp;gt;
}

@if(currentPage?.MainBody != null){
    &amp;lt;div data-epi-edit=&quot;MainBody&quot;&amp;gt;
        @((MarkupString)currentPage.MainBody.ToString())
    &amp;lt;/div&amp;gt;
}

@code {
    [Parameter]
    public int ContentLinkId { get; set; }

    [Parameter]
    public bool EditMode { get; set; }

    private BlazorTestPage currentPage { get; set; }
    private string contextMode = string.Empty;
    
    protected override void OnInitialized()
    {
        @if (EditMode)
        {
            currentPage = GetLatestDraft();
        }
        else
        {
            currentPage = contentLoader.Get&amp;lt;BlazorTestPage&amp;gt;(new ContentReference(ContentLinkId));
        }

        opeService.Refresh += RefreshComponent;

        base.OnInitialized();
    }

    protected override Task OnAfterRenderAsync(bool firstRender)
    {
        opeService.Init(js);             
        return base.OnAfterRenderAsync(firstRender);
    }

    protected async void RefreshComponent(object sender, EventArgs e)
    {
        currentPage = GetLatestDraft();

        await InvokeAsync(() =&amp;gt; StateHasChanged());
    }

    private BlazorTestPage GetLatestDraft()
    {
        var currentVersion = contentVersionRepository.List(new ContentReference(ContentLinkId))
            .OrderByDescending(x =&amp;gt; x.Saved)
            .FirstOrDefault();

        return contentLoader.Get&amp;lt;BlazorTestPage&amp;gt;(currentVersion.ContentLink);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s a fairly simple component that renders two properties - Heading and MainBody. It takes two parameters - the ID of the content (trying to pass the PageData-object directly from the MVC view will result in JSON-serialization issues that I&#39;ve had no luck trying to resolve) and a bool that tells us whether we are in EditMode or not, so we only render the draft if we are. The reason we are not using the ContextModeResolver directly in the component for this is that it relies on HttpContext which is unreliable to use in a Blazor Component.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;There&#39;s not much logic here, but a couple of things to note: We wait until the OnAfterRender-lifecycle event to initiaize our OnPageEditingService - we won&#39;t be able to invoke our JS-listener before then.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Also, in OnInitialized, we hook up the RefreshComponent-method that will fire when the Refresh-event is triggered. For simplicity, we&#39;re doing a full reload of currentPage here, but if you really wanted, you could do a selective update, since the details of what properties were changed is available from our JS-event-listener. To retrieve that data in our OnPageEditingService, we could modify the RefreshOnSave as follows:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[JSInvokable]
public void RefreshOnSave(JsonElement details)
{
    // Logic (make some custom event args and pass to Refresh.Invoke)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this can help to kick start any budding Blazor projects out there :) Let me know in the comments if you have any questions!&lt;/p&gt;</id><updated>2022-09-12T10:36:46.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>MusicFestival Blazor WebAssembly site!</title><link href="https://world.optimizely.com/blogs/jorgen-tonvang/dates/2020/4/music-festival-asp-net-blazor-site/" /><id>&lt;p&gt;While we&#39;re waiting for the official .NET Core support for Episerver to come out (my beta invite must have been lost in the e-mail), I&#39;ve ported the excellent &lt;a href=&quot;https://github.com/episerver/musicfestival-vue-template&quot;&gt;MusicFestival SPA&lt;/a&gt; to run on Blazor WebAssembly - so you can have SPA templates running purely on .NET Core and Blazor with Episerver CMS today.&lt;/p&gt;
&lt;h2&gt;What is Blazor and why should you care?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor&quot;&gt;Blazor&lt;/a&gt; is a free and open source web framework from Microsoft that allows us to create rich and interactive front-ends using C# and .NET Core. The application is compiled to &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt; and served by the browser.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This enables us to create a dynamic front-end totally independent of Javascript, which means you can say good bye to the ever changing chaos of Babel, Webpack, Linters and the several billion files in your node_modules folder.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I can&#39;t speak for all developers, obviously, but after working with AngularJS, ReactJS and Vue the past 6-7 years - the simplicity of Blazor is very refreshing. While it is still in preview and parts of it are a little rough around the edges, it gets new releases frequently and Microsoft definitely puts a lot of effort into it.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;How does it work with Episerver?&lt;/h2&gt;
&lt;p&gt;Since Blazor requires .NET Core, the application needs to have it&#39;s own project - but the compiled WebAssembly-application can be hosted anywhere.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For this site, I&#39;ve set it up so that the Blazor application is published to a subfolder on the Episerver site whenever the project is built. Then there are URL-rewrite rules in place that rewrites all requests to the subdirectory, with exceptions for requests to Content Delivery API and the Episerver-interface.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Blazor&#39;s built in router doesn&#39;t support loading components dynamically from routes that are unknown at compile time, so I made a custom router for this that replaces the default.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I&#39;ll make another blog post or two covering the specifics of working with Episerver and Blazor, but until then, please check it out on Github:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jtonvang/musicfestival-blazor&quot;&gt;https://github.com/jtonvang/musicfestival-blazor&lt;/a&gt;&lt;/p&gt;</id><updated>2020-04-02T12:37:59.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>