Reaction: Cart product link doesn't work

Created on 16 Nov 2016  路  17Comments  路  Source: reactioncommerce/reaction

When clicking of the items you previously added to the cart it does not open the respective product page.

  • Let's say you have 2 products - Product A and Product B.
  • If you add 5 of Product A to the cart and then open Product B
  • Then click of the Product A item in the cart
  • It does not work

It seems that the cart link structure is wrong and missing the product ID after "product/..." e.g. http://localhost:3000/reaction/product/%2F/BQTAZfTfCJeHjf3H9

bug verified reproducible

All 17 comments

I believe this is what he is talking about here (clicking on the product above produces an error)

basic_reaction_product-copy

One possible solution is the following which seems to work well for me:

  1. Add "handle" to the variant schema
    E.g. to /lib/collections/schemas

handle: {
type: String,
optional: true
}

  1. Then ensure that when the handle / perm link is maintained on parent that this value is updated down to all the child variants

E.g. /imports/included.../productAdmin.js

handleFieldBlur = (event, value, field) => {
if (this.props.onProductFieldSave) {
if (field != "handle") {
this.props.onProductFieldSave(this.product._id, field, value);
}
else {
this.props.onProductFieldSave(this.product._id, field, value);
// Now the children as well - so all have the same base handle
const variantList = ReactionProduct.getVariants(this.product._id);
for (let variant of variantList) {
this.props.onProductFieldSave(variant._id, field, value);
}
}
}
}

  1. Now you can ref the handle directly in the path for
    E.g. href="{{pathFor 'product' handle=handle variantId=_id}}"

:)

Additionally when you first first a product it also creates a variant, so you need to set the variant handle at this point as well. E.g in methods / catalog.js / "products/createProduct"

return Products.insert({
  type: "simple" // needed for multi-schema
}, {
  validate: false
}, (error, result) => {
  // additionally, we want to create a variant to a new product
  if (result) {
    let handle = "";
    const parentProduct = Products.findOne(result);
    if (typeof parentProduct != "undefined") handle = parentProduct.handle;
    Products.insert({
      ancestors: [result],
      price: 0.00,
      title: "",
      type: "variant", // needed for multi-schema
      handle: handle
    });
  }
});

I am working on this issue
and plan to have a working solution today, and hopefully a pr for Reaction.
The problem seems that a subscription to get the cart's products doesn't exist
So the minimongo's products doesn't contain the required docs.
The only time the url is correct, is because the products col is filled by some other
procedure, e.g for showing products in the homepage or in pdp.

So my approach is to add an extra filter"productIds" in products publication in order to be able to get products with specific ids.
So the cartDrawer can subscribe to it supplying it with the items' ids.

Another approach is to simply add a product subscription(not products) to cartItems.js like
Template.CartDrawerItems.onCreated(function () {
this.subscribe("Product", productId);
});
This is less invasive but requires more calls(as many as the cart items)

A third one I can think(but maybe not too clean) is to make Cart publication, to return products collection as well (only the products
it contains)

The url that is being used in the checkout drawer, ie: http://localhost:3000/reaction/product/example-product/SMr4rhDFnYvFMtDTX isn't working to load the product details and select the variant?

The checkout drawer url, (handle + variantId ) is working for me to both choose a product, and also to select the selected variant on the PDP. Testing in 0.19.0. Tested urls in incognito window for the pdp, is there another case missing?

If the product is in the products minimongo collection, the url is correct. But this is a side
effect of the product being fetched by some other procedure e.g viewing the product in the pdp
or being listed in the homepage.
To reproduce it ,create another product and add both of them in the cart .
Then visit the pdp of one of them and open the cart drawer.
You will notice that the other's product url is incorrect: http://localhost/reaction/product/%2F/product-Id

This is because ,a subscription for fetching this product doesn't exist

Ah, it's not the url that's not working, it's that the url breaks when you click back and forth between them. pathFor must lose context? Re: sub/pub.. I'm not sure why we don't just use cart.items[].productId + cart.items[].variants.variantId to construct the url.. then you'd not ever need to worry about another publication source. (also note: the product page has it's own sub "product")

I have the impression ,that this doesn't happen because of the lose of context but because the products doc never got fetched from the server as a specific sub/pub to do that(getting the product docs that cart contains), doesn't exist.

If you add one more product this will get more obvious- the product in the pdp will have correct url in the cart drawer, but the other two products will have broken url.

Re: cart.items[].productId + cart.items[].variants.variantId ,this would be a nice less resourceful way, unfortunately for the time being product is used also for the image fetching : https://github.com/reactioncommerce/reaction/blob/release-0.19.0/imports/plugins/core/checkout/client/templates/cartDrawer/cartItems/cartItems.js

What do you think about refactoring it and making a method ,kind of, product/defaultImage that returns the image?

that also could just be using this.. in looking at those helpers it's not immediately obvious to me why we're querying products collection there.. there might have been a time when this didn't have the full product context, but seems like this is now the full product.

Works just as well:

import _ from "lodash";
import { Media } from "/lib/collections";
import { Template } from "meteor/templating";

/**
 * cartDrawerItems helpers
 *
 * @provides media
 * @returns default product image
 */
Template.cartDrawerItems.helpers({
  product: function () {
    return this;
  },
  media: function () {
    const product = this;
    let defaultImage = Media.findOne({
      "metadata.variantId": this.variants._id
    });

    if (defaultImage) {
      return defaultImage;
    } else if (product) {
      _.some(product.variants, function (variant) {
        defaultImage = Media.findOne({
          "metadata.variantId": variant._id
        });
        return !!defaultImage;
      });
    }
    return defaultImage;
  }
});

Indeed!
:+1:

and you can just do "{{pathFor 'product' handle=productId variantId=variants._id}}" in cartItems.html. This works, (because handle is also a regex search), and solves this issue. Is it important to have a friendly url here?

Couldn't this be a small issue for analytic tools(google analytics etc )?
The only other way I can think (and maybe not too clean) is to include handle in cart's item docs.
What do you think?

Not for SEO, because you're not going to ever crawl that url (as it'd be only a consumer adding to cart). Don't think it'd be an issue for analytics as you'd probably analyze with product data available, and it'd only be a very small group that ever would share that url. (and hey, now you know where they got the url, lol). We could add handle to cart, I suppose.. but not sure it really gets us anything (but back to a 1/2 way human friendly url)

We could later add functionality to the router to redirect to the human friendly version...

Nice,
So is it okay, for the time being, to make a pr with your solution?

馃憤 I think so... we can get it in 0.19. I think this was always a dance around including handle in the cart, and I guess it really just doesn't matter that much here.

Resolved in #1861

Was this page helpful?
0 / 5 - 0 ratings