Grails rendering blank page - grails

I'm using Grails 2.3.11.
When I'm going to localhost:8080/myapp/questionnaire/show/3, application shows a blank page. And debug won't even step into any of the methods below.
class QuestionnaireController {
def beforeInterceptor = [action: this.&loadQuestionnaire, only: ['show']]
Questionnaire questionnaireInstance
def show = {
render(view: 'show', model: [questionnaire: questionnaireInstance])
}
private def loadQuestionnaire() {
questionnaireInstance = Questionnaire.findById(params.id)
if(!questionnaireInstance) {
redirectWhenNotFound(params.id)
}
}
private def redirectWhenNotFound(def id) {
flash.message = message(code: 'default.not.found.message', args: [
message(code: 'questionnaire.label'),
id
])
redirect(uri: "/")
}
However, When I try localhost:8080/myapp/questionnaire/show, it goes to loadQuestionnaire(), obviously can't find domain instance, because of params.id not existing, so it goes to redirectWhenNotFound() and redirects it properly to the main page.
Question is - why is it not working normally?
Edit:
It shows a blank page only when there is an instance with the given id, when there isn't, it goes into the method.
Eventually I found out the solution, which is in the answer below.

After checking few more things, that it only fails when there is an instance with the given id, and it goes through the method when there is no instance with the given id, it turned out there has been a misspelled column in a questionnaire table, after editing it, Grails updated the schema and not deleted the old one.
Probably this was the problem, because I dropped that column, removed all rows, restarted application, created a new instance and it works now.
The problem was that it was failing silently, so I am leaving this answer for anyone, who might be having the same problem in the future.

Related

Create Linked Database Record in MVC5 Controller

I'm just starting out with ASP.NET MVC, and anything but the basics throws me at the moment, so please bear with me.
I am writing a little application รก la UserVoice, whereby users can post ideas, and other users can vote for those posts.
I have a View which displays the detail of a particular Post (suggestion). On that View I have button labelled Vote which, when clicked, should create a record in my database's PostVote table linked to the Post. I just can't get this work, though! Here's what I have:
Post > Details.cshtml
Vote
PostVoteController.cs
[HttpPost]
public ActionResult VoteForPost(int id, Post post)
{
if (ModelState.IsValid)
{
db.PostVote.Add(new PostVote
{
PostID = id,
VoteTime = DateTime.Now,
VoteUser = UserHelper.GetUsername()
});
db.SaveChanges();
}
return View(post);
}
The only reason I'm requiring the post parameter in my method is because, as I understand it, unless I want to use Ajax I need to use an ActionResult and return a View, and so I've tried to copy the code in the Edit method of the PostController which was generated automatically by Visual Studio and works fine. The idea is just to reload the Post Details View after the new PostVote record has been created,
I realise I've probably written nonsense, but nothing I'm trying works. When I click the Vote button, I'm directed to a page with a URL of http://localhost:58974/PostVote/VoteForPost/1, which I sort of understand, but is obviously wrong. I can't figure out how to just pass the ID of the Post to which the new PostVote record should be linked as a variable rather than it forming part of the URL. Obviously I get a 404 error because the page doesn't exist.
Update
Ok, thanks to the comments posted I've now got this in my View:
#using (Html.BeginForm("VoteForPost", "PostVote", new { id = Model.PostID }, FormMethod.Post))
{
<button type="submit">Vote</button>
}
This is successfully creating the records as desired, but is still trying to redirect the browser to http://localhost:58974/PostVote/VoteForPost/1 afterwards. Bearing mind that the Controller (PostView.cs) doing the record creation is not the one which displayed the Post Details in the first place (that's Post.cs), how do I get my method to just return me back to the Details View I'm already looking at?

Grails disallow normal create

Grails noob here. I am build a simple grails app where I have the default create/ edit / list / show pages for a Product domain object. I also have the ProductController. The create method by default does this:
def create() {
[productInstance: new Product(params)]
}
My understanding is this will take me to the create page. And there will be nothing in the params object so everything will be blank.
I want to change the behaviour so that the when the create() method is invoked, a pop up is returned to the User: Sorry you are not allowed to create new data.
The user will stay on the same page and the only thing that will happen is the pop up.
How do I do this without using JavaScript?
Thanks
If the create method is not allowed, why not just remove the create() method from the controller, delete the create.gsp page and remove the 'New' button from the list page?
If you do want to offer a 'New' button and display a popup message that says you can't use the button, that is probably easiest done in javascript.
If you can just display the message in the default grails message panel rather than in a popup, then change the controller method to
def create() {
flash.message = "Sorry you are not allowed to create new data."
redirect(action: "list")
}
Prior to sending the user to the GSP view, you should make a decision in the controller, if the user is allowed to execute the create action. In your controller this could be something simple like:
params.allowed = false
The result of this decision is then passed to you GSP where you can evaluate it inside the GSP using something like:
<g:if test="${params.allowed == false}">
Alert: you are not allowed...
</g:if>
For the alerts, there are good looking alternatives to javascript like: http://getbootstrap.com/components/#alerts

returning back to same page if validation fails in grails

I am doing some form validation (using a plugin) in my controller and set a boolean variable called status =true/false if the validation passes/fails
this validation is happening in update action , and the request is coming from edit.gsp.
If the validation fails I want the control to go back to edit.gsp. can I achieve this or I have to create the update.gsp separately and copy the content of edit.gsp to show the edited form values with the errror message on update.gsp page? I want avoid passing all other paramters to backend and back for just 1 fields sanitization check.
It should look something similar like javascript but actually backend validation redirecting to same page on error..
How do I achieve this?
On successful validation I am doing a redirect to manageTemplate.gsp.
Regards,
Thanks in advance
Depending on how you trigger the validation using the plugin I would think the easiest way is to write a custom validator on your domain object. More info: http://grails.org/doc/latest/ref/Constraints/validator.html
If you don't want to (or can't) go down that route you can manually check the validation against the plugin in your controller. It will basically be the same pattern that the scaffolded Grails pages/controllers use.
In your controller you can validate with your plugin and if the domain instance doesn't validate you redirect the User back to the edit action and include the 'params'. That way all the fields will still be populated as they were before.
A updated snippet from a scaffolded Grails controller (for a domain object called Tag):
// ...
// Code above here just gets the domain object to edit (tagInstance in this case)
// and checks that the object hasn't be updated in the meantime
tagInstance.properties = params
// params have been applied to object, you can now do custom validation
def status = extraValidationService.validate( tagInstance ) // or however you run the validation
if ( !status ) {
flash.message = "Did not pass custom validation"
render(view: "edit", model: [tagInstance: tagInstance]) // Back to same edit page (field values maintained)
return
}
// Everything below here is unchanged
if ( !tagInstance.save( flush: true, failOnError: false ) ) {
render(view: "edit", model: [tagInstance: tagInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'tag.label', default: 'Tag'), tagInstance.id])
redirect(action: "show", id: tagInstance.id) // This is where you redirect to the manageTemplate action.gsp
This will not give you the validation highlighting on the fields that failed validation. To get that you can either use the custom validator method or you may (I've never done it) be able to manually 'tell' a domain object which fields failed validation and why.

Grails render view of different controller

My question is similar to this following post
Render a view of another controller
I have a TestConfigController my question is what can I do in case validation fails and I want to render controller:test and view:edit rather then controller:testCOnfig and view:edit
def save() {
def testConfigInstance = new TestConfig(params)
if (!testConfigInstance.save(flush: true)) {
/*Rather then view:"edit" i want view:"/test/edit" which does not work */
render(view:"edit", model: [testConfigInstance: testConfigInstance],id:params.test.id)
return
}
println "+++++++++++++++++++++++++"
flash.message = message(code: 'Data successfully saved', args: [message(code: 'testConfig.label', default: 'Successfully saved')])
redirect(action: "edit", controller:"test", id:params.test.id)
}
Any pointers? I have already looked into grails redirect which does not have "model" param and thus can not pass the validation errors to the view
Also I have looked in to grails render which does not have controller param so that I can go back to different controller!
Please let me know if more detail/code is needed
EDIT
Following happens while using one of the two things
render(view:"/test/edit", model: [testConfigInstance: testConfigInstance],id:params['test.id'])
The code above renders the page /test/edit with no reference to testid eventually erroring out saying "test.id" can not be null.. (means its rendering /test/edit and not /test/edit/1)
render(view:"/test/edit/"+params['test.id'], model: [testConfigInstance: testConfigInstance],id:params['test.id'])
The code above leads to following error
The requested resource (/EasyTha/WEB-INF/grails-app/views/test/edit/1.jsp) is not available.
Either one of the above code renders just "/test/edit" no id at the end, thus eventually erroring out saying test.id can not be null.
The value of id that you are trying to append in the view path should be a part of the model map. The values that you provide in the model map are available in the view that is rendered.
In the first option that you tried, the id parameter doesn't make any difference as the render method doesn't use any 'id' parameter (the redirect method uses the id parameter to generate the redirect url).
Your code snippet should be something like this:
render(view:"/test/edit", model: [testConfigInstance: testConfigInstance, id:params['test.id']])
The render method that you are using here doesn't redirect you to some other action. render just prints the parsed viewName to the output stream. Eg. render(view:"/test/edit") just renders the edit.gsp view. It isn't actually redirecting you to the edit action of test controller. Therefore, just passing the id in the model map won't give you access to the testInstance on the view. You will have to get the testInstance By id and pass it to the view in the model map
render(view:"/test/edit", model: [testConfigInstance: testConfigInstance, testInstance: Test.get(params['test.id'] as Long)])
Anuj Arora is right:
If you just want to render an arbitrary view you can use the full path to the view related to the grails-app/view folder:
In your case:
render(view:"/test/edit", model: [testConfigInstance: testConfigInstance],id:params.test.id)
should work.
If you are only wanting to render the view /test/edit, then the call render(view:'/test/edit',...) should be all you need.
If instead, you also want to include some of the processing from the TestController and edit action, then look at the chain() call. It has a model parameter where you can pass the validation errors and controller/action parameters to redirect to the other controller.

Grails - Very Simple Set Collection Value Not Working

Controller for a Bug:
this is the create method for a bug, I printed out bugInstance.activities and it had my activity object in it
def create = {
def bugInstance = new Bug()
def activity = new Activity(description:"created")
bugInstance.properties = params
bugInstance.addToActivities(activity)
return [bugInstance: bugInstance]
}
Then I looked at the save method, and printed the exact same thing, and the result is null, so somehow it's lost the activity I created, and I have no idea why. Is this really the default behavior? Am I doing something really basic wrong, because there doesn't seem to be any reason such a simple piece of code wouldn't work.
def save = {
def bugInstance = new Bug(params)
println bugInstance.activities
if (bugInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'bug.label', default: 'Bug'), bugInstance.id])}"
redirect(action: "show", id: bugInstance.id)
}
else {
render(view: "create", model: [bugInstance: bugInstance])
}
}
I know I can work around this by adding the activity in the save method, but why do I lose the activity from create() -> save()
You never call save() on the new instance:
def create = {
def bugInstance = new Bug()
def activity = new Activity(description:"created")
bugInstance.properties = params
bugInstance.addToActivities(activity)
bugInstance.save()
return [bugInstance: bugInstance]
}
You don't need to save the Activity because it'll be transitively saved since it's in the activities collection.
might sound like a stupid question but are you setting a hidden parameter or anything in the create.gsp with the Bug Instance instantiated in the create?? I mean, I don't see anything wrong with what your doing here. What does your create.gsp look like?
If you are using the auto-generated create.gsp the activities set will not be included in the form. It is of course there in the model, but there will be no activities field rendered on the client side. When it comes back to save, it is clear that the activity is lost. Depending on what you want to achieve you could add some activity selector to the create.gsp or (to start with) a hidden field with your activities description, but then in the save action I guess you have to handle the the activities param in any case, as the magic of grails doesn't go as far as instanciating the Activity for you. The same way as you instantiate the Bug itself in the save action, you have to instantiate the Activity and even save it, if you want it to be persisted.
Edit: If you really want to pass around the whole activities list, you could make use of indexed properties.
In create.gsp add this:
<g:each status="i" var="activity" in="${bugInstance.activities}">
<!-- one hidden field for each property of each attached activity -->
<g:hiddenField
name="activities[${i}].description"
value="${activity.description}" />
</g:each>
And in the save method this:
params.activities.each{ activity ->
bugInstance.addToActivities(new Activity(activity))
}
But in your case it might be sufficient to instantiate the one activity from a single field.

Resources