Developer forum

Forum » Development » Previous & Next navigation on Productpages in DW 8.2

Previous & Next navigation on Productpages in DW 8.2

Rob Lohmann
Reply

For one of our customers we want to implement previous- and nextbuttons on the productdetail pages. As far as I know there isn't such a functionality available by default, but I could be mistaking ofcourse.

One thing we should keep in mind is that we often have seperate productdetail pages. for example we can have a productoverview on this url: http://www.domain.com/assortment/category1/subcategory.aspx and the products are shown on http://www.domain.com/products/my-product-name.aspx. We do this to ensure unique producturl's for each product. Does anyone have experience with custom building (or default ofourse) this type of navigation (next and previouws links) on productdetail pages?

 


Replies

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi Rob,

 

 

I don't think it's feasible. Mostly because the concept of a previous or next product doesn't exist in just the context of a product. If you want to do some sort of navigation then you have to have a context in which the product can exist with unique next and previous products.

 

As a context, you could use the product list that the user clicked on to get to the product detail page, but that won't always work--especially if the user enters the product detail page directly, like bookmarking the product. In this situation the context doesn't exist and the navigation will not work.

 

 

If you can live with only having the navigation if the user came from the list, then you can build the context yourself by creating a notification subscriber for ProductList.BeforePaging and save the product collection in a session variable (or similar). Then create a ProductListTemplateExtender in which you render your navigation based on the current product and it's placement in the collection from earlier. NB. there is a potentially large memory overhead in saving the entire product list for all visitors!

 

 

There is an edge case with this one if you're using the optimized product retrieval on your product catalog because it only fetches the products that are displayed on the given paging of the list. Therefore, you might want to turn that feature off.

 

 

As you can probably gather, this is not straight forward and there are some considerations to be made. However, I hope you got the inspiration you sought :)

 

 

- Jeppe

 

 

 

 

 

 

 

 
Nuno Aguiar
Reply

Hi Rob,

 

If you want a simple approach you can list products 1 by 1, getting the result you want.

 

It's not perfect and has it's drawbacks since not all tags are available in the list view, but doable. No development required

 

Nuno

 
Morten Bengtson
Reply
This post has been marked as an answer

I like your approach, Nuno... simple and no development required, but I think there is still an issue with the url though - it will point to a page on a product list, not the actual product page. For SEO the actual product url could be specified in canonical meta tag?

I read this post yesterday and wanted to see if there was another way to do this. Below you can see my solution, which is based on a product template extender that can be used for displaying previous/next links on the product details page.

NOTE:

  • It only works on group product lists - not advanced product lists, search results etc.
  • You might need to change the sorting of the products.
  • It fetches all products in the same group from the database (!), which should probably be done with custom sql query so that it only returns the columns that are needed.
  • Caching the data might be a good idea.
  • There is no guarantee other than "it works on my machine" ;-)

