How to execute an action without going to another view in grails? - ajax

I have a code that starts the server and stops the server. However when i press the button the server gets started but it also redirects me to a new view. The code in controller i have is
def test= amazonWebService.ec2.startInstances(new StartInstancesRequest([InstanceToStart]))
I want to execute test when button is pressed without going to new view. The code I have in my gsp page is
<g:link action="test">
<input type="button" value="Start Server" class="button" id="startServer1" />
</g:link>

If you don't mind the page refreshing, in your test action, redirect to the same view you came from. So assuming you got to your current view via index:
class SomeController {
def index() {
// index.gsp rendered via convention
}
def test() {
// execute your code then
redirect action: index, params: params
}
}
You're other option is to submit an Ajax request when the link is clicked and not have to refresh the page at all.

Use a remote function, see here http://grails.org/doc/2.1.0/ref/Tags/remoteFunction.html

Following is the alternative answer to the question.
By default Grails view resolver tries to find a view similar to the action name. In your case, Grails is trying to find test.gsp and if it is there in your repository it renders that. You can override this behaviour by using the Grails URL mappings feature. There you will get to specify which view should be rendered for a certain action.
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
For further information visit : http://grails.org/doc/2.2.x/ref/Plug-ins/URL%20mappings.html

Related

thymeleaf doesn't update a model value in my template after a post

I have a session attribute called
#SessionAttributes("myNewAttribute")
which is defined at the top of my controller. when I hit my second endpoint, the post, the value does not update in thymleaf from blank to showing the string but it does print it to my console. I'm going a little crazy. Here is my controller file and my template:
#Controller
#SessionAttributes("myNewAttribute")
public class SpringFileController {
#GetMapping("/pageone")
public String displayPageOne(Model model) {
return "pageone";
}
#PostMapping("/clickedsavebutton")
public String handleSaveButton(Model model){
model.addAttribute("myNewAttribute","catnip");
String valueToDisplay = (String)model.getAttribute("myNewAttribute");
System.out.println(valueToDisplay);
return "pageone";
}
}
my template
<p th:text="${myNewAttribute}"></p>
<!--the save button, other things, wtc.. -->
So in my console when I click save I get "catnip" but "catnip" is not displayed on the page UNTIL I refresh the page. What am I doing wrong
edit: I added window.location.reload() after my post call in my template which makes the attribute show, but I lose other things on the page that have been changed if I do it this way
This is expected behavior, Thymeleaf is a rendering engine and as such can not change things after the initial load of the page.
To get the desired effect you'd have to reload (part) of your web page. this can be either a complete refresh or refreshing a div or field using something like ajax/javascript.
I added
$( "#required-field-div" ).load(window.location.href + " #required-field-div" );
to my function on the save button and got this to work. thank you for all the help!

Understanding WebFlow on Grails

I'm using Spring Web Flow on Grails (2.0.8.1 on grails 2.3.11). I've created all dir under views and this is my little flow:
def registrazioneUtenteFlow = {
log.info("Registrazione Flow")
step1_informazioni_personali {
on("next").to("step2_informazioni_personali")
}
step2_informazioni_personali {
on("submit").to "step3_informazioni_personali"
on("return").to "step1_informazioni_personali"
}
step3_informazioni_personali {
}
}
I created three gsp under controllerName/flowName. Inside each JSP there is a form with a submit button. Ex. for first GSP:
<g:form action="registrazioneUtente">
<g:submitButton name="next" value="NEXT" />
</g:form>
Now.. If i put inside the state the "on" rule, I have an 404 with this url
registrazioneUtente?execution=e8s1&format=
But if I leave this from step1_informazioni_personali, I can see the page.
Why?!?
Thanks in advance
I found the problem:
The error happens because my controller have a namespace : "SITE". Removing it, all it work.

Returning errors to gsp page in Grails after checking for hasErrors and Validating

