Grains repetead code for actions in controllers - grails

I have already posted this question, but i realised the aswer was not what i was looking for. Imagine this controller:
class exampleController{
def action1 = {
...
[lala: lala, lele: lele]}
...
}
def action15 = {
...
[lala: lala, lele: lele]
}
I want to be able to return in all the action in this controller the same params. Imagining this:
def book = Book.findAllByIsbn(Isbn.get(1))
[book: book]
Is there any way of doing this, besides writing all the same code on all the actions? I have tried this method and it isnt working:
def action5 = {getModel()}
private getModel() {
def book = Book.findAllByIsbn(Isbn.get(1))
[book: book]
}
}
It is not working because, and my thought is, he doest accept multiple [return1: aaa, return2: bbb]. Any suggestion please ? I have also tried filters like in here: Grails controllers repeated code for all actions
but i couldnt managed to make it work. I would apreciated a detailed explanaintion about any of the solutions if possible:p Thanks in advanced,
VA

So it's not the same model, but a model with a repeated part.
You should know that the return value is an ordinary Map.
So, return value can be constructed like return getCommonModel() + [book: currentBook] where getCommonModel() returns another Map.

If you want to return the same model from all your actions, this approach should work:
class ExampleController {
def action5 = {getModel()}
def action1 = {getModel()}
//etc.
private getModel() {
def book = Book.findAllByIsbn(Isbn.get(1))
[book: book]
}
}
If you want to return the same model and render the same view from all your actions, you could return the same ModelAndView from each action, but then I would ask why do you need separate actions if they're all doing exactly the same thing?
I don't really understand your hypothesis
It is not working because, and my thought is, he doest accept multiple [return1: aaa, return2: bbb]
If your suggesting that getModel() can only return a model with a single entry, I find that very hard to believe. Can you elaborate a bit on this, or post some more information (e.g. stacktrace, unit test) that shows how/why it's not working?
Update
After reading your comments below I think I finally understand what you want to achieve, which is to append the model returned by getModel() (above) to the model returned by various other actions. Does this work:
class ExampleController {
def action5 = {
def action5Model = [foo: 'bar']
return addBookModel(action5Model)
}
def action1 = {
def action1Model = [foo2: 'bar2']
return addBookModel(action1Model)
}
//etc.
private Map addBookModel(Map model) {
def book = Book.findAllByIsbn(Isbn.get(1))
model.book = book
return model
}
}
This approach will only work when you want to add the book model within a single controller. If you want to add the book model in several controllers you can do this by:
putting addBookModel in an abstract class that the controllers extend
putting addBookModel in a class that is mixed-in with the controllers (using #Mixin)
putting addBookModel in a filter that is executed after the controller actions

If you are using exact same model in multiple pages. I would recommend you use a taglib for it.

Related

How can i respond to the same gsp view from the controller?

I am pretty new to grails and wondering how to use the respond object to the same view.
Note: I might not be clear with my question - but please check the below stuffs.
I have a one controller called "ProcessController.groovy" which have 2 methods inside them.
I have only one gsp called "create.gsp"
class PublishedSetController {
...
def create() {
Person p = new Person()
...
respond p // Working fine and this forward's to the view "create.gsp"
}
def createClone() {
Person p = new Person()
p.name = 'joe'
...
// Would like to forward to view "create.gsp"
respond view: 'create', model: p
}
}
Error:
Could not resolve view with name 'createClone' in servlet with name 'grailsDispatcherServlet'
I am aware that grails is mostly codeByConvention - but not sure how to fix this issue.
So can anyone please help, Thanks.
Correct syntax for respond is:
respond p, view: 'create'
You can read more about respond in the documentation.

GRAILS: findALL() vs FindBy---(params.id)

Greeting everyone,
I am trying to pass a parameters from a URL to a findAll() method.
LINE3 I use findAll() to define mouse.
LINE2 def house will bring in the parameter DELAWARE when I go to the page: http://localhost:8080/TestApp/home/county/DELAWARE
House will only show one instance instead of a list.. is there anyway to pass the url instead of ["DELAWARE"]? (please see line 3) thanks :)
def county() {
def house = Home.findByCounty(params.id) //sends only user related address to view
def mouse = Home.findAll("from Home h where h.county= ?", ["DELAWARE"]);
if (!house) {
response.sendError(404)
} else {
[house:house, mouse:mouse ]
}
}
Working Code +1 #Danilo
def county() {
def house = Home.findAllByCounty (params.id) //sends only county specified thru URL e.g. http://localhost:8080/TestAPP/home/county/DELAWARE
if (!house) {
response.sendError(404)
} else {
[house:house ]
}
}
findBy* will return at most one row, if you want to get all rows use findAllBy*
In order to understand how the URL will be used by Grails you have to have a look at conf/UrlMappings.groovy. You may find something like this:
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
}
}
this means that when you call TestApp/home/county/DELAWARE what Grails is trying to do is use the home controller (HomeController), invoking the county method (def county(){...}) and passing DELAWARE as id.
This should work correctly if inside county method of the HomeController you have:
def filteredInstances = Home.findAllByCounty(params.id)

Difference between artifact.clazz and bean.class

