Grails: mobile version of a controller - grails

I am wondering if you have tried to make a mobile version of a controller?
Right now I am extending GrailsLayoutDecoratorMapper with my custom MobileDecoratorMapper which applies layout.mobile.gsp if the mobile phone is detected and I would like to do something similar for some controllers. My idea is to check in the filter if there existing a mobile version of a controller (for example SomethingControllerMobile or SomethingController.mobile.groovy) and if so redirect to it instead of a default SomethingController.
The reason for that is I would like to avoid a lot of if/else statements inside controller itself to check whether it is mobile, and if so do something differently - i do not want spaghetti code.
Does it makes sense to you and if so have you tried to do something similar and what was your approach? The only thing that comes to my mind is check for files in the filter but it does not look like a proper solution, i think this should be possible to be done on urlmapping level, where on the basis of the url grails decides which controller to invoke

The Spring Mobile plugin allows you to conditionally execute controller code for mobile devices in a fairly elegant fashion
def list = {
def view = "list"
withMobileDevice {
// mobile-specific logic goes here, in this simplistic example we
// just change the view, but you can do anything you like....
view = "mobileList"
}
render(view: view, model: [list: listInstance])
}

Related

ASP.Net MVC dynamically switching Views

I have an ASP.Net MVC 4 application where the user can choose a theme or a design of for their hosted one-page site (within this application). At first, I thought to do this with the built-in Areas but due to some application restrictions I've decided not to use that method. The way I thought to do it (which so far works) is to send the user to the index action of the controller, there find out which theme they have chosen and then return the appropriate view. this way I don't have the action name on the url which is nice since the url needs to be simple like: abc.com/cb/websiteID. btw, every theme/design has one view in the folder.
For some reason this method does not sit well with me and I think there should be a better way of doing this. Is there a downfall to this? Is this method a bad practice? is there a better way?
If I've left out a detail, please let me know and I'll do my best to address it.
Do you have a limited set of themes, which your users can choose from?
If so, I would consider to have a layout-per-theme instead, have a single view and dynamically switch layout based on params...
//in your controller
public ActionResult(int id) {
string layoutForThemeName = SomeService.GetThemeForUser(id);
ViewBag.LayoutName = layoutForThemeName
}
// in your view Index.cshtml
#{
Layout = ViewBag.LayoutName;
}
Do not forget that Razor let you inherit one layout from another, so you could event create base layout with script references etc. and layout for every of your themes.

Reuse Grails Webflow for mobile and desktop site

I have a grails webflow that works fine for my desktop browser app. Now I'd like to reuse the webflow for my mobile site. I'd prefer not to have 2 separate webflows and just change the pages that are used for each state. I tried the following:
viewState{
String view = 'viewState'
boolean isMobile = this.isMobileUser()
if(isMobile){
view = "/flowDir/mobile/viewState"
}
render(view:view)
}
However this isn't working. If the mobile site is accessed first then the desktop will get the mobile pages and vice versa.
Has anyone encountered this problem? I'd really hate to have 2 webflows that do the same exact thing. I'd also hate to hack into Sitemesh. Any ideas or suggestions on how to reuse this flow would be greatly appreciated.
Try putting the mobile view resolution into an after filter so that your web flow executes the same, but you intercept the view being rendered and rename it.
http://adammonsen.com/post/548
I agree with droggo that responsive design is the best solution.
However, as an alternative, you could potentially have all webflow users going to the same view, but within that view itself you could override the g:layout taglib with a taglib of your own that is able to determine whether you're running as a mobile user or not, and hence can apply an appropriate layout.
It's maybe a little heavy handed in this context, but it would give you a solution that would be reusable across your entire app, not just within webflow, and it keeps the mobile-specific code in one place.
So in your webflow:
viewState{
String view = 'viewState'
[snip] The rest of your code goes here
render(view:view)
}
Then your view:
<myapp:applyLayout name="someWebflowLayout">
[snip] Your non-layout GSP code goes here
</myapp:applyLayout>
Then your taglib:
class MyAppTagLib {
static namespace = "myapp"
def applyLayout = { attrs, body ->
boolean mobileUser = false
[snip] some logic to determine if this is a mobile user or not goes here
if (mobileUser) {
attrs.name = "mobileLayout/${attrs.name}"
} else {
attrs.name = "desktopLayout/${attrs.name}"
}
out << g.applyLayout(attrs, body)
}
}
You would then have two layout files called someWebflowLayout - one within /layouts/mobileLayout and one within /layouts/desktopLayout (although I appreciate these might not exactly match your existing structure)
The code in the taglib above is only a rough guide, and it'd need bolstering to deal with the other parameters that g:applyLayout takes.

