Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

Saturday, 10 September 2016

Ajax call in Sitecore without registering routes

4 comments
I have already blogged about How to make Ajax call in Sitecore MVC where I was registering routes in RouteConfig. In this blog post I am going to explain how to make Ajax call in Sitecore MVC without registering routes in RouteConfig. This method is relatively very easy with less code changes. I am using Sitecore 8.2 initial release version for this blog post.

I’ve created a data template in Sitecore which is having three fields as shown in figure.


I’ve created few items based on newly created data template. See below figure:


I’ve created a basic controller rendering which is displaying books in a dropdown list. Below is the code of controller rendering for your reference:

Controller Code

public class BookDetailsController : SitecoreController
    {       
        public override ActionResult Index()
        {           
            List<SelectListItem> bookItems = new List<SelectListItem>();
            bookItems.Add(new SelectListItem { Text = "--Select Book--", Value = ""});
            bookItems.Add(new SelectListItem { Text = "Learn ASP", Value = "{EC2B22FE-8A6E-431F-8114-6B2944AE81B8}" });
            bookItems.Add(new SelectListItem { Text = "Learn MVC", Value = "{EBB9F7D5-22DB-4B7A-A275-42D9B5C88715}" });
            bookItems.Add(new SelectListItem { Text = "Learn SITECORE", Value = "{C89AE332-9845-4379-8C45-E8D58AAA5685}" });
            ViewBag.Books = bookItems;          
            return View();
        }    
}  

MVC View Code

Select Any Book :
@Html.DropDownList("Books")

<div id="BookDetail" style="white-space: pre;">

</div>
On change event of dropdown list; I’ll display book details (book title, book author and book language) of selected book. I’ll use AJAX to achieve this functionality.

Create a MVC controller action: I’ve written GetBookDetails action with [HttpPost] attribute in BookDetails Controller where I am passing Sitecore item id of selected book as input parameter. I’ll get book details by using item id of book item and return book details as JsonResult.
 [HttpPost]
        public JsonResult GetBookDetails(string itemId)
        {
            Book book = new Book();
            if (Sitecore.Data.ID.IsID(itemId))
            {
                Item item = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse(itemId));
                if (item != null)
                {
                    book.BookTitle = item.Fields["Book Title"].Value;
                    book.BookAuthor = item.Fields["Author"].Value;
                    book.BookLanguage = item.Fields["Language"].Value;
                }
            }
            return Json(book);
        }
public class Book
    {
        public string BookTitle { get; set; }
        public string BookAuthor { get; set; }
        public string BookLanguage { get; set; }
    } 
Implement AJAX call: I’ll use jQuery to make Ajax call. In the below code, I am reading the value of selected book from dropdown list and passing book item id as an input parameter while making Ajax call to get book details. Use the returned JsonResult set to update the HTML div BookDetail. Notice the value of url while making ajax request.
url: "api/Sitecore/BookDetails/GetBookDetails"
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.js"> </script>
<script type="text/javascript">
    $(function () {
        $("#Books").bind("keyup change", function () {
            var itemId = $(this).val();
            if (itemId != "") {
                $.ajax({
                    url: "api/Sitecore/BookDetails/GetBookDetails",
                    type: "POST",
                    data: { itemId: itemId },
                    context: this,
                    success: function (data) {
                        var BookString = "Book Title: " + data.BookTitle + "\n" + "Book Author: " + data.BookAuthor + "\n" + "Book Language:" + data.BookLanguage;
                        $("#BookDetail").text(BookString);
                        console.log("success", data);
                    },
                    error: function (data) {
                        console.log("error", data);
                    }
                });
            }
            else {
                $("#BookDetail").text("");
            }
        });
    });
</script>
Related read:
Comments and suggestions are most welcome. Happy coding!
Read More...

Sunday, 12 July 2015

Bundling and minification in Sitecore MVC

2 comments
This is a quick blog post on how to implement bundling and minification in Sitecore MVC project.  During development phase, it is always good to have multiple Javascripts and CSS files for better readability and maintainability of code.  But multiple Javascripts and CSS files degrade the performance of production website and also increase the load time of webpages as it requires multiple HTTP requests from browser to server.  Bundling and minification reduce the size of Javascript and CSS files and bundle multiple files into a single file and make the site perform faster by making fewer HTTP requests. Below steps explain how to implement bundling and minification for Sitecore MVC project:
  1. Add Microsoft ASP.NET Web Optimization Framework to your solution from nuget or run the following command in the Package Manager Console to install Microsoft ASP.NET Web Optimization Framework.
    PM> Install-Package Microsoft.AspNet.Web.Optimization
  2. Create your CSS and Javascript bundles in “BundleConfig” class under App_Start folder and add reference of "System.Web.Optimization" namespace.
    public class BundleConfig
        {
            public static void RegisterBundles(BundleCollection bundles)
            {
                //js bundling using wildcard character *
                bundles.Add(new ScriptBundle("~/bundles/js").Include("~/assets/js/*.js"));
    
                //css bundling using wildcard character *
                bundles.Add(new StyleBundle("~/bundles/css").Include("~/assets/css/*.css"));
            }
        }
  3. Register bundle in the Application_Start method in the Global.asax file. If you are using Multi-site instance of Sitecore MVC then recommend way to implement bundling logic is by creating a new processor into the initialize pipeline. This blog explains how to do configure it using pipeline processor.
     protected void Application_Start(object sender, EventArgs e)
            {
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
  4. Enable bundling and minification by setting the value of the debug attribute in the compilation element to false in web.config.
    <compilation defaultLanguage="c#" debug="false" targetFramework="4.5">
    </compilation>
    
    We can override the value of the debug attribute in code by using EnableOptimizations property of the BundleTable class.
    protected void Application_BeginRequest(object sender, EventArgs e)
            {
                EnableBundleOptimizations();
            }
    
            private void EnableBundleOptimizations()
            {
                string debugMode = Request.QueryString["debug"];
                if (!string.IsNullOrEmpty(debugMode) && string.Equals(debugMode, "true", StringComparison.InvariantCultureIgnoreCase))
                {
                    BundleTable.EnableOptimizations = false;
                }
                else
                {
                    BundleTable.EnableOptimizations = true;
                }
            }
    Here in Application_BeginRequest method of Global.asax I am calling one custom method EnableBundleOptimizations() which sets the value of EnableOptimizations property to true or false based on value of querystring “debug”. Main idea behind this logic is that we can check/debug CSS or Javascript file on production by passing querystring parameter debug as true. 
  5. Replace Javascripts and CSS references in layout or rendering view with below code:
    @Styles.Render("~/bundles/css")
    @Scripts.Render("~/bundles/js")
    
  6. In web.config set an ignore url prefix for your bundle so that Sitecore won’t try to resolve the URL to the bundle. Update setting IgnoreUrlPrefixes according to your bundle name:
    <setting name="IgnoreUrlPrefixes" value="/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing|/bundles/js|/bundles/css"/>
  7. Now compile your solution and verify that bundling and minification is enabled by checking view source of webpage.
    Pass querystring as debug=true in url and now verify view source of webpage. Bundling and minification is not enabled. This enables us to debug Javascript and CSS files in production website.
Comments and suggestions are most welcome. Happy coding!
Read More...

Saturday, 9 August 2014

Sitecore Page Editor Mode renders raw/json value as content

2 comments
Recently I have faced one strange issue while working with Sitecore Page Editor Mode. Sitecore Page Editor Mode was rendering some raw json value as content. In this post I am going to narrate my findings while doing the troubleshooting. Please note that I am working with Sitecore7.0 with MVC enabled.
  • John West has already written great blog related to this issue. Please refer to John’s blog which describes resolutions to issues that can cause the Page Editor in the Sitecore to render a jumble of JSON as part of the content of a page. 
  • Ensure that webedit.css file is getting loaded and accessible. You’ll find below setting in web.config file:
    <setting name="WebEdit.ContentEditorStylesheet" value="/webedit.css"/>
    Value attribute specifies the location where webedit.css is located. By default the webedit.css file is located at the root of website. In my case, webedit.css was getting loaded perfectly.
    If webedit.css is not getting loaded into DOM then add a reference to the webedit.css in <head> of layout to resolve the problem.
  • Ensure that Sitecore.MvcExperienceEditor.config file is enabled if you are working with Sitecore MVC. You can find this file at \App_Config\Include folder. Sitecore.MvcExperienceEditor.config file is responsible to render all required javascript/css files in page editor mode.
  • Verify that you are not getting any jQuery conflict related errors. If yes, then override $ function by calling "jQuery.noConflict()".
    <script type="text/javascript" src="jquery-1.8.3.js"></script>
    <script type="text/javascript">
    var jq = jQuery.noConflict(true);
    $jq(document).ready(function() {
     // Your jQuery related code go here. Use $jq for all calls to jQuery.
          });
    </script>
    
  • Ensure that Layout HTML is not causing any issue in page editor mode. Following code was written in BaseLayout.cshtml file to render IE version specific conditional css classes on body tag:
    <!--[if lt IE 8]>      <body class="lt-ie9 lt-ie8"> <![endif]-->
    <!--[if IE 8]>         <body class="lt-ie9"> <![endif]-->
    <!--[if gt IE 8]><!-->
    <body>
    <!--<![endif]-->
    
    Bingo! It was the culprit who was causing the main issue. I’ve removed code of loading conditional css classes on body tag and I was no longer getting jumbled json in Sitecore Page Editor Mode.
Drop a comment below if you are aware of any additional solution for resolving this issue. Comments and suggestions are most welcome. Happy coding!
Read More...

Sunday, 3 August 2014

Error realted to Item Id while working with Glass Mapper in Sitecore page editor mode

3 comments
Today I was working on Sitecore Glass Mapping framework for my Sitecore MVC project. I’ve installed Glass Mapper from nuget and configured it for Sitecore MVC project. For more information on the Glass.Sitecore.Mapper visit the official website. Thanks Mike and Tom for this great framework. Below are the few details about my development environment:
  • MVC Version:  4Sitecore Version:  7.0
  • Glass.Mapper version:  3.0.10.23
  • Glass.Mapper.Sc version:  3.2.0.39
  • Glass.Mapper.Sc.Mvc version:  3.2.0.35
  • Glass.Mapper.Sc.Razor version:  3.0.9.13
I am using SimpleInjector IOC container to create objects that are used by Glass.Mapper rather than default Castle Windsor or default inbuilt Glass.Mapper method. Using other IOC container with Glass.Mapper is very easy and I got my webpage up and running in Sitecore normal mode easily. However I got below error while working in Sitecore Page Editor Mode.
You can not save a class that does not contain a property that represents the item ID. Ensure that at least one property has been marked to contain the Sitecore ID. Type: SitecoreRamblings.Models.NewsModel
   at Glass.Mapper.Sc.Configuration.SitecoreTypeConfiguration.ResolveItem(Object target, Database database)
   at Glass.Mapper.Sc.GlassHtml.MakeEditable[T](Expression`1 field, Expression`1 standardOutput, T model, Object parameters, Context context, Database database, TextWriter writer)
Below is the implementation of Model class:
using Glass.Mapper.Sc.Configuration.Attributes;
using SitecoreRamblings.Service.Interface;

namespace SitecoreRamblings.Models
{
    [SitecoreType(AutoMap = true)]
    public class NewsModel
    {
        private readonly INewsService _service;       
        public virtual string Title { get; set; }
        public virtual string Body { get; set; }
        public virtual string Abstract { get; set; }

        public NewsModel(INewsService service)
        {
            _service = service;
        }     
    }
}
After investigation; I’ve realized that I forgot [my stupidity on peak :( ] to add an additional property in NewsModel class to represent the Sitecore ID. I’ve added below property in NewsModel class:
[SitecoreId]
public virtual Guid Id { get; set; }
Sitecore ID is required to allow Glass.Mapper to link your model to the actual Sitecore item in Page Edit mMode. After building solution, I was no longer getting error in Page Editor Mode.
Comments and suggestions are most welcome. Happy coding! 
Read More...

Monday, 21 July 2014

ReferenceError: Sys is not defined while working in Sitecore Content Editor

3 comments
Today I was working on clean installation of Sitecore CMS 7.0 rev. 130918 and configured it to support MVC. Somehow I started getting below error while working with rich text editor in Sitecore Content Editor.
Uncaught ReferenceError: Sys is not defined.

It seems that embedded scripts were not getting loaded by Webresource.axd and Scriptresource.axd handlers. While troubleshooting, I’ve checked for WebResource.axd in IgnoreUrlPrefixes setting to ensure that necessary javascripts are getting loaded. Below entry was present in web.config:
<setting name="IgnoreUrlPrefixes" value="/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing"/>
I’ve also added Scriptresource.axd in IgnoreUrlPrefixes settings but it didn’t work in my case then I started to check MVC routes. The routes for the MVC Web Application are defined in the RouteConfig.cs under the App_Start folder of the MVC Project. In static function RegisterRoutes(RouteCollection routes) below route was defined:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
The route with the pattern {resource}.axd/{*pathInfo} was included to prevent requests for the web resource files such as WebResource.axd or ScriptResource.axd from being passed to a controller. I’ve deleted above route entry and build the solution. Bingo! I was no longer getting any error message. Please drop a comment if anybody knows more detail about this error and let me know your opinion.

Comments and suggestions are most welcome. Happy coding!
Read More...

Friday, 27 June 2014

Alternative way of using RedirectToAction() in Sitecore MVC

26 comments
Today I’ve noticed a post in Sitecore SDN forum regarding RedirectToAction() is not working in Sitecore MVC. In this blog post I am going to explain how we can redirect to regular MVC controller action in Sitecore MVC project. We cannot use RedirectToAction() in Sitecore MVC as it interrupts the page rendering process in Sitecore. See below diagram made by David Morrison to get the overview of Sitecore MVC Request Pipeline Execution Lifecycle.
For the demo purpose; I’ve created a controller rendering GetBook which will get the book id using data source and will display specific book details. If data source is set to null then I’ve to redirect to Error Page (or Redirect to anywhere as per your requirement).
Below are the few ways to perform redirection (returning/rendering a view) in Sitecore MVC:
  1. Redirect to specified URL (Sitecore Page / ASPX Page / External URL) using Redirect(): Redirect() tells MVC to redirect to specified URL instead of rendering HTML. In this case, browser receives the redirect notification and makes a new request for the specified URL. URL in the browser's address bar gets updated. This acts similar to Response.Redirect() in Asp.Net WebForm. Moreover, Redirect() also cause the browser to receive a 302 redirect response status code within your application.
    public ActionResult DisplayBookDetail()
            {
                string DataSourceId = RenderingContext.Current.Rendering.DataSource;
                if (DataSourceId != null)
                {
                    // Write your logic here
                    return View();
                }
                else
                {
                    return Redirect("~/CustomASPXPages/Error.aspx");
                }
            }
    
    Use RedirectPermanent() instead of Redirect() if you want to return 301 redirect response status code.
  2. Redirect to another View: return View("ViewName") tells MVC to generate HTML to be displayed for the specified view and sends it to the browser. This acts similar to Server.Transfer() in Asp.Net WebForm. return View() doesn't make a new requests, it just renders the view without changing URLs in the browser's address bar.
    public ActionResult DisplayBookDetail()
            {
                string DataSourceId = RenderingContext.Current.Rendering.DataSource;
                if (DataSourceId != null)
                {
                    // Write your logic here
                    return View();
                }
                else
                {
                    return View("Error");
                }
            }
    
  3. Redirect to non-Sitecore Page using RedirectToRoute(): In RouteConfig.cs file you need to add a route that triggers MVC action. The routes for the MVC Web Application are defined in the RouteConfig.cs under the App_Start folder of the MVC Project.
    You can modify RouteConfig.cs and use the RegisterRoutes function to register the custom route. The route maps the first segment of a URL to a controller name, the second segment of a URL to a controller action, and the third segment to a parameter named id. The RouteConfig.cs contains the following code:
    public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                RouteTable.Routes.MapRoute("ErrorDetails", "GetBook/Error", new { controller = "GetBook", action = "Error" });
            }
        }
    
    The RouteConfig.cs contains a static RegisterRoutes function which is called from the Global.asax file.
    protected void Application_Start(object sender, EventArgs e)
            {
               RouteConfig.RegisterRoutes(RouteTable.Routes);        
            }
    
    RedirectToRoute redirects to a specific route defined in the Route table.
    public ActionResult DisplayBookDetail()
            {
                string DataSourceId = RenderingContext.Current.Rendering.DataSource;
                if (DataSourceId != null)
                {
                    // Write your logic here
                    return View();
                }
                else
                {
                    return RedirectToRoute("ErrorDetails");
                }
            }
    
  4. Redirect to Sitecore Page using RedirectToRoute():Use below code to redirect to Sitecore Page using RedirectToRoute()
    public ActionResult DisplayBookDetail()
            {
                string DataSourceId = RenderingContext.Current.Rendering.DataSource;
                if (DataSourceId != null)
                {
                    // Write your logic here
                    return View();
                }
                else
                {
                    //Get Sitecore Item where you want to redirect
                    Item item = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse("{24240FF2-B4AA-4EB2-B0A4-63E027934C38}"));
    
                    var pathInfo = LinkManager.GetItemUrl(item, UrlOptions.DefaultOptions);
    
                    return RedirectToRoute(MvcSettings.SitecoreRouteName, new { pathInfo = pathInfo.TrimStart(new char[] { '/' }) });
                }
            }
    
Comments and suggestions are most welcome. Happy coding!
Read More...

Saturday, 21 June 2014

Tutorial: How to make Ajax call in Sitecore MVC

32 comments
Today I’ve stumbled upon a post in Sitecore SDN forum regarding how to make an AJAX call in SITECORE MVC. In this blog post I am going to explain how to implement AJAX call in SITECORE MVC solution by adding routes in RouteConfig. Also check how to make Ajax call in Sitecore without registering routes in RouteConfig. We can divide complete functionality in below steps:
  1. Create a MVC controller action: Create an action in MVC controller which will return JSON result set by using Json() method. This action method may accept input parameters to implement business logic and return JsonResult accordingly.
  2. Register route for controller: In RouteConfig.cs file you need to add a route that triggers the controller and action.
  3. Implement AJAX call and Update HTML: Implement AJAX Call to controller action using jQuery and pass the input parameters, if any.
Example:  I’ve created a data template in Sitecore which is having three fields as shown in figure.
I’ve created few items based on newly created data template. See below figure:

I’ve created a basic controller rendering which is displaying books in a dropdown list. Below is the code of controller rendering for your reference:

Controller Code
public class BookDetailsController : SitecoreController
    {       
        public override ActionResult Index()
        {           
            List<SelectListItem> bookItems = new List<SelectListItem>();
            bookItems.Add(new SelectListItem { Text = "--Select Book--", Value = ""});
            bookItems.Add(new SelectListItem { Text = "Learn ASP", Value = "{EC2B22FE-8A6E-431F-8114-6B2944AE81B8}" });
            bookItems.Add(new SelectListItem { Text = "Learn MVC", Value = "{EBB9F7D5-22DB-4B7A-A275-42D9B5C88715}" });
            bookItems.Add(new SelectListItem { Text = "Learn SITECORE", Value = "{C89AE332-9845-4379-8C45-E8D58AAA5685}" });
            ViewBag.Books = bookItems;          
            return View();
        }    
}  
View Code
Select Any Book :
@Html.DropDownList("Books")

<div id="BookDetail" style="white-space: pre;">

</div>
On change event of dropdown list; I’ll display book details (book title, book author and book language) of selected book. I’ll use AJAX to achieve this functionality.
  1. Create a MVC controller action: I’ve written GetBookDetails action with [HttpPost] attribute in BookDetails Controller where I am passing Sitecore item id of selected book as input parameter. I’ll get book details by using item id of book item and return book details as JsonResult.
    [HttpPost]
            public JsonResult GetBookDetails (string itemId)
            {
                Book book = new Book();
                if (Sitecore.Data.ID.IsID(itemId))
                {
                    Item item = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse(itemId));
                    if (item != null)
                    {                   
                        book.BookTitle= item.Fields["Book Title"].Value;
                        book.BookAuthor = item.Fields["Author"].Value;
                        book.BookLanguage = item.Fields["Language"].Value;
                    }
                }
                return Json(book);
            }
        }
    public class Book
        {
            public string BookTitle { get; set; }
            public string BookAuthor { get; set; }
            public string BookLanguage { get; set; }
        }
    
  2. Register route for controller: In RouteConfig.cs file you need to add a route that triggers GetBookDetails action. The routes for the MVC Web Application are defined in the RouteConfig.cs under the App_Start folder of the MVC Project.
    You can modify RouteConfig.cs and use the RegisterRoutes function to register the custom route to perform Ajax calls. The route maps the first segment of a URL to a controller name, the second segment of a URL to a controller action, and the third segment to a parameter named id. The RouteConfig.cs contains the following code: 
    public static class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                RouteTable.Routes.MapRoute("BookDetails", "BookDetails/GetBookDetails", new { controller = "BookDetails", action = "GetBookDetails" });
            }
        }
    
    The RouteConfig.cs contains a static RegisterRoutes function which is called from the Global.asax file.
            protected void Application_Start(object sender, EventArgs e)
            {
               RouteConfig.RegisterRoutes(RouteTable.Routes);        
            }
  3. Implement AJAX call: I’ll use jQuery to make Ajax call. In the below code, I am reading the value of selected book from dropdown list and passing book item id as an input parameter while making Ajax call to get book details. Use the returned JsonResult set to update the HTML div BookDetail.
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.js"> </script>
    <script type="text/javascript">
        $(function () {
            $("#Books").bind("keyup change", function () {
                var itemId = $(this).val();
                if (itemId != "") {
                    $.ajax({
                        url: "BookDetails/GetBookDetails",
                        type: "POST",
                        data: { itemId: itemId },
                        context: this,
                        success: function (data) {
                            var BookString = "Book Title: " + data.BookTitle + "\n" + "Book Author: " + data.BookAuthor + "\n" + "Book Language:" + data.BookLanguage;
                            $("#BookDetail").text(BookString);
                            console.log("success", data);
                        },
                        error: function (data) {
                            console.log("error", data);
                        }
                    });
                }
                else {
                    $("#BookDetail").text("");
                }
            });
        });
    </script>
Comments and suggestions are most welcome. Happy coding! 
Read More...