contentful documentHtmlToString format links

Viewed 689

I am using contentful for our CMS needs and using the contentful SDK for JavaScript. I am trying to use their documentToHtmlString method to add some attributes to my anchor links. I have tried doing it like this:

return documentToHtmlString(text, {
  renderNode: {
    [BLOCKS.PARAGRAPH]: (node, next) => {
      let content: any[] = [];

      node.content.forEach((item) => {
        if (item.nodeType === 'hyperlink') {
          let itemContent = item.content[0];
          let value = itemContent['value'];

          let uri = item.data.uri;
          console.log(value, uri);

          content.push(
            `<p><a href="${uri}" data-category="contact" data-action="email">${value}</a></p>`
          );
        } else {
          content.push(`<p>${next([item])}</p>`);
        }
      });

      console.log(content.join(''));

      return content.join('');
    },
  },
});

But when I inspect the result, it doesn't have my data-category or data-action. Is there a better way to add attributes to a link?

Their documentation shows this: https://www.contentful.com/developers/docs/javascript/tutorials/rendering-contentful-rich-text-with-javascript/

but there is no mention of anchors :(


The plot thickens... I figured maybe it doesn't like data attributes, so I added a few more:

content.push(
  `<p><a class="test" href="${uri}" category="contact" data-category="contact" data-action="email" target="_blank">${value}</a></p>`
);

and what actually gets rendered is this:

<a class="test" href="mailto:bob@example.com" target="_blank">bob@example.com</a>

Notice how it's added class and target, but omitted category, data-category and data-action.....


Thanks to @stefan-judis for telling me about inline. I have now updated my code to this:

[INLINES.HYPERLINK]: (node, next) => {
  console.log(node);

  let value = node.content[0]['value'];
  let uri = node.data.uri;

  return `<a class="test" href="${uri}" data="test" category="contact" data-category="contact" data-action="email" target="_blank">${value}</a>`;
},

and I removed the BLOCKS code, but unfortunately, I still get the same issue. It is not rendering all the attributes (only class and target). Infact, it renders exactly the same as above. It's like there is some internal formatting that removes attributes...

2 Answers

I created a quick example achieving your desired outcome.

client.getEntry("...").then(({ fields }) => {
  const richText = fields.richText;

  const renderOptions = {
    renderNode: {
      // this is the important part 
      [INLINES.HYPERLINK]: (node, next) => {
        console.log(node);
        return `<a href="${
          node.data.uri
        }" style="background: red; color: white;" data-foo>${next(
          node.content
        )}</a>`;
      },
      [BLOCKS.EMBEDDED_ASSET]: (node, next) => {
        // ...
      }
    }
  };
  document.getElementById("app").innerHTML = documentToHtmlString(
    richText,
    renderOptions
  );
});

I'm not sure what going on in your environment, but as you see on CodeSandbox, the code renders data attributes and everything. :)

@Stefan was correct with his answer; it was Angular that was messing up my content. I had to use angulars DomSanitizer to fix the problem like this:

public htmlToString(text: any): SafeHtml {
  const renderOptions = {
    renderNode: {
      [INLINES.HYPERLINK]: (node, next) => {
        return `<a href="${node.data.uri}" style="background: red; color: white;" data-foo>${next(node.content)}</a>`;
      },
    },
  };

  const content = documentToHtmlString(text, renderOptions);

  return this.sanitizer.bypassSecurityTrustHtml(content);
}

That allowed me to add it to my html like this:

<div class="content" [innerHtml]="section.content"></div>

And it all worked as expected

Related