2

I have a project where i want to show different views to Desktop e Mobile users, mostly for better experience and speed. Right now i'm using 51degrees to tell me if the request comes from a mobile or desktop user and then in my Action Filter i will change the view name from "Index" to "Index.Mobile", the thing is i'm getting some problems with Views where i don't want to use a Layout at all.

This is my Method:

public override void OnActionExecuted( ActionExecutedContext filterContext )
{
    var result   = filterContext.Result as ViewResult;
    var view     = filterContext.ActionDescriptor.ActionName;

    //IsMobile is filled on my OnActionExecuting method
    var IsMobile = filterContext.Controller.ViewBag.IsMobile;

    if( result == null ) return;

    string viewName;
    string masterName;

    if( IsMobile )
    {
        viewName   = view + ".Mobile";
        masterName = "_Layout.Mobile";

        //Check if view exists
        var viewResult = ViewEngines.Engines.FindView( filterContext.Controller.ControllerContext, viewName, null );

        if( viewResult.View == null )
        {
            viewName = view;
        }
    }
    else
    {
        viewName   = view;
        masterName = "_Layout";
    }

    result.ViewName   = viewName;
    result.MasterName = masterName;
}

What happens is, here i set a value for the MasterName or Layout and at two specific Controllers i need to set it as null but it still fetches the Layout i set before.

What i need is, this method will set the view and layout name and if need i will set the Layout as null inside the view i loaded.

Can that be done ?

EDIT

I tried to implement another method, Link, but i couldn't find a way to change the Layout view, only the action.

Community
  • 1
  • 1
Ariel
  • 911
  • 1
  • 15
  • 40
  • 1
    You may find it easier to just build the site mobile first and use media queries to change the appearance of your site. This would reduce that more or less unsustainable code, especially if you pass this project onto some one else. I could be missunderstanding the question but if you are simply trying to render your site based on device, media queries are the way to go. – Zach M. Aug 11 '15 at 20:18
  • I already use them but i have lots of elements that don't need to be on the mobile site so i'm using this method to provide a cleaner and lighter version of the website – Ariel Aug 11 '15 at 20:21
  • if you are using these why not just define a class of `.mobile-hide{ display:none;}` in your site css media queries. – Zach M. Aug 11 '15 at 20:24
  • Because the mobile would still load the images, html, css and all the rest and i don't want that, i want to remove the content instead of just hide it. Right now i use bootstrap, with this method i can reduce drastically the amount of data downloaded. – Ariel Aug 11 '15 at 20:28
  • 1
    I don't see anywhere in your posted code where you are nulling the Layout/MasterName. – Sam Axe Aug 11 '15 at 21:25
  • I'm doing that at my view, i forgot to mention that...sorry – Ariel Aug 12 '15 at 02:13

3 Answers3

1

You can create a folder in your project like "Areas/Mobile" and keep your views, controllers and even models (view models for mobile) in there. You just have to create a routing rule to pass across the requests coming from a mobile device to controllers inside the "Area/Mobile". It would be much easier to manage and build a mobile version of the site.

TejSoft
  • 3,213
  • 6
  • 34
  • 58
  • That doesn't sound much easier for me since i would have to duplicate almost everything just to hide some itens of my layout and change a few lines at my Controllers.....i might be wrong but i think that what i'm doing is the best option for my case since im doing that for the main project plus the areas, if not please explain to me why. Thanks for your insight – Ariel Aug 12 '15 at 02:20
  • Why don't you associate the layout that in the _ViewStart file? Check this answer: http://stackoverflow.com/questions/8419826/dynamic-change-viewstart-layout-path-in-mvc-3 – TejSoft Aug 12 '15 at 07:17
  • Correct me if i'm wrong but isn't that almost the same as defining my Layout at the beginning of my view? – Ariel Aug 12 '15 at 12:03
  • 2
    Almost same, but if you put that logic in view start then you don't have to put the same code in all the views. There are many ways to solve the problem, all it comes down to your coding style and the time you have. – TejSoft Aug 13 '15 at 00:45
  • Yeah i agree, though i only need to set the Layout at two specific views so ViewStart wouldn't really work About the Mobile Area strategic, i like that and might use it in my next project...thank you – Ariel Aug 13 '15 at 12:51
1

After some debugging i saw that setting the value inside the View wasn't working because the ViewEngine would ignore this new value and keep the value setted on the OnActionExecuted method.

I managed to fix my problem using the same method but with the addition of a ViewBag telling me if this particular view needs a Layout. Since the OnActionExecuted will execute after the Controller and before the View it's possible to set the value as true/false at the Controller and then read it inside the OnActionExecuted.

This is the working example:

public override void OnActionExecuted( ActionExecutedContext filterContext )
{
    var result   = filterContext.Result as ViewResult;
    var IsMobile = filterContext.Controller.ViewBag.IsMobile;
    var view     = filterContext.ActionDescriptor.ActionName;

    if( result == null ) return;

    string viewName;
    string masterName;
    bool hasLayout = filterContext.Controller.ViewBag.HasLayout ?? true;

    if( IsMobile )
    {
        viewName   = view + ".Mobile";
        masterName = hasLayout ? "_Layout.Mobile" : null;

        //Check if new view exists
        var viewResult = ViewEngines.Engines.FindView( filterContext.Controller.ControllerContext, viewName, null );

        if( viewResult.View == null )
        {
            viewName = view;
        }
    }
    else
    {
        viewName   = view;
        masterName = hasLayout ? "_Layout" : null;
    }

    result.ViewName = viewName;
    result.MasterName = masterName;
}

If anyone has a better option besides that please share, this workaround looks nice but it feels a little bit wrong.

Ariel
  • 911
  • 1
  • 15
  • 40
1

Well to change view name dynamically you can also use this, which is simple

 public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsLocal)
            {
                filterContext.Result = new ViewResult { ViewName = "Index" };
            }
        }

This worked for me :)

Lijin Durairaj
  • 4,910
  • 15
  • 52
  • 85