2

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...

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • Heyo, Contentful DevRel here. I just recently recorded an episode that covers more or less your case. Maybe it's helpful. :) https://www.contentful.com/developers/videos/learn-graphql/#rendering-of-contentful-richtext – stefan judis Aug 13 '20 at 21:56
  • Thanks, I watched your video which was easy to follow, but unfortunately, it didn't work. I have updated my question – r3plica Aug 14 '20 at 09:07

2 Answers2

2

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 judis
  • 3,416
  • 14
  • 22
  • It's strange; I copied your answer and pasted it into my code. If I put a console log like this: `console.log(this.contentfulService.htmlToString(this.component.fields.content));` I can actually see that it has all the properties. But it never gets rendered like that. I should point out I am using angular and I don't do `document.getElementById` but instead I do this: `
    `
    – r3plica Aug 21 '20 at 09:52
  • Then it seems to be an Angular thing and unfortunately, I can't help with that. :/ – stefan judis Aug 21 '20 at 10:06
  • It's ok, I figured it out. I will post here! – r3plica Aug 21 '20 at 10:14
0

@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

r3plica
  • 13,017
  • 23
  • 128
  • 290