Sep 22, 2020 Github Markdown Toolbar Install @github/markdown-toolbar-element and use it inside our Angular component @nartc suggested me to use that package to enable a markdown toolbar. I had a look and really like that tiny package, plus It came from Github itself 😊 To add that to an Angular application, simply run. I was using ngx-markdown with Angular 5 and it was working fine. However, I had to upgrade to Angular 8 and since then, the n are not rendered anymore. I read that with Angular 6, white spaces are not preserved anymore and that could be the issue. Ngx-markdown proposes a fix by adding the directive ngPreserveWhitespaces but it still doesn't work. For instance, writing `Array.prototype.map` in markdown will render as Array.prototype.map. To write longer or more detailed snippets of code, it is often better to place them inside a code block. Code blocks allow you to use multiple lines, and markdown will render it inside its own box and with code type font.
Angular is known for its facile infrastructure. Theoretically, every work is possible on this powerful yet flexible framework. In this tutorial, we will learn how to profoundly render Markdown as HTML in Angular 11 or even the more previously developed versions.
Apparently, this could be done through many other methods; however, i am going to lay down the foundation using the custom Angular Pipe.
We will also use the marked plugin, which allows us to convert [Markdown] into HTML. Markdown is an uncomplicated text format whose purpose is to be relentless to read and write, even when not converted to HTML.
Pipes are usually employed to transform data; they are in use since the inception of Angular 1 and known by the name of filters. But the wind took the turn since Angular 2, and they are called as Pipes.
So, without further ado, let’s start creating a custom Markdown Pipe in Angular 11.
Setting Up New Angular Application
Creating a new angular application can’t be possible without having the Angular CLI installed on your system. So, shoot the command from the command prompt to install global Angular CLI.
Next up, run the command to create an angular application; however, you can skip this step if the app is already installed on your machine.
Navigate to the project root:
Install Marked Parser Package
Install the marked Markdown parser plugin through NPM. Its a low-level and powerful compiler for parsing Markdown without caching or blocking for long periods.
Create Pipe in Angular
Head over to the command prompt using the Angular CLI to generate a new Pipe in your Angular application.
Now, as we can see, there are two pipe files that have been generated. So open the markdwon.pipe.ts file and add the following code inside.
Let me shed light on the above simple logic; the first marked package is imported on top. Then using the transform()
method, we are parsing the HTML.
Import Markdown Pipe in AppModule
Subsequently, we have to import the MarkdownPipe pipe in app.module.ts file.
Finally, we can bring the markdown HTML pipe in action.
Insert the given below code in app.component.ts file.
Pass the text or content value into the [innerHTML]
directive with a markdown pipe name. Add the given below code in app.component.html file.
Run the Angular project and see the output.
The Bottom Line
Eventually, we raised the curtain from the secrets of building the custom Markdown pipe in Angular application. One thing is for sure that we understood the concept of parsing the Markdown in HTML.
Generating HTML from Markdown at runtime is a great way to load content, but the generated HTML sits outside of the the Angular ecosystem. Here I detail a method for replacing the generated HTML elements in the DOM with dynamically created Angular components
Aim
In this tutorial we will add regular <a>
elements to the Markdown with a custom data
attribute, and render these as HTML at runtime. We will then query the DOM for the generated HTML, and replace any native <a>
elements that contain our custom data attribute with Angular components, the templates of which will contain <a>
elements with routerLink
directives
Angular Component Template
Why?
Adding a routerLink
attribute in the Markdown would have no effect as the generated HTML is just formatted text which is outside the Angular framework. The routerLink
attributes would have no functionality. Clicking on such a link would refresh the page and reload the application. We need to generate the anchor elements as Angular components with templates, so the routerLink
directives are instantiated and navigation is handled by the Router
Markdown Content
Lets first create some Markdown content, and add the links with a custom data attribute so we can identify the elements in the generated HTML. We will be using marked.js to do the Markdown to HTML conversion
markdown.md
The markdown contains some text, some HTML syntax links with custom data attributes, and a link written in markdown syntax. We have added our routerLink text to this data-routerlink
attribute, and will process it at a later stage after the markdown has been rendered as HTML
Most markdown converters allow for raw HTML blocks to be used along with specific markdown syntax like [I'm an inline-style link](https://www.google.com)
marked.js
To install marked.js
run the following commands
Create a markdown pipe to use as either a service or a template pipe - the complete code can be seen in the StackBlitz demo section ⚡️
Trusted HTML
The HTML generated by marked.js
will be bound to an element's innerHTML
property in the page's template:
We need to use a pipe to let Angular know that the HTML being bound to the innerHTML
property is trusted:
The generated HTML applied to the <div>
creates the following elements:
At this point, although the links will work, they would refersh the page and the application would reload. This is why we need to replace the native <a>
elements with Angular components
Angular Component
Create the Angular Component
Firstly we need to create a component that will replace the native <a>
elements in the DOM
anchor.component.ts
Entry Components
As the AnchorComponent
will not be used directly in a template
i.e. declared to be a required component ahead of time, Angular will ignore it during the build/AOT compilation and tree-shaking phase, and no associated ComponentFactory
will be available to generate the component dynamically. For dynamic components wee add the AnchorComponent
to the entryComponents
array of the containing NgModule
Dynamic Components
For this example, we will be query the DOM for links containing our custom data attribute [data-routerlink]
. The attribute values will be used as inputs for the AnchorComponent
. We will use a service to dynamically create the Angular components
Component Factory Service
I've created a service with a utility function to generate components based on the Angular component type
Retrieve, Create, Replace
In the host component of the markdown/HTML we will call a function to retrieve the HTML elements that match our custom data attribute, generate AnchorComponent
instances, and replace the current HTML elements with our dynamically generated components native elements. The inputs of the generated components will be initialized, and detectChanges()
will be called to check and update the component templates
Angular Markdown Editor Wysiwyg
Note that before the addDynamicAnchors() function is called, there is a check to see that the application is running in the browser. If the page is being rendered server side, then the HTML would suffice until pre-rendered HTML is replaced with the template at runtime
To build the components, we need a reference to the ViewContainerRef instance of the targeted HTML element, which is retrieved using the @ViewChild()
decorator. The process is detailed in the functions below...
home-page.component.ts
By default, the created components are added as immediate siblings of the ViewContainerRef
. I commented out the replace lines of code so we can see what the default behaviour would look like. Here the ViewContainerRef is div.render-div
. The two <bc-anchor>
components we added are immediatlely below the closing </div>
tag. You can also see the <a
data-routerlink='..'>
elements we want to replace
Ngx Markdown Editor
In the forEach
loop, the existing anchor elements are replaced with the Angular components hostView elements. If you inspect the image below, you can see that the <bc-anchor>
elements are no longer siblings of the div.render-div
, but have now been inserted where the <a
data-routerlink='..'>
elements were previously
Destroy
Angular Markdown
As a consequence of being added to the application via the ViewContainerRef
createComponent()
function, the generated component's ngOnDestroy()
lifecycle hook will be invoked when the containing view is being destroyed. There is one caveat with this: if the created components are part of a page/route that is being reused, they will remain in memory and possibly remain visible in the DOM (depending on where the elements were placed). Further dynamic components will be appended to the ViewContainerRef along with the existing ones. To get around this, we clear the ViewContainerRef each time the page is reused
StackBlitz Demo
Home Link
It would be remiss of me not to inlcude one of those dynamically generated links I've been talking about! So here you go, a link to the main blog page - without refreshing the browser 😉