In my grails project some of controllers' actions annotated with custom annotation, say CustomAnnotation.
Also, there is a filter that checks controllers' actions whether they annotated by CustomAnnotation or not.
I've tried two ways to perform this check:
1) Look for annotations of bean's class methods
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", controllerName)
def controller = applicationContext.getBean(artefact.clazz.name)
def actionMethod = controller.class.declaredMethods.find { it.name == actionName }
def isAnnotated = actionMethod.isAnnotationPresent(CustomAnnotation)
2) Look for annotations of artifact's clazz methods
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", controllerName)
def actionMethod = artefact.clazz.declaredMethods.find { it.name == actionName }
def isAnnotated = actionMethod.isAnnotationPresent(CustomAnnotation)
While the first way doesn't work for me, the second works well.
Why these classes are different and what is the difference?
Transforming the comment in answer, the difference can be clarified by printing the class of the controller:
In the first case, the bean class is a proxy:custom.package.CustomController$$EnhancerByCGLIB$$a131be82 you can notice that by the "EnhacerByCGLIB" part. In the second one the class is correct.

MVC : Does putting data in cache or session belong in controller?

I'm a bit confused if saving the information to session code below, belongs in the controller action as shown below or should it be part of my Model?
I would add that I have other controller methods that will read this session value later.
public ActionResult AddFriend(FriendsContext viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
// Start - Confused if the code block below belongs in Controller?
Friend friend = new Friend();
friend.FirstName = viewModel.FirstName;
friend.LastName = viewModel.LastName;
friend.Email = viewModel.UserEmail;
httpContext.Session["latest-friend"] = friend;
// End Confusion
return RedirectToAction("Home");
}
I thought about adding a static utility class in my Model which does something like below, but it just seems stupid to add 2 lines of code in another file.
public static void SaveLatestFriend(Friend friend, HttpContextBase httpContext)
{
httpContext.Session["latest-friend"] = friend;
}
public static Friend GetLatestFriend(HttpContextBase httpContext)
{
return httpContext.Session["latest-friend"] as Friend;
}
I wouldn't worry too much about where you put your code to save the model to session. It's one line of code so you are not saving anything or making anything clearer by extracting it out.
For creating your Friend object, I personally would either use something like Automapper, or have a populate method on my view model.
var friend = viewModel.Populate(new Friend());
public void Populate(Friend friend)
{
friend.FirstName = this.FirstName;
}
As for saving the friend to session, if you do want to extract it, I would do something similar to your static methods but as session extension methods. Just because it makes it immediately clear where it's being stored.
// set
Session.LatestFriend(friend);
// get
var latestFriend = Session.LatestFriend();
Controller is the right place to store and get data, and populate models with that Data.
Models are used as support for that data to be shown within views.
So what you're doing is pretty correct.
No need for the static class that will just get or store from Session, as it is about only one line of code.
+1 for the advice of Bigfellahull when dealing with multiple fields initialization.

mvc best practice for common code

I have an MVC application. The following code is being used in multiple places within a controller and in multiple controllers. I would like to have this code in one place and call it from each location. What's the best way to do that in MVC?
The code below gets a row from the database and creates ViewData that can be read from the view. With webforms, I would create a public sub within a class and pass the year and month values. Is there a way this code could become part of the model?
var monthlyexpenseincome = (from vu_monthlyresult in dbBudget.vu_MonthlyResults
where vu_monthlyresult.Month == defaultmonth && vu_monthlyresult.Year == defaultyear
select vu_monthlyresult).Single();
var yearlyexpenseincome = (from vu_yearlyresult in dbBudget.vu_YearlyResults
where vu_yearlyresult.Year == defaultyear
select vu_yearlyresult).Single();
ViewData["MonthlyExpenses"] = monthlyexpenseincome.Expenses;
ViewData["MonthlyIncome"] = monthlyexpenseincome.Income;
ViewData["MonthlyProfit"] = monthlyexpenseincome.Income - monthlyexpenseincome.Expenses;
Generally, If you have common code across multiple controllers, you can create another class which inherits from Controller and keep your methods there and let your indidual Controlellers inherit this new class
public class BaseController : Controller
{
protected string GetThatInfo()
{
//do your magic logic and return some thing useful
return "This is demo return.Will be replaced";
}
}
And now you can inherit from this for your other controllers
public class UserController: BaseController
{
public ActionResult Index()
{
return VieW();
}
}
But In your case, The data you are taking is something specific to your Domain data. so i would suggest you to move it to a different class ( like a new Service / Business Layer)
public static class ProfitAnalysis
{
public static decimal GetTotalExpense()
{
//do your code and return total
}
}
And you can call it from wherever you wanted like
decimal totalExp=ProfitAnalysis.GetTotalExpense();
And You will soon realize, Having so much ViewData usage is making your code difficult to read and Maintain. Do not wait for that day. Switch to strongly typed classes to pass data.
You should place your queries in a "business layer", which is just a class that you call to do the business logic. Then you can reuse it in any place you like, just instantiate the business class and use it. You could also make the methods static if they don't require state, then you wouldn't have to even instantiate it.
For example:
var expenseService = new expenseService();
ViewData["MonthlyExpenses"] = expenseService.GetMonthlyExpenses();

Resources