How to Code Braze Web SDK Content Cards - Tutorial For Beginners

492 Views Asked by At

I have used their website docs and followed their YouTube videos, but I am still having trouble setting this up.

I am looking to test locally using gulp and BrowserSync, but it will also be going into production/staging, using our CMS (Salesforce Commerce Cloud).

Using this CMS causes difficulty as we cannot use frameworks/libraries such as React and jQuery so it has to be vanilla JavaScript.

I have created a custom content card in the Braze dashboard using their "classic" template. It contains a sample heading, subheading and image. There has also been a few custom key:value pairs (i.e. position:banner). See screenshots for braze dashboard setup.

The @braze/web-sdk is also globally initialized on our site.

The trouble arises when I try to code the content card onto the site, pull the key:value pairs and display a content card. Nothing is showing up. The code is showing up in developer tools, but nothing from the content cards is showing on the page.

I am not sure what I am doing wrong. Please help or point me in the right direction for a basic tutorial. Their website is lacking in examples and I cannot find anything on Google/YouTube.

general card setup

key:value pairs

Here is an example of my code:

HTML

<!-- Test banner type for braze. -->
<div id="banner-container"></div>

<!-- Test announcement type for braze. -->
<!-- <div id="announcement"></div> -->

<!-- Test slick carousel. -->
<!-- <div class="example-slider">
  <div><h3>1</h3></div>
  <div><h3>2</h3></div>
  <div><h3>3</h3></div>
  <div><h3>4</h3></div>
  <div><h3>5</h3></div>
  <div><h3>6</h3></div>
</div> -->

<!-- Test slick carousel slider type for braze. -->
<!-- <div class="hero-slider">
  <div>
    <img
      src="https://cdn.shopify.com/static/sample-images/garnished.jpeg"
      alt=""
    />
  </div>
  <div>
    <img src="https://cdn.shopify.com/static/sample-images/shoes.jpeg" alt="" />
  </div>
  <div>
    <img src="https://cdn.shopify.com/static/sample-images/shoes.jpeg" alt="" />
  </div>
</div> -->

JavaScript (app.js)

// function hide(obj) {
//   console.log("I would hide this card");
//   $(obj).hide("slow");
// }

// function notify(status, total) {
//   $(`.toast--${status} .toast__message`).text(
//     `A total of ${total} content cards were detected`,
//   );
//   $(`.toast--${status}`).toggle("slow");
//   console.log(`this number of cards were rendered`, total);
//   setTimeout(function () {
//     $(`.toast--${status}`).fadeOut("slow");
//   }, 5000);
// }

// Initialize jQuery library.
(function ($) {
  "use strict";

  // Preloader.
  // $(window).on("load", function () {
  //   $("#preloader").fadeOut("slow", function () {
  //     $(this).remove();
  //   });
  // });

  // E-commerce touchspin.
  // $("input[name='product-quantity']").TouchSpin();

  // Video lightbox.
  // $(document).on("click", '[data-toggle="lightbox"]', function (event) {
  //   event.preventDefault();
  //   $(this).ekkoLightbox();
  // });

  // $(".toast__close").click(function (e) {
  //   e.preventDefault();
  //   var parent = $(this).parent(".toast");
  //   parent.fadeOut("slow", function () {
  //     $(this).hide();
  //   });
  // });

  // Instagram feed.
  // if ($("#instafeed").length !== 0) {
  //   var userFeed = new Instafeed({
  //     get: "user",
  //     userID: "623597756",
  //     resolution: "low_resolution",
  //     accessToken: "623597756.02b47e1.3dbf3cb6dc3f4dccbc5b1b5ae8c74a72",
  //   });
  //   userFeed.run();
  // }

  // Count down JS.
  // $("#simple-timer").syotimer({
  //   year: 2019,
  //   month: 5,
  //   day: 9,
  //   hour: 20,
  //   minute: 30,
  // });

  // Hero slider.
  $(".hero-slider").slick({
    // autoplay: true,
    infinite: true,
    arrows: true,
    dots: true,
    prevArrow:
      "<i class='fa fa-regular fa-chevron-left heroSliderArrow nextArrow'></i>",
    nextArrow:
      "<i class='fa fa-regular fa-chevron-right heroSliderArrow nextArrow'></i>",
    // dots: false,
    autoplaySpeed: 7000,
    pauseOnFocus: false,
    pauseOnHover: false,
  });

  $(".hero-slider").slickAnimation();

  $(".hero-slider").on(
    "beforeChange",
    function (event, slick, currentSlide, nextSlide) {
      console.log(event);
    },
  );

  // Optionally show all in-app messages without custom handling.
  braze.display.automaticallyShowNewInAppMessages();

  braze.changeUser("1234");

  window.activeCards = [];

  // Subscribe to content cards updates. The subscriber callback will be called whenever content cards are updated. this method should...
  // braze.subscribeToContentCardsUpdates should be called before braze.openSession below.
  braze.subscribeToContentCardsUpdates((newCards) => {
    var card;
    var duplicate;

    console.log(JSON.stringify(newCards));

    // Creates instances of the custom cards by calling it's constructor as well as passing the required arguments below (newCards).
    for (card in newCards.cards) {
      var imageURL = newCards.cards[card].imageURL,
        title = newCards.cards[card].title,
        description = newCards.cards[card].description,
        // position is pulling from extras object in the key value pairs. See in braze dashboard. This indicates where it will show on the site.
        position = newCards.cards[card].extras.position,
        originalObject = newCards.cards[card],
        style = newCards.cards[card].extras.style;

      let exists = activateCards.filter((obj) => {
        return obj.originalObject.id == newCards.cards[card].id;
      });

      console.log(exists);

      // activeCards creates a new instance of the custom card class from card.js and assigns it to the activeCards array.
      if (exists.length == 0) {
        activateCards[card] = new CustomCard(
          `activateCards[${card}]`,
          title,
          description,
          imageURL,
          position,
          originalObject,
        );

        if (position == "inbox") {
          activateCards[card].pushToInbox();
        } else {
          // render function comes from card.js and determines how to display/render the card based on the type property. The three types in this example are banner, announcement and slider. See card.js render function for more comments.
          activateCards[card].render();
        }

        activateCards[card].styleUpdate(style);
      } else {
        duplicate = true;
        console.log("This is a duplicate, ignoring");
      }
    }

    if (card && !duplicate) {
      notify("blue", parseInt(card) + 1);
    } else if (!card && !duplicate) {
      notify("yellow", 0);
    }
  });

  braze.registerBrazePushMessages();
  // Start (or continue) a session.
  braze.openSession();
})(jQuery);

