skip to Main Content

I’m creating a cart-drawer for my Shopify theme. The drawer currently have all the cart line items listed in it with a forloop like so

<ul>
    {% for item in cart.items %}
       {% render 'ajax-cart-item-template' with item: item, data_line_index: forloop.index, for_drawer: true %}
    {% endfor %}
</ul>

I need to add the feature of "Whenever someone clicks on add to cart button, the drawer will automatically open and the item will be added to it"

So what I’m trying to achieve is something like this

var cart_item = document.createElement('div');

cart_item.innerHTML = "{% include 'ship-cart-line-item' with item: '" + r.items[0] + "', data_line_index: '" + r.items[0].id + "', for_drawer: true %}";

cartDrawer.appendChild(cart_item);

Where r is an array containing 1 object that is the product that we added

After searching I tried to add in addToCart function the following code:

var newDiv = document.createElement('div');

newDiv.innerHTML = `{% raw %}{% include 'cart-line-item' %}{% endraw %}`;

cartDrawer.appendChild(newDiv);

But it returned the liquid code as a plain string in the drawer
(without adding `, it says that raw is not defined)

Then I tried to fetch the snippet from Shopify and add the parameters I need to it like so

var cart_item = document.createElement('div');
var include_url = '{{ "ship-cart-line-item" | include }}';
fetch(include_url + '?item=' + JSON.stringify(r.items[0]) + '&data_line_index=' + r.items[0].id + '&for_drawer=true')
  .then(function(response) {
    return response.text();
  })
  .then(function(html) {
    cart_item.innerHTML = html;
    var parent = document.getElementById('cartDrawer');
    parent.appendChild(cart_item);
  });

Then I tried to load the content of the snippet into a variable using AJAX then passing the parameters & appending the result to the parent, cart drawer

const cartDrawer = document.getElementById("cart-drawer");

const cart_item = document.createElement("div");

const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
    cart_item.innerHTML = this.responseText;
    cartDrawer.appendChild(cart_item);
  }
};

xhr.open("GET", "{% render 'cart-line-item', item: r.items[0], data_line_index: r.items[0].id, for_drawer: true %}", true);
xhr.send();

But I’ve got an error saying 995fe33we7716795p70e72afbmd3a6af29l.js:2 GET https://www.example.com/products/%7B%%20render%20'cart-line-item',%20item:%20r.items[0],%20data_line_index:%20r.items[0].id,%20for_drawer:%20true%20%%7D 400 e.send

I read about alternate template, but I still didn’t know how to correctly apply it. I added this script to fetch the handlebar that makes the template out of a snippet and I included it inside theme.liquid

<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>

Then created a snippet called "ajax-cart-line-item.liquid" and I copied and pasted the cart-line-item.liquid code inside of it and wrapped them all with a script tag like so

<script id="cart-line-item-template" type="text/x-handlebars-template">
   <li> ...</li>
</script>

Then inside addToCart function, the click event listener, I added the following code

let cart_item = document.createElement('div');

const source = document.getElementById("cart-line-item-template").innerHTML;
const template = Handlebars.compile(source);
let item = r.items[0]
const html = template({ item });
cart_item.innerHTML = html;
cart_drawer_content.appendChild(cart_item)
cart_drawer.setAttribute('aria-hidden', 'false')

But I got an error saying "Cannot read property innerHTML of null", to solve that I tried to include the new snippet (ajax-cart-line-item.liquid) inside the cart drawer instead of "cart-line-item" but that resulted the page to be refreshed on each quantity selector change or deleting an item.

<ul>
    {% for item in cart.items %}
       {% render 'ajax-cart-item-template' with item: item, data_line_index: forloop.index, for_drawer: true %}
    {% endfor %}
</ul>

I noticed that inside the devtools when I added "ajax-cart-item-template", the <li> elements (each cart line item is an <li> and they’re all wrapped inside the script tag as mentioned before) but the <li> elements are not inside the script tag in the DOM although it’s correctly implemented in the code

I would appreciate any help!

2

Answers


  1. Chosen as BEST ANSWER

    I found my answer in this YouTube video https://youtu.be/AfhM409bOEM

    I created a template inside templates folder, I chose index from the dropdown then named the file "index.cart.liquid" with no layout like so ```{% layout none %}, I copied the cart content that should be updated and pasted it inside the template.

    Then on each addToCart or updateCart I fetched /?view=cart

    const fetchCartDrawer = (openDrawer) => {
      fetch("/?view=cart")
      .then(res => res.text())
      .then(html => {
        cart_drawer_content.innerHTML = html
        openDrawer && cart_drawer.setAttribute('aria-hidden', 'false')
      })
    }
    

    This will fetch the content of the template and change the innerHTML of the drawer to the updated cart items, view=cart is coming from the naming of the template

    Note: you can access the template content by going to "www.example.com?view=cart"


  2. You might use the Section rendering API which allows you to query the content of a specific section (won’t work with a snippet) and then use it in the response:
    https://shopify.dev/docs/api/section-rendering

    It has been created for that.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search