I have added some comments in the code that explains how it works...

    using System.Collections.Specialized;
    using System.Globalization;
    using System.Linq;
    using System.Web;

    using Dynamicweb;
    using Dynamicweb.eCommerce.Products;
    using Dynamicweb.Rendering;

    /// <summary>
    /// Renders links to previous and next product in the same group as the current product.
    /// </summary>
    public class PreviousNextProduct : ProductTemplateExtender
    {
        #region Constants and Fields

        private const string LinkDefault = "Default.aspx?";

        private const string LinkQueryPattern = "{0}={1}";

        private const string LinkQuerySeparator = "&";

        private const string TagPattern = "X:Product.{0}.{1}";

        private const string TagPrefixNextProduct = "NextProduct";

        private const string TagPrefixPreviousProduct = "PreviousProduct";

        #endregion

        #region Public Methods and Operators

        public override void ExtendTemplate(Template template)
        {
            var productId = Base.Request(ParameterName.ProductId);
            if (!this.Product.ID.Equals(productId))
            {
                // Do not proceed if this is not the details view of the current product. No need to render this on product lists.
                return;
            }

            // Get the group id from query string - make sure you use links including the group id (Ecom:Product.LinkGroup) on product lists.
            var groupId = Input.Request(ParameterName.GroupId);

            if (string.IsNullOrWhiteSpace(groupId))
            {
                // No group id was found, so we can't find the previous and next products.
                return;
            }

            // Get all products in the same group.
            var products = Product.GetProductsByGroupID(groupId);

            // Find the index of the current product and check if there are any previous and next products.
            var currentIndex = products.IndexOf(this.Product.ID);
            var hasPrevious = currentIndex > 0;
            var hasNext = currentIndex + 1 < products.Count;

            // Render the previous product if there is any.
            if (hasPrevious)
            {
                var previousProduct = products[currentIndex - 1];
                RenderProduct(template, TagPrefixPreviousProduct, previousProduct);
            }

            // Render the next product if there is any.
            if (hasNext)
            {
                var nextProduct = products[currentIndex + 1];
                RenderProduct(template, TagPrefixNextProduct, nextProduct);
            }
        }

        #endregion

        #region Methods

        private static string GenerateProductLink(Product product)
        {
            var query = new NameValueCollection(HttpContext.Current.Request.QueryString);
            query.Set(ParameterName.ProductId, product.ID);
            query.Set(ParameterName.VariantId, product.VariantID);

            var queryStringKeyValuePairs = from key in query.AllKeys 
                                           where !string.IsNullOrWhiteSpace(query[key])
                                           select string.Format(CultureInfo.InvariantCulture, LinkQueryPattern, key, query[key]);

            var queryString = string.Join(LinkQuerySeparator, queryStringKeyValuePairs);

            return LinkDefault + queryString;
        }

        private static void RenderProduct(Template template, string prefix, Product product)
        {
            var link = GenerateProductLink(product);
            template.SetTag(string.Format(CultureInfo.InvariantCulture, TagPattern, prefix, TagName.Id), product.ID);
            template.SetTag(string.Format(CultureInfo.InvariantCulture, TagPattern, prefix, TagName.VariantId), product.VariantID);
            template.SetTag(string.Format(CultureInfo.InvariantCulture, TagPattern, prefix, TagName.Name), product.Name);
            template.SetTag(string.Format(CultureInfo.InvariantCulture, TagPattern, prefix, TagName.Number), product.Number);
            template.SetTag(string.Format(CultureInfo.InvariantCulture, TagPattern, prefix, TagName.Link), link);
        }

        #endregion

        private static class ParameterName
        {
            #region Constants and Fields

            public const string GroupId = "GroupID";

            public const string ProductId = "ProductID";

            public const string VariantId = "VariantID";

            #endregion
        }

        private static class TagName
        {
            #region Constants and Fields

            public const string Id = "ID";

            public const string Link = "Link";

            public const string Name = "Name";

            public const string Number = "Number";

            public const string VariantId = "VariantID";

            #endregion
        }
    }

 

Votes for this answer: 1
 
Morten Bengtson
Reply

And a template for testing...

 
Rob Lohmann
Reply

Thanks for all responses and the good thinking on the question.

@nuno, I have to agree with Morten on this one. I still see problems with producturls here.

@Morten, thanks for the really good answer with templates and code example. I'll try this later on and let you know if it worked.

 
Nuno Aguiar
Reply

Hi Rob and Morten,

 

Yes, I was aware of the URL and SEO problems, though I did not remember them. We used that solution on a project a while back, where there was no room for custom development and obviously, no great SEO and URL interest.

 

@Morten - great solution, even with the "limitations".

 

Nuno

 
Rob Lohmann
Reply

Hi Morten,

The code works good. I've added caching and modified the urlgeneration slightly so it's culture dependend. We're running an multilanguage site.
I was also getting strange url's. It gave my urls like this;

http://www.domain.com/products/<productname>/<group1>/subgroup.aspx

Looked strange, but somehow they worked.. I'll still have to make changes so the sorting the user gives in the backend is also used. But the idea works. Can this be integrated into dynamicweb as an default supported functionality? The code is quite simple and I think it can be used very often.

Rob
 

 

You must be logged in to post in the forum