Making the web work. Together.

NopCommerce 2.3 (MVC) alternative product gallery

Posted by Ed on 10 January 2012 | 0 Comments

Tags: , , , , ,

I had a request recently to change the thumbnail selections in the product detail screen from a popup (using Slimbox2) to an option that changes the main image when clicking a thumbnail.

This is pretty standard fayre and you can do the ajax stuff either by writing your own image swap script or using an existing gallery plugin.

I opted for changing from Slimbox to Galleriffic. This makes it really easy to do, gives a ton of options and is pretty easy to do.

Ok, on with the show...

This is probably a reasonably good guide to extending or changing NopCommerce 2+ (MVC) code, which is probably something a lot of people will be trying to do.

You'll need a working version of NopCommerce with source code and Galleriffic. Nop is [url=http://nopcommerce.com/]here[/url], Galleriffic is [url=http://www.twospy.com/galleriffic//]here[/url]. JQuery is already in Nop, so no need to worry about that.

First thing to do is find where the existing image pop-ups are generated?

If you look in ProductTemplates.VariantsInGrid.cshtml, you'll see a reference to _ProductDetailsPictures in a render call. This is probably where we need to look.

In _ProductDetailsPictures.cshtml, we see several really cool things.

A reference to AddScriptParts() with a call to Slimbox (Great, we now know where to inject our JQuery/Plugin stuff)
A reference to the default picture in a div (@Model.DefaultPictureModel etc)
A set of thumbnails in a table

So we know that Galleriffic [in our example] needs a reference to JQuery, itself and an initialiser. It also needs a div to target with the selected image (or the default) and a set of thunbs in a list with the correct #id.

JQuery is loaded already for this view.

Copy the Galleriffic script into the scripts folder (in Nop.Web).

Now, create an initialiser script in the scripts folder.  Call it something like 'gallery.init.js'. Add the inititaliser as per Galleriffic. Mine looks like this:

[code]

jQuery(document).ready(function($) {
    var gallery = $('#thumbs').galleriffic({
//        delay:                     3000, // in milliseconds
//        numThumbs:                 20, // The number of thumbnails to show page
//        preloadAhead:              40, // Set to -1 to preload all images
//        enableTopPager:            false,
//        enableBottomPager:         true,
//        maxPagesToShow:            7,  // The maximum number of pages to display in either the top or bottom pager
        imageContainerSel:         '.picture', // The CSS selector for the element within which the main slideshow image should be rendered
        controlsContainerSel:      '#controls', // The CSS selector for the element within which the slideshow controls should be rendered
        // captionContainerSel:       '', // The CSS selector for the element within which the captions should be rendered
        // loadingContainerSel:       '', // The CSS selector for the element within which should be shown when an image is loading
        renderSSControls:          false, // Specifies whether the slideshow's Play and Pause links should be rendered
        renderNavControls:         false, // Specifies whether the slideshow's Next and Previous links should be rendered
//        playLinkText:              'Play',
//        pauseLinkText:             'Pause',
//        prevLinkText:              'Previous',
//        nextLinkText:              'Next',
//        nextPageLinkText:          'Next ›',
//        prevPageLinkText:          '‹ Prev',
//        enableHistory:             false, // Specifies whether the url's hash and the browser's history cache should update when the current slideshow image changes
//        enableKeyboardNavigation:  true, // Specifies whether keyboard navigation is enabled
        autoStart:                 false, // Specifies whether the slideshow should be playing or paused when the page first loads
        syncTransitions:           false, // Specifies whether the out and in transitions occur simultaneously or distinctly
        defaultTransitionDuration: 1000, // If using the default transitions, specifies the duration of the transitions
        onSlideChange:             undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }
        onTransitionOut:           undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... }
        onTransitionIn:            undefined, // accepts a delegate like such: function(slide, caption, isSync) { ... }
        onPageTransitionOut:       undefined, // accepts a delegate like such: function(callback) { ... }
        onPageTransitionIn:        undefined, // accepts a delegate like such: function() { ... }
        onImageAdded:              undefined, // accepts a delegate like such: function(imageData, $li) { ... }
        onImageRemoved:            undefined  // accepts a delegate like such: function(imageData, $li) { ... }
    });
});  

[/code]

(This shows all the available options with any unused ones commented out, so it's a bit messy here).

Now in the _ProductDetailsPictures.cshtml file, comment out or delete the reference to Slimbox.js and add these 2 references:

[code]

@{
    Html.AddScriptParts(@Url.Content("~/Scripts/jquery.galleriffic.js"));
}
@{
    Html.AddScriptParts(@Url.Content("~/Scripts/gallery.init.js"));
}

[/code]

If you like, you can now F5 and check it all runs, and the 2 files are showing in the source.

We now need to add one thing to the 'model', so we can get a uniform display of product images, irrespective of size. Here's what I mean and why.

In the standard Slimbox presentation, it loads the default image into the picture div, which it gets from '@Model.DefaultPictureModel.FullSizeImageUrl'.

This formats the image based on 'Product detail image size' in Media Settings, which is in the DefaultPictureModel, which we don't have available in our PictureModel.

To fix this, we need to add a new field to the PictureModel which we can use in the controller to define a set of picture URLs that are set to the correct [and uniform] size.

This means that we can upload images of different sizes into the product details, but we can always rely on them being re-sampled to whatever our 'picture' div is set to, so we don't get unruly behaviour if someone uploads a 800 x 800 pixel image. We always get in bounced down to out defined setting.

Open PictureModel.cs [in Nop.Web/Models/Media. Add the line 'public string ProductImageURL { get; set; }' with all the other getters & setters. This just gives us a place to define a URL specifically for our uniform images.

(This really should be done as an extended partial class, so we retain the base code intact. It's cool if you want to do it that way, this is just a bit easier to manage for now).

Next, we need to amend the controller to add data into our new ProductImageURL. In theory, we could add a whole new controller for this, but just changing the existing one works. Open CatalogueController.cs (it's in Controllers) and find the ActionResult for Product (about line 1165).

The bit we need to amend is actually in the 'PrepareProductDetailsPageModel(product);' method, so navigate to there. Scroll down to the 'pictures' section, and there are 2 things it does. One is set the DefaultPictureModel and the other is to create a list of PictureModels based on the product model pictures.

Just add the line:

[code]

                        ProductImageURL = _pictureService.GetPictureUrl(picture, _mediaSetting.ProductDetailsPictureSize),

[/code]

Into the model.PictureModels.add() call. Don't forget the trailing comma! This will give us a URL, with the correct picture size, to use as a reference in our thumbs list.

Now we need to remove the default picture reference from the picture div, plus create a list of thumbs in _ProductDetailsPictures.cshtml.

So comment out or remove the contents of the picture div, so it now looks like this:

[code]

<div></div>

[/code]

[EDIT: You may want to call this div prod-picture, as I just noticed another div called picture if you add related products. Whatever you call it, don't forget to re-name the target 'imageContainerSel:' var in the gallery init script!]

This tells the initialiser where to put the images when we select them. You'll note it is regerenced in the initialiser script.

Now comment out or remove the table contining the thmbs and replace with:

[code]

<div id="thumbs">
    @if (Model.PictureModels.Count > 0)
    {
        <ul>
                @foreach (var picture in Model.PictureModels)
                {
                    <li>
                        <a
                            href="@picture.ProductImageURL"
                            rel="lightbox-p"
                            title="@Model.Name"
                           >
                            <img src="@picture.ImageUrl" alt="@picture.AlternateText" title="@picture.Title" />
                        </a>
                    </li>
                }
        </ul>
    }
</div>

[/code]

This creates a div #thumbs that Galleriffic needs to find its content. You'll need to do some CSS to format the list how you like it.

Not, we've also changed the code above to '@if (Model.PictureModels.Count > 0)'. In the original, it only showed thumbnails if we had 2 or more images, but in this version, we need to show all the thumbs.

So there you have it. A pretty simple alternative to Slimbox2.

Enjoy :)


Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page | RSS feed for all comments