GSP vs. Controller in Grails

I have some experience maintaining Grails apps; now creating a "task management" application as an exercise.
Apparently there is a view dichotomy of Groovy Server Pages versus Controller actions that render a view, as evidenced by this snippet from a URLMappings.groovy example:
static mappings = {
// ..
"/" (view:'/index')
"/login/$action?" (controller: 'login')
"/logout/$action?" (controller: 'logout')
"500" (view:'/error')
}
where user-facing URLs must be mapped to either views (GSPs) or controllers rendering a view, e.g.:
class LoginController {
/**
* Show the login page.
*/
def auth = {
// .. auth logic
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl, rememberMeParameter: config.rememberMe.parameter]
}
}
From a design perspective, how do I choose which method to use? When do I create views with GSPs/taglibs like typical server pages outputting HTML, and when do I map a URL to a controller that renders through a delegate GSP? Can I combine both approaches? Have I oversimplified the options here?
To add to what hvgotcodes said, related to your question, the only time you'd want to map directly to a GSP view is when that view is effectively "static".
By static I mean that it isn't relying on the database or any real calculations for rendering the view. It can still be dynamic in that it relies on tag libraries for dealing with common elements, and things like the "Welcome user" text at the top of pages.
As soon as you want to deal with user-supplied input, looking up database information, manage more complicated URLs, or include calculations, you should be using a controller.
The end goal is that GSPs only contain visual and layout information, as well as the occasional static block of text. But you should always avoid mixing any logic in with the GSP, because it clutters the code and always leads to maintenance headaches later on.
Edit regarding Tag Libraries:
As I wrote below:
Tag libraries are for any logic that is connected to the view, like looping over elements, or toggling the visibility of something. Whenever you are tempted to put code directly into your GSP, it probably should be put in a tag library. Of course, there are always exceptions for one-offs.
So, if you have logic code in your view, that specifically relates to visual or layout content, that should be put in a tag library. A good example is the <sec:ifLoggedIn> tag from Spring Security Core, which can be used to toggle the visibility of an element if the user is logged in. This is much better than writing it manually like so:
<sec:ifLoggedIn>blah blah</sec:ifLoggedIn>
<g:if test="${session.user?.loggedIn}">blah blah</g:if>
Because it makes the purpose clearer (by its title), as well as abstracting the logic away, so if you later need to change the way something works, you only have to change it in one place.
tl;dr:
GSPs - Simplified "static" content
Tags - Reusable dynamic components specifically for visual or layout content
Controllers / GSPs - dynamic content
I don't think it's a dichotomy. GSPs and Controller actions (are intended to) work in tandem, the controller invoking services to load data in preparation for passing that data to the appropriate GSP.
The url mapping stuff is for if you want to break the Grails convention for urls, which is orthogonal to how loading data and displaying data (are supposed) to work.
The only time (IMHO) there is a dichotomy is when developers in a project code functionality inconsistently; i.e. it is certainly possible to give the appearance of a dichotomy.

When should we implement a custom MVC ActionFilter?