I'm building this web app that works with user profiles. A user edit a profile and if he enters wrong data, the profile shows the invalid fields in red and shows the message. Very simple scenario. But for some reasons it doesn't work the way I assumed. I might be missing something somewhere. Here is what I do in my controller.
As you can see in the code below, I create a new Profile, I bind the params to it using an include list to only bind those params that profile accepts, and then call hasErrors on the profile instance. In debug mode, I can see that myProfile has errors when the user enters something wrong, but when I get back to the editProfile page, it doesn't show what field has the error. my gsp form has the render error tag and also hasError for each field.
def update (){
def myProfile = new Profile()
dataBind(params, myProfile, include:[.....])
if (myProfile.hasErrors()){
respond myUser.errors, view: 'editProfile'
return
}
else{
redirect(action:"viewProfile")
}
}
My gsp is something like this, I don't have access to my home computer right now to post the exact gsp page:
<g:hasErrors bean="${myUser}" field="name">
<g:message error="${myUser.errors.getFieldErrors("name")}" />
</g:hasErrors>
Instead of trying to pass the errors back, pass the entire object to your gsp. Assuming the bean you have in your gsp page is 'myUser', then this is what the render statement should be:
render view: 'editProfile', model: [myUser: myProfile]
When you use respond bean.errors, the bean will be called based on class name, not variable name.
In your case, you need to access the bean in you gsp as profile like so:
//variable in controller
def myProfile = new Profile()
//bean in views
<g:hasErrors bean="${profile}" field="name">
<g:message error="${profile.errors.getFieldErrors("name")}" />
</g:hasErrors>
If you need to call your bean with a specific name like myUser, just pass it in the model:
render (view:'editProfile', model:[myUser: myProfile])

Grails remoteField - Can I call a render/redirect?

I'm not looking for a div update. I'm looking for a full page "refresh"
So I have a tag:
<g:remoteField controller="person" action="updatePerson" id="${personInstance.id}" paramName="search" name="updatePerson" value="${personInstance?.favoriteBreed}" />
In my attached method, I want to perform the action of updating the domain object, then refresh the entire page. I've tried both renders and redirects to simple actions within the application, but nothing is called. So I'm thinking this isn't possible with the remoteField tag.
Is this true?
Why reimplementing via AJAX the normal behaviour of a page? Why don't you just call a redirect to the right action at the end of the called controller?
Let's say you are in the view resulted from the showPerson action. In this view, call the updatePerson with a simple (don't care about the params attribute, it's only an example):
<g:Link controller="person" action="updatePerson" id="${personInstance.id}" params="${name: 'search'}">click me</g:link>
and in the controller do something like:
def updatePerson = {
// your own updating stuff
redirect(action: "show", id: person.id)
}
Isn't it easier? :)
<g:remoteField /> have the event onSuccess that can be used to do what you want. You can check more options here.
Try (not tested):
<g:remoteField controller="person" action="updatePerson"
id="${personInstance.id}" paramName="search" name="updatePerson"
value="${personInstance?.favoriteBreed}" onSuccess="reloadPage()" />
And you will need to create the javascript function:
function reloadPage() {
window.location.reload();
}

How do you build a Single Page Interface in ASP.NET MVC?

I want to build a webapplication with a "Single Page Interface", using ASP.NET MVC.
I have searched if this was at least possible and I think the answer is: not by simple means (reading http://msdn.microsoft.com/en-us/magazine/cc507641.aspx#S2 second-last paragraph; that article is from May 2008, though).
I found other examples which implemented this by coding/hacking with jQuery. However, I'm looking for a clean solution, using standard .NET approaches, if possible.
What I want is precisely the same functionality when you create a new "MVC Web Application". However, instead of links to "/Home/About" which reloads the entire page, I want links to "#Home/About" which loads only the new part via AJAX.
The standard approach of calling templates (partial views) with Html.RenderPartial is exactly what I want, only then loading them in through AJAX-requests.
Of course, it might be that I can't use these templates that are rendered by the Master-page for some reason (maybe it's expecting to always be called in a certain context from a certain place or so). But maybe there's another clean solution for how to build your template-pages and fetching them from the Master-page.
Who has a nice solution for implementing such thing, a Single Page Interface?
PS: I'm developing in Visual Web Developer 2008 Express Edition with MVC 1.0 installed, in C#
[edit]
Below I read that working with the templates is possible and that jQuery looks indeed like inevitable, so I tested it.
The following code transforms regular links created by Html.ActionLink into anchor-links (with #) to contain history, and then fetch the page via AJAX and only injecting the html-part I'm interested in (i.e. the partial page inside div#partialView):
$("a").each(function() {
$(this).click(function() {
$("div#partialView").load($(this).attr("href") + " div#partialView");
location.hash = $(this).attr("href");
return false;
});
});
These links also allow for graceful degredation.
But what I have left now, is still fetching the whole page instead of only the partial page. Altering the controller didn't help; it still provided me html of the whole page, with all of these statements:
public ActionResult About()
{
return View();
return View("About");
return PartialView();
return PartialView("About");
}
How could I only return the content of the part I'm interested in (i.e. the contents of Home/About.aspx)?
What I'd like is POSTing a value with AJAX (e.g. "requesttype=ajax") so that my controller knows the page is fetched via AJAX and only returns the partial page; otherwise it will return the whole page (i.e. when you visit /Home/About instead of #Home/About).
Is a good practice to alter Global.asax.cs maybe, to create a new routing schema for AJAX-calls, which will only return partial pages? (I haven't looked into this much, yet.)
[edit2]
Robert Koritnik was right: I also needed an About.ascx page (UserControl) with only the small HTML-content of that page. The first line of About.aspx was linked with the Master-page via MasterPageFile="~/..../Site.master" which caused that all HTML was printed.
But to be able to execute the following in my controller:
public ActionResult About()
{
return Request.IsAjaxRequest() ? (ActionResult)PartialView() : View();
}
I needed to alter the way a PartialView (.ascx file) and a View (.aspx) file was found, otherwise both methods would return the same page (About.aspx, ultimately resulting in an infinite loop).
After putting the following in Global.asax.cs, the correct pages will be returned with PartialView() and View():
protected void Application_Start()
{
foreach (WebFormViewEngine engine in ViewEngines.Engines.Where(c => c is WebFormViewEngine))
{
/* Normal search order:
new string[] { "~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx"
"~/Views/Shared/{0}.ascx"
};
*/
// PartialViews match with .ascx files
engine.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.ascx" };
// Views match with .aspx files
engine.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
}
RegisterRoutes(RouteTable.Routes);
}
Full view vs. Partial view
Seems like you've messed up something. If you create an About.aspx page with all the HTML needed to display the whole page it doesn't really matter if you say
return PartialView('About');
The view still returns all the HTML that's written in it.
You should create a separate About.ascx that will only have the content of the page itself without the header and other stuff that's part of the whole page.
Your original page About.aspx will have something like this in its content (to avoid repeating writing the same content twice):
<%= Html.RenderPartial("About") %>
And you can have two controller actions. One that returns a regular view and one that returns a partial view:
return View("About"); // returns About.aspx that holds the content of the About.ascx as well
return PartialView("About"); // only returns the partial About.ascx
Regarding routes in Global.asax
Instead of writing separate routes for Ajax calls you'd rather write an action filter that will work similar as AcceptVerbsAttribute action filter. This way your requests from the client would stay the same (and thus preventing the user from manually requesting wrong stuff), but depending on the request type the correct controller action will get executed.
Well, you can load Partial View through AJAX request. In example, I'll use jquery to make an ajax call.
These could be the action in controller (named HomeController):
public ActionResult About()
{
//Do some logic...
//AboutView is the name of your partial view
return View("AboutView");
}
JQuery ajax call to place the retured html in place you want:
var resultDiv = $('#contentDIV');
$.ajax({
type: "POST",
url: "/Home/About",
success: function(responseHTML) {
resultDiv.replaceWith(responseHTML);
}
});
[edit-question is updated]
It is possible to do exactly what you want. First controller action can give you back the partial view, so mine "AboutView" could have been something like this:
<table>
<tr>
<th>
Column1Header
</th>
<th>
Column2Header
</th>
</tr>
<tr>
<td>
...
</td>
<td>
...
</td>
</tr>
and this HTML is exactly what are you going to have in responseHTML on success handler in jquery ajax method.
Second, you can distinguish in controller action if the request is an ajax request:
public ActionResult About()
{
//partial AboutView is returned if request is ajax
if (Request.IsAjaxRequest())
return View("AboutView");
else //else it will be the default view (page) for this action: "About"
return View();
}
We've got a site that does exactly this, and you really want to use the jQuery route here--alot easier to implement in the long run. And you can easily make it gracefully degrade for users who don't have javascript enabled--like google.
it isn't all that clear what are you asking for, is it for a complete example or for some specific functionality? You should be able to do this without JQuery for simple scenarios, you can use the Ajax view helpers like the ActionLink method. Also, I don't really understand what is your issue with RenderPartial, but maybe you're looking for something like RenderAction from ASP.NET MVC Futures.
ASP.NET MVC 4 (now in beta) adds support for Single Page Applications in MVC.
http://www.asp.net/single-page-application
UPDATE:
...and they removed it from the MVC 4 RC
UPDATE:
...and it is back with the 2012 Fall update

Resources