JavaScript (card.js)

class CustomCard {
  // This constructor function assigns values to properties (id, title, description, imageURL, type and originalObject).
  constructor(id, title, description, imageURL, type, originalObject) {
    this.id = id;
    this.title = title;
    this.description = description;
    this.imageURL = imageURL;
    this.type = type;
    this.originalObject = originalObject;
    this.style = undefined;
  }

  // ? INFO: There is another method that can be used called requestContentCardRefresh. When it is called it will request an immediate refresh of the content cards from Braze servers. By default, cards are refreshed when a new session opens, but this will allow you to control at any time when you want to do a refresh.

  // ? INFO: You need to manually log analytics when using custom content cards. this is the log dismissal and impressions below.

  // Logs that the user dismissed the given content cards. this is done automatically when you use Braze's...
  dismiss() {
    braze.logCardDismissal(this.originalObject);
  }

  // Logs that the user saw the given content cards. this is done automatically when you use Braze's...
  logView() {
    braze.logCardImpressions(this.originalObject);
  }

  styleUpdate(style) {
    switch (style || this.style) {
      case "shoes":
        this.style = style;
        $('link[href="css/style.css"]').attr("href", "css/shoes.css");
        break;
      default:
        console.log("No style changes detected");
    }
  }

  pushToInbox() {
    var inboxElement = `<div class="media">
      <a class="pull-left" href="#">
        <img class="media-object" src="${this.imageURL}" alt="image" />
      </a>
      <div class="media-body">
        <h4 class="media-heading"><a href="#">${this.title}</a></h4>
        <div class="cart-price">
          <span>${this.description}</span>
        </div>
      </div>
      <a href="#" onclick="hide(this.parentNode); ${this.id}.dismiss();" class="remove"><i class="fa fa-solid fa-xmark"></i></a>
    </div>`;
    console.log(this.id);
    $(".empty-inbox").hide();
    $("#message-inbox").append(inboxElement);
    $(".notification").show();
  }

  render(style) {
    // See key:value pair (position:banner) in the braze dashboard.
    if (this.type == "banner") {
      var newImage;

      if (this.imageURL) {
        newImage = this.imageURL;
      } else {
        newImage =
          "https://cdn.shopify.com/static/sample-images/garnished.jpeg";
      }

      // When the banner is clicked it will trigger the below hide function, which will hide the clicked element as well as the dismiss element, which will dismiss the card.
      var bannerElement = `<div onClick="hide(this); ${this.id}.dismiss();" class="category-box">
        <div class="inner">
          <img src="${newImage}" alt="" />
          <section class="sec03">
            <h3>${this.title}</h3>
            <p>${this.description}</p>
          </section>
        </div>
      </div>`;
      console.log(this.id);
      // The banner element will then be appended to the HTML element with the id of banner-container.
      $("#banner-container").append(bannerElement);
    } else if (this.type == "announcement") {
      $("#announcement").css(
        "background-image",
        "url(https://cdn.shopify.com/static/sample-images/garnished.jpeg)",
      );
      // See key:value pair (position:slider ) in the braze dashboard.
    } else if (this.type == "slider") {
      // Slider will create a hero element with image title and description and dismiss button
      var heroElement = `<div id="card-${this.id}" class="slider-item th-fullpage hero-area" style="background-image: url(${this.imageURL});"><div class="container"><div class="row"><div class="col-lg-8 text-right"><p data-duration-in=".3" data-animation-in="fadeInUp" data-delay-in=".1">${this.title}</p><h1 data-duration-in=".3" data-animation-in="fadeInUp" data-delay-in=".5">${this.description}</h1><a onClick="${this.id}.dismiss();" data-duration-in=".3" data-animation-in="fadeInUp" data-delay-in=".8" class="btn" href="#">Dismiss</a></div></div></div></div>`;

      // Hero element is added to the beginning of the slider, using heroElement, 0.
      $(".hero-slider").slick("slickAdd", heroElement, 0, true);

      $(".hero-slider").slickAnimation();
    } else {
      console.log("Rendering default card");
    }
  }
}

What I tried:

I tried coding the content cards on the front end using javascript and connecting to the braze sdk by using the key:value pair of position:banner. This code was injected into HTML at the banner ID.

All non-braze styles and HTML show up fine, but it does not seem to want to display any of the Braze content card elements on the page.

0

There are 0 best solutions below