Should we move logic that supposes to be in Controller (like the data to render the partial view) to ActionFilter?
For example, I'm making a CMS web site. There should be a advertisement block to be rendered on several pages but not all the pages. Should I make an ActionFilter attribute like [ShowAd(categoryId)] and decorate the action methods with this attribute?
The implementation of this controller would include service calls to retrieve information from database, buildup view models and put in the ViewData. There would be a HtmlHelper to render the partial view using the data in ViewData if it exists.
That just seems yucky to me.
When I'm trying to figure out whether I need an ActionFilter, the first question I have is, Is this a cross-cutting concern?. Your particular use-case doesn't fit this, at first blush. The reason is, is that an ad is just another thing to render on a page. There's nothing special about it that makes it cross-cutting. If you replaced the word 'Ad' with 'Product' in your question, all the same facts would be true.
So there's that, and then there's the separation of concerns and testability. How testable are your controllers once you have this ActionFilter in place? It's something else you've got to mock out when testing, and what's worse is that you have to mock out those dependencies with every controller you add the ActionFilter to.
The second question I ask is, "How can I do this in a way that seems most idiomatic in the platform I'm using?"
For this particular problem, it sounds like a RenderAction and an AdController is the way to go.
Here's why:
An Ad is its own resource; it normally isn't closely tied to anything else on the page; it exists in its own little world, as it were.
It has its own data-access strategy
You don't really want to repeat the code to generate an Ad in every place you could use it (which is where a RenderPartial approach would take you)
So here's what such a beast would look like:
public AdController : Controller
{
//DI'd in
private AdRepository AdRepository;
[ChildActionOnly]
public ActionResult ShowAd(int categoryId)
{
Ad ad = Adrepository.GetAdByCategory(categoryId);
AdViewModel avm = new AdViewModel(ad);
return View(avm);
}
}
Then you could have a custom partial view that is set up around this, and there's no need to put a filter on every action (or every controller), and you don't have try to fit a square peg (an action filter) in a round hole (a dynamic view).
Adding an Ad to an existing page then becomes really easy:
<% Html.RenderAction("ShowAd", "Ad" new { categoryId = Model.CategoryId }); %>
If your ad system is simple enough, there is no reason you could/should not use an action filter to insert enough info into the view data to generate the ad in your view code.
For a simple ad system, say.. a single ad of a specific category shows up in the same place in the layout on every page and that's it, then there is no real argument of a better way except to prepare for future changes to the system. While those concerns may be legitimate, you may also have it on good authority that requirement will never change. But, even if requirements do change, having wrapped all the code that generates ads in one place is the most important aspect and will save you much more time up front than a more robust solution might. Obviously there are more than a few ways to wrap this code in a single place.
As for the way you are choosing to do it, I would keep your action filter cleaner to only have it insert the category into the view data and have all the magic happen inside your html helper which would take the category in as a parameter. Building up view models to shove into the view data is going to require a bit of extra work, and put code all over the place when it doesn't need to be there. Keep it simple and do all of the html generation inside of the html helper which is responsible for...building html.

asp.net mvc how it decides which view to load

I am attempting to construct an asp.net mvc app which will use the urls like:
/Controller/[Number]/Action/Id
I have got it to always call my controller and pass it the Number and the Id fine...
However I now want to return a different view depending on the Number
I could have options like:
if([Number] == 1) { return View("ViewName");}
if([Number] == 2) { return View("ViewName2");}
however I instead was wondering if there was a way to change the core so that instead of searching at ~/Views/controller/action.aspx I could have my own method which did some checking on the Number then passed to the virtual file provider is a different path
Hope this makes sense!
Decide which view to load, depending on input parameters is a controller task. You could write your own view engine.
But it is easier to return the full path to the view you want to return.
return View("~/myviews/ViewName3.aspx");
This will render ViewName3 from given directory.
You might want to look at decorating your controller method with Action Filter Attributes.
Then, you could do something special inside the Action Filter Attribute.
Or, you could pass Number to a Model object, then have the model Object return the right View path.
Either way, your instinct of trying to keep too much logic out of the Controller is sound, especially if [Number] is somehow a business concern and not a view concern.
You need to look into / google creating a custom view engine.
By the sounds of things you probably just want to extend the built-in WebFormViewEngine and just override the locations and the .FindView() method.
HTHs,
Charles

Resources