<?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[The Synthesis]]></title><description><![CDATA[The Synthesis]]></description><link>https://blog.retrievo.net</link><generator>RSS for Node</generator><lastBuildDate>Sat, 25 Apr 2026 02:04:06 GMT</lastBuildDate><atom:link href="https://blog.retrievo.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to integrate TinyMCE with the all New Blazor Static Server render mode]]></title><description><![CDATA[Blazor static server-side rendering (static SSR), not to be confused with interactive SSR, has finally landed in .NET 8.
This mode does not require a websocket connection, but keep in mind pages are fully rendered on the server and delivered to the c...]]></description><link>https://blog.retrievo.net/how-to-integrate-tinymce-with-the-all-new-blazor-static-server-render-mode</link><guid isPermaLink="true">https://blog.retrievo.net/how-to-integrate-tinymce-with-the-all-new-blazor-static-server-render-mode</guid><category><![CDATA[Blazor ]]></category><category><![CDATA[SSR]]></category><category><![CDATA[tinymce]]></category><category><![CDATA[Server side rendering]]></category><dc:creator><![CDATA[Janus]]></dc:creator><pubDate>Wed, 14 Feb 2024 22:17:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707948817079/2bc97975-4917-4b32-a524-25a819874371.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Blazor</strong> static server-side rendering (<strong>static SSR</strong>), not to be confused with <strong>interactive SSR</strong>, has finally landed in .NET 8.</p>
<p>This mode does not require a websocket connection, but keep in mind pages are fully rendered on the server and delivered to the client as HTML. This means you can lose some of the exciting interactivity introduced by interactive <strong>SSR</strong> and <strong>CSR</strong> via <strong>WebAssembly</strong>. The upside is that without that constant websocket connection your application is now more scalable. Of course you can introduce interactivity back into the parts of your application that actually need it.</p>
<p>Now unto the main task at hand.</p>
<p>What we will try to achieve is integrating TinyMCE into your <strong>Static SSR</strong> Application without enabling any interactivity. We will accomplish this by configuring Javascript code to execute when a statically rendered page, possibly with enhanced navigation, is loaded or updated.</p>
<p><strong>Step 1.</strong><br />Register with tinyMCE and get your free API Key</p>
<p><strong>Step 2.</strong><br />Generate your Blazor Application. In this case I will just use the template that utilizes Interactive Auto, but per component/page. Not globally. With this mode you must enable interactivity where you need it, so by default everything is rendered in <strong>Static SSR</strong> Mode.</p>
<pre><code class="lang-plaintext">dotnet new blazor -int Auto -o MyTinyMceProject
</code></pre>
<p><strong>Step 3.</strong><br />Enter the project directory and create a Razor Class Library (<strong>RCL</strong>)</p>
<pre><code class="lang-plaintext">dotnet new razorclasslib -o MyTinyMceScriptInitilizer
</code></pre>
<p><strong>Step 4.</strong><br />Configure these projects such that the <strong>RCL</strong> is referenced by the main <strong>Blazor</strong> project</p>
<pre><code class="lang-plaintext">dotnet sln add MyTinyMceProject/MyTinyMceProject.csproj
dotnet sln add MyTinyMceScriptInitilizer/MyTinyMceScriptInitilizer.csproj
dotnet add MyTinyMceProject/MyTinyMceProject.csproj reference MyTinyMceScriptInitilizer/MyTinyMceScriptInitilizer.csproj
</code></pre>
<p><strong>Step 5.</strong><br />In the <strong>RCL</strong> we need to create two files. In the root of the project create a razor component <code>MyPageScript.razor</code> with the following code.</p>
<pre><code class="lang-csharp">&lt;page-script src=<span class="hljs-string">"@Src"</span>&gt;&lt;/page-script&gt;

@code {
    [<span class="hljs-meta">Parameter</span>]
    [<span class="hljs-meta">EditorRequired</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Src { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">default</span>!;
}
</code></pre>
<p>In the <code>wwwroot</code> folder of the <strong>RCL</strong> create a file <code>MyTinyMceScriptInitilizer.lib.module.js</code> with the following code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> pageScriptInfoBySrc = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerPageScriptElement</span>(<span class="hljs-params">src</span>) </span>{
    <span class="hljs-keyword">if</span> (!src) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
            <span class="hljs-string">'Must provide a non-empty value for the "src" attribute.'</span>
        );
    }

    <span class="hljs-keyword">let</span> pageScriptInfo = pageScriptInfoBySrc.get(src);

    <span class="hljs-keyword">if</span> (pageScriptInfo) {
        pageScriptInfo.referenceCount++;
    } <span class="hljs-keyword">else</span> {
        pageScriptInfo = { <span class="hljs-attr">referenceCount</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">module</span>: <span class="hljs-literal">null</span> };
        pageScriptInfoBySrc.set(src, pageScriptInfo);
        initializePageScriptModule(src, pageScriptInfo);
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">unregisterPageScriptElement</span>(<span class="hljs-params">src</span>) </span>{
    <span class="hljs-keyword">if</span> (!src) {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> pageScriptInfo = pageScriptInfoBySrc.get(src);
    <span class="hljs-keyword">if</span> (!pageScriptInfo) {
        <span class="hljs-keyword">return</span>;
    }

    pageScriptInfo.referenceCount--;
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initializePageScriptModule</span>(<span class="hljs-params">src, pageScriptInfo</span>) </span>{
    <span class="hljs-keyword">if</span> (src.startsWith(<span class="hljs-string">'./'</span>)) {
        src = <span class="hljs-keyword">new</span> URL(src.substr(<span class="hljs-number">2</span>), <span class="hljs-built_in">document</span>.baseURI).toString();
    }

    <span class="hljs-keyword">const</span> <span class="hljs-built_in">module</span> = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(src);

    <span class="hljs-keyword">if</span> (pageScriptInfo.referenceCount &lt;= <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span>;
    }

    pageScriptInfo.module = <span class="hljs-built_in">module</span>;
    <span class="hljs-built_in">module</span>.onLoad?.();
    <span class="hljs-built_in">module</span>.onUpdate?.();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onEnhancedLoad</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> [src, { <span class="hljs-built_in">module</span>, referenceCount }] <span class="hljs-keyword">of</span> pageScriptInfoBySrc) {
        <span class="hljs-keyword">if</span> (referenceCount &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">module</span>?.onDispose?.();
            pageScriptInfoBySrc.delete(src);
        }
    }

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> { <span class="hljs-built_in">module</span> } <span class="hljs-keyword">of</span> pageScriptInfoBySrc.values()) {
        <span class="hljs-built_in">module</span>?.onUpdate?.();
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">afterWebStarted</span>(<span class="hljs-params">blazor</span>) </span>{
    customElements.define(
        <span class="hljs-string">'page-script'</span>,
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HTMLElement</span> </span>{
            <span class="hljs-keyword">static</span> observedAttributes = [<span class="hljs-string">'src'</span>];

            attributeChangedCallback(name, oldValue, newValue) {
                <span class="hljs-keyword">if</span> (name !== <span class="hljs-string">'src'</span>) {
                    <span class="hljs-keyword">return</span>;
                }

                <span class="hljs-built_in">this</span>.src = newValue;
                unregisterPageScriptElement(oldValue);
                registerPageScriptElement(newValue);
            }

            disconnectedCallback() {
                unregisterPageScriptElement(<span class="hljs-built_in">this</span>.src);
            }
        }
    );

    blazor.addEventListener(<span class="hljs-string">'enhancedload'</span>, onEnhancedLoad);
}
</code></pre>
<p><strong>Step 6.</strong><br />In the main <code>/Components/Pages</code> folder of the main project create two files.</p>
<p>The first file will be your <strong>Blazor</strong> component in which you want the TinyMCE Editor. We will call this page <code>MyTinyMcePage.razor</code> and in it place the following code.</p>
<pre><code class="lang-csharp">@page <span class="hljs-string">"/tinymce"</span>

&lt;MyTinyMceScriptInitilizer.MyPageScript Src=<span class="hljs-string">"./Components/Pages/MyTinyMcePage.razor.js?tinymce"</span> /&gt;

&lt;EditForm Model=<span class="hljs-string">"MyPageData"</span> OnValidSubmit=<span class="hljs-string">"HandleSubmit"</span> FormName=<span class="hljs-string">"tinymce-page"</span> method=<span class="hljs-string">"POST"</span>/&gt;
    &lt;div&gt;
        &lt;label&gt;Content:&lt;/label&gt;
        &lt;InputTextArea @bind-Value=<span class="hljs-string">"MyPageData.Content"</span> id=<span class="hljs-string">"MyPageData_Content"</span>&gt;
        &lt;/InputTextArea&gt;
    &lt;/div&gt;
&lt;/EditForm&gt;

@code {

    [<span class="hljs-meta">SupplyParameterFromForm(FormName = <span class="hljs-meta-string">"tinyemce-page"</span>)</span>]
    <span class="hljs-keyword">public</span> MyPageModel MyPageData {<span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>;} = <span class="hljs-keyword">new</span>();

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MyPageModel</span>
        {
           <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? Content { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">HandleSubmit</span>(<span class="hljs-params"></span>)</span> {
            Console.Writeline(MyPageData?.Content)
        }
}
</code></pre>
<p>The second file will sit right next to the <strong>Blazor</strong> component and will be called <code>MyTinyMcePage.razor.js</code> and will contain the following code.</p>
<pre><code class="lang-javascript">TinyMceInit = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">selector</span>) </span>{
    tinymce.remove(<span class="hljs-string">`textarea#<span class="hljs-subst">${selector}</span>`</span>);
    tinymce.init({
        <span class="hljs-attr">selector</span>: <span class="hljs-string">`textarea#<span class="hljs-subst">${selector}</span>`</span>,
    });
};

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onLoad</span>(<span class="hljs-params"></span>) </span>{
    TinyMceInit(<span class="hljs-string">'MyPageData_Content'</span>);
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onUpdate</span>(<span class="hljs-params"></span>) </span>{
    TinyMceInit(<span class="hljs-string">'MyPageData_Content'</span>);

}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onDispose</span>(<span class="hljs-params"></span>) </span>{}
</code></pre>
<p><strong>Step 7.</strong><br />Finally, update your <code>App.razor</code> component in the root of your main project to have the link to the tinyMCE CDN. place this</p>
<pre><code class="lang-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tiny.cloud/1/&lt;[YOUR_TINYMCE_API_CODE]&gt;/tinymce/6/tinymce.min.js"</span>
        <span class="hljs-attr">referrerpolicy</span>=<span class="hljs-string">"origin"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>below</p>
<pre><code class="lang-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"_framework/blazor.web.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>That's it! Launch your project, <code>dotnet watch --project MyTinyMceProject --launch-profile https</code> , and enjoy your freshly minted TinyMCE editor.</p>
]]></content:encoded></item><item><title><![CDATA[How to setup a typescript environment for your codewars locally]]></title><description><![CDATA[Setting up Typescript
Note: I have a preference to not install typescript globally and only per project.
This setup utilizes vscode.

Create your project folder and navigate into it.

Create a sub folder "src", in your project folder, in which you wi...]]></description><link>https://blog.retrievo.net/how-to-setup-a-typescript-environment-for-your-codewars-locally</link><guid isPermaLink="true">https://blog.retrievo.net/how-to-setup-a-typescript-environment-for-your-codewars-locally</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Janus]]></dc:creator><pubDate>Thu, 29 Sep 2022 12:56:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664455424253/u3aD1AbAh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-setting-up-typescript">Setting up Typescript</h2>
<p>Note: I have a preference to not install typescript globally and only per project.
This setup utilizes vscode.</p>
<ol>
<li><p>Create your project folder and navigate into it.</p>
</li>
<li><p>Create a sub folder "src", in your project folder, in which you will place your code.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664453329102/gTZVjDUBV.png" alt="Selection_1381.png" /></p>
</li>
<li><p>in the integrated CLI, from your project folder,  run the following command</p>
<pre><code>yarn add -D typescript ts-node
</code></pre></li>
<li><p>Run the following command in the CLI to initialize the typescript setup</p>
<pre><code>npx tsc --init
</code></pre></li>
<li><p>configure the generated tsconfig.json to your liking. Here are my settings for example.</p>
<pre><code>{
 <span class="hljs-string">"compilerOptions"</span>: {
     <span class="hljs-string">"target"</span>: <span class="hljs-string">"es2016"</span> ,
     <span class="hljs-string">"experimentalDecorators"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"module"</span>: <span class="hljs-string">"CommonJS"</span> ,
     <span class="hljs-string">"rootDir"</span>: <span class="hljs-string">"./src"</span> ,
     <span class="hljs-string">"moduleResolution"</span>: <span class="hljs-string">"node"</span> ,
     <span class="hljs-string">"sourceMap"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"outDir"</span>: <span class="hljs-string">"./dist"</span> ,
     <span class="hljs-string">"removeComments"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"noEmitOnError"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"esModuleInterop"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"strict"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"noUnusedLocals"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"noUnusedParameters"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"noImplicitReturns"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"noImplicitOverride"</span>: <span class="hljs-literal">true</span> ,
     <span class="hljs-string">"allowUnreachableCode"</span>: <span class="hljs-literal">false</span> ,
     <span class="hljs-string">"skipLibCheck"</span>: <span class="hljs-literal">true</span> 
 }
}
</code></pre></li>
<li><p>Set up debugging by opening debugger and create a new "launch.json" file. </p>
<pre><code><span class="hljs-string">"preLaunchTask"</span>: <span class="hljs-string">"tsc: build - tsconfig.json"</span>,
</code></pre><p>Add the above line,  above the "outFiles" line in "launch.json".
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664454504383/XQiB4VshU.png" alt="Selection_1382.png" /></p>
</li>
<li><p>Finally to run your code, create a new typescript file, for example "myfile.ts", and from the CLI</p>
<pre><code>npx ts-node ./src/myfile.ts
</code></pre><p>The output to your script will be in the terminal.</p>
</li>
</ol>
<h2 id="heading-optional-setting-up-testing">Optional: Setting up testing</h2>
<ol>
<li>Install mocha and chai<pre><code>yarn add -D chai mocha @types/chai @types/mocha
</code></pre></li>
<li><p>Modify "package.json"
add <code>"test": "mocha --require ts-node/register src/**/*.ts"</code> to your scripts key.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664573399034/iUow38KgR.png" alt="Selection_1384.png" /></p>
</li>
<li><p>For simplicity I write the solution and test in the same file. Note too that I name the test similarly to the solution being solved. This will come in handy for running single test.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664574246342/IaQzFFJNU.png" alt="Selection_1385.png" /></p>
</li>
<li><p>Run the test
<code>yarn test --grep "Reverse words"</code><br />
I use the name of the test to run <strong>ONLY</strong> that single test. As we probably do not want to run all test.</p>
</li>
</ol>
<p>All done.</p>
<p>Additionally you can set up a git repo to upload your work to a github or your remote git repository of choice.</p>
]]></content:encoded></item></channel></rss>