Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Matt Taylor 873 posts 2086 karma points
    Jan 24, 2014 @ 17:00
    Matt Taylor
    2

    Using Ajax with the framework

    When incorporating Ajax into previous webform projects I've used a couple of techniques.

    1. Use Umbraco Base to expose a server side web method and call it with Ajax.
    2. Use Ajax to call a template directly which will return the result.

    Any tips on how best to Ajaxify things in the MVC Hybrid Framework?

    The kind of thing I'm talking about is the Contact form or the paging on the news overview page which always look better without full page refreshes.

    Regards,

    Matt

     

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Jan 25, 2014 @ 17:29
    Jeroen Breuer
    101

    Hello,

    I've used both number 1 and 2 in the Hybrid Framework :-).

    1. Use Umbraco Base to expose a server side web method and call it with Ajax.

    In the Hybrid Framework everything is a SurfaceController. This means you can just do ajax calls to the controllers. For example this is a JsonController:

    public class JsonController : BaseSurfaceController
    {
        /// <summary>
        /// Return the location as JSON.
        /// </summary>
        /// <param name="locationId"></param>
        /// <returns></returns>
        public ActionResult GetLocation(int locationId)
        {
            return Json(LocationLogic.GetLocation(locationId), JsonRequestBehavior.AllowGet);
        }
    }

    You can call it like this:

    $('#selectLocation').change(function () {
    
        $.ajax({
            url: '/umbraco/surface/json/GetLocation/',
            type: 'POST',
            dataType: 'json',
            data: '{ "locationId": ' + $(this).find("option:selected").val() + ' }',
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
    
                //Update the text on the website
                $('#spanContactName').html(data.Name);
                $('#spanContactPhone').html(data.Phone);
                $('#spanContactEmail').html(data.Email);
    
                //Update the input fields
                $('#inputContactname').val(data.Name);
                $('#inputContactPhone').val(data.Phone);
                $('#inputContactEmail').val(data.Email);
    
            }
        });
    });

    2. Use Ajax to call a template directly which will return the result.

    I've been using this example: http://24days.in/umbraco/2012/ajax-paging-using-alttemplates/

     

    In the Hybrid Framework you just route hijack the alt template and make it return the correct data.

    The normal template. Instead of calling a macro just call a partial view and pass in a model.

    <div id="doLayer">
        <div id="doOverview">
            @Html.Partial("DepositOverview", Model.DepositOverview)
        </div>
    </div>

    This is the controller for the normal template and the alt template:

    public class DepositOverviewController : BaseSurfaceController
    {
        public ActionResult DepositOverview()
        {
            var model = GetModel<DepositOverviewModel>();
            model.DepositOverview = GetDepositOverview();
            return CurrentTemplate(model);
        }
    
        /// <summary>
        /// Used for ajax paging.
        /// </summary>
        /// <returns></returns>
        public ActionResult AjaxDepositOverview()
        {
            //We don't use GetModel because we dont need the base model properties.
            //Only the deposit overview part is reloaded.
            var model = GetDepositOverview();
    
            return CurrentTemplate(model);
        }
    
        public DepositOverview GetDepositOverview()
        {
            var depositItems = DepositLogic.GetAllowedDeposists();
            var pager = Umbraco.GetPager(10, depositItems.Count());
            return new DepositOverview()
            {
                //Only put the paged items in the list.
                DepositItems = depositItems.Skip((pager.CurrentPage - 1) * pager.ItemsPerPage).Take(pager.ItemsPerPage),
                Pager = pager
            };
        }
    
    }

    So when ajax paging is done only the data for the part that is reloaded get's fetched again. 

    I hope it's a bit clear :-).

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Jan 25, 2014 @ 19:17
    Jeroen Breuer
    0

    You could also use the WebAPI for a /base replacement: http://our.umbraco.org/documentation/Reference/WebApi/

    I'm not using the WebAPI in my example because the WebAPI is stateless and I'm using sessions in the code.

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Jan 27, 2014 @ 12:01
    Jeroen Breuer
    0

    Hi did any of my examples help you?

    Jeroen

  • Matt Taylor 873 posts 2086 karma points
    Jan 27, 2014 @ 15:22
    Matt Taylor
    0

    Hi Jeroen,

    Sorry for my slow response, I've been away from the office.

    Your examples are great and I plan to test them in my project as soon as I get some time. Right now it's all working so the Ajax stuff is a nice to have but I want to put it in. ;-)

    The WebAPI is new to me so I will look at that too, thanks.

    Matt

  • Matt Taylor 873 posts 2086 karma points
    Feb 04, 2014 @ 17:20
    Matt Taylor
    0

    I've just been looking at Ajax.BeginForm which is another option just as long as you don't need to use anything form the Umbraco context because unfortunately there is no Ajax.BeginUmbracoForm.

  • Matt Taylor 873 posts 2086 karma points
    Feb 13, 2014 @ 22:26
    Matt Taylor
    0

    Hi Jeroen,

    I've utilised both options 1 & 2 above now to Ajaxify the contact form and do some paging. All working pretty great.

    I then started to investigate the WebApi as you suggested mainly because I don't so much like the idea of creating extra templates just to do paging.
    I then came accross this post which backed up my thoughts.

    So with the aid of this getting started guide and the Umbraco docs I decided to do some tests.

    I found your NewsApiController in the Extensions project and found it is not used by anything so I thought I would call it as a test.

     

    $.getJSON('http://v2.mysite.local/umbraco/api/NewsApi/GetAllNews/1154')

    However I can see using Firebug that this is just returning a 404.
    I've tried playing with the url and re-read all the docs to make sure I've got things correct but no joy.

    Navigating the url in the browser directly yields the following:

    <Error>
    <Message>
    No HTTP resource was found that matches the request URI 'http://v2.mysite.local/umbraco/api/NewsApi/GetAllNews/1154'.
    </Message>
    <MessageDetail>
    No action was found on the controller 'NewsApi' that matches the request.
    </MessageDetail>
    </Error>

     

    Any idea why this isn't working. Is there a reason why you abandoned the us of this WebApi function in your framework example?

    Thanks,

    Matt

     

     

     

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Feb 17, 2014 @ 12:10
    Jeroen Breuer
    1

    Hello,

    I added the newsapi as an example. For parameters you need use a querystring. You could try this:

    http://v2.mysite.local/umbraco/api/NewsApi/GetAllNews?newsOverviewId=1154

    Jeroen

  • Matt Taylor 873 posts 2086 karma points
    Feb 17, 2014 @ 12:27
    Matt Taylor
    0

    Ah, querystring. That's why it wasn't working.

    I thought in MVC you could just append the parameters to the url as demo'd here.

    I guess the Umbraco routing is a little different.

    Thanks for answering and putting me back on the right track Jeroen.
    We've covered a fair bit of ajax techniques. I'm sure this thread is going to help a few people.

  • Jos Huybrighs 23 posts 55 karma points
    Mar 19, 2014 @ 23:11
    Jos Huybrighs
    0

    Hello,

    I am trying to use the hybrid framework in a new project, but with controllers that support client-side AJAX requests to return partial views to the browser.

    I first tested a simple ajax-enabled controller on your VS-solution that comes with the hybrid framework. I specifically added the following to the solution:

    In NewsOverviewController.cs I added:

    public ActionResult GetNewsThruAjax(int id, int year)
    {
      var model = new LimitedNewsOverviewModel();
      // Still todo: populate NewsItems in model
      return PartialView("PartialNewsOverview", model);
    }

    I added PartialNewsOverview.cshtml

    @model Umbraco.Extensions.Models.LimitedNewsOverviewModel
    <p>This comes in though AJAX.</p>

    In NewsOverview.cshtml I added somewhere on the top:

    ...
    <div id="getId">Click to invoke ajax request</div> <div id="ajaxContent"></div> <script type="text/javascript"> $(function () { $('#getId').on('click', function (event) { $('#ajaxContent').load('/umbraco/surface/NewsOverview/GetNewsThruAjax/2/2014'); }); }); </script>
    ... 

     

    When testing this, the XmlHttpRequest is sent to the server, but returns with a 404 not found.

     

    I then changed the controller to accept only 1 parameter in the request, like so:

    public ActionResult GetNewsThruAjax(int id)
    {
      var model = new LimitedNewsOverviewModel();
      // Still todo: populate NewsItems in model
      return PartialView("PartialNewsOverview", model);
    }

    and changed the jquery call to only sent the 'id' parameter.

    This works perfectly!

    Is there a reason why multiple arguments would not be supported? I didn't investigate how the MVC routing actually works in umbraco but, given that this is an Ajax request I would expect that the requirement to support more than 1 argument is a necessity. Because of Ajax I will need extra information in the request in order to be able to create a model for the partial view that gets populated with objects that typically have to come from the umbraco content tree.

     

    A side question: I also started to try this in my own project solution but noticed there that my controller never gets called through Ajax (even with only 1 input parameter). Do I have to configure something to enable Controller-Ajax calls? My controller correctly returns the normal page razor view, but the Ajax method never gets hit.
    It is also not clear for me what the controller classname actually has to be. Does it has to be the same as the umbraco document type alias or the template/view alias? I assigned both the same name.

    Jos

     

     

     

  • Matt Taylor 873 posts 2086 karma points
    Mar 20, 2014 @ 09:03
    Matt Taylor
    1

    Jos,

    Did you read all the posts above? Particularly the last couple before yours.
    The parameters need to be passed as a query string. It got me too.

    My understanding of the routing is that it will look for a controller that is named the same as the item's document type.
    It will then look for an action that matches the item's temlplate.

    So for a documet type called 'MemberProfile' it will look for a controller called 'MemberProfileController'
    The controller class MUST inherit from SurfaceController but in this framework you just inherit from BaseSurfaceController which already inherits from SurfaceController.

    If the template for the item is also called 'MemberProfile' it will automatically look for an action called ''MemberProfile' and use it.
    If the document type can have a second template e.g. 'MemberProfileEdit' create another action called 'MemberProfileEdit' and that will get called instead when that template is in use.

    I believe if you don't have an action that matches your template then the default 'Index' action from the inherited BaseSurfaceController will be used.

    Regards,

    Matt

     

  • Matt Taylor 873 posts 2086 karma points
    Mar 20, 2014 @ 09:09
    Matt Taylor
    0

    Info about the routing works can be found here.

  • Jos Huybrighs 23 posts 55 karma points
    Mar 20, 2014 @ 09:37
    Jos Huybrighs
    0

    Matt,

    Thanks for the superfast response.
    I actually noticed the query string description in the posts, but thought that it was not relevant for my example because your ajax calls where routing to /umbraco/api/controller. I was more or less hoping that routing to something like a SurfaceController using /umbraco/surface/controller would follow the 'normal' MVC principles.
    Ok, not what I would have liked but I can live with that.

    Also thanks for the clarification about document type en template with respect to controller and method naming.

    Jos

  • Matt Taylor 873 posts 2086 karma points
    Mar 20, 2014 @ 11:12
    Matt Taylor
    1

    Ah you are right, it is me that should have re-read my own posts.

    The way to get around the single parameter limitation when calling an Action with Ajax is to make that parameter a model object, then build that object in Jquery and pass it.

    Here's the script I used to call public ActionResult SendContact(ContactFormModel model)

    var fields = {
                    "Name": escape(jQuery("[id$='Name']").val()),
                    "Email": escape(jQuery("[id$='Email']").val()),
                    "Message": escape(jQuery("[id$='Message']").val()),
                    "Captcha": escape(jQuery("[id$='Captcha']").val()),
                    "Age": escape(jQuery("[id$='Age']").val()),
                    "PageId": escape(jQuery("[id$='PageId']").val())
                }

                var jsonString = JSON.stringify({ model: fields });

                $.ajax({
                    url: '/umbraco/surface/contact/SendContact/',
                    type: 'POST',
                    dataType: 'json',
                    data: jsonString,
                    contentType: 'application/json; charset=utf-8',
                    success: function (data) {
                        ShowSuccess(data);
                    },
                    error: function (xhr, status, error) {
                        $('#facebookG').hide();
                        $("#submit").removeAttr('disabled');
                        var msg = eval("(" + xhr.responseText + ")");
                        ShowError(msg);
                    }
                });

     

    It works nicely.

    Regards, Matt

Please Sign in or register to post replies

Write your reply to:

Draft