I try to render a view, which works fine, but it doesn't seem to get the model object I pass along to it. I can't figure out the reason, as this should be very straightforward according to all manuals and examples.
Model object
class Race {
def distance = "1/4 mile"
def racer1
def racer2
}
RaceController renders here
def doFullRace(Race race) {
render (view: 'raceProgress', model: [race: race])
}
and raceProgress.gsp should display it easily enough
<html>
<body>
<div id="raceStart" align="center">
...
<p>${race.racer1} is racing ${race.distance} against ${race.racer2}</p>
</div>
</body>
</html>
but I instead I get this
Any ideas on what basic thing I missed?
You have the following:
def doFullRace(Race race) {
render (view: 'raceProgress', model: [race: race])
}
One of the ways for race to be null there is if all of the following are true:
Race is a domain class
The request submitted to doFullRace includes a request parameter named id
There is no record in the database with an id that matches params.id
From http://docs.grails.org/3.3.9/guide/theWebLayer.html#commandObjects...
If the command object’s type is that of a domain class and there is an
id request parameter then instead of invoking the domain class
constructor to create a new instance a call will be made to the static
get method on the domain class and the value of the id parameter will
be passed as an argument.
And...
If the command object’s type is a domain class and there is no id
request parameter or there is an id request parameter and its value is
empty then null will be passed into the controller action unless the
HTTP request method is "POST", in which case a new instance of the
domain class will be created by invoking the domain class constructor.
For all of the cases where the domain class instance is non-null, data
binding is only performed if the HTTP request method is "POST", "PUT"
or "PATCH".
Related
My home_controller.rb looks likes like this:
class HomeController < ApplicationController
def index
#post_a = Post.where(title: 'a').sample
#post_b = Post.where(title: 'b').sample
end
And index.html.erb is like this:
<div>
<%= #post_a.title %>
<%= #post_b.title %>
</div>
When the page is refreshed, #post_a.title and #post_b.title are changed.
Is there any way to prevent data from being changed by refresh or redirect?
It is a basic property of RESTful, hence state-less, systems like those created by Rails that a response to a request cannot directly access what is done in another response. Everything that is to be accessed beyond responses have to be saved somewhere, usually databases.
In order to access from the second response the values of the variables you have set in the first response, the standard way is to save information in session. You can perhaps read this.
Given that you are using redirect, another way is to pass the values as parameters to the path helper method when you call the redirect. Instead of doing:
redirect_to foo_bar_path
you can do:
redirect_to foo_bar_path(post_a_id: #post_a.id, post_b_id: #post_b.id)
and then in the controller action at the destination of redirect, you can access the ids of #post_a and post_b as params[:post_a_id] and params[:post_b_id].
The variables hold the value you assign to them. As you are taking a random record (using sample), the result of the query is a random record fetched from the database, therefore the value of those 2 variables is sort of random.
If you want a predictable record to be assigned to #post_a and #post_b, then you need to update the query to fetch the record you want.
For example, fetch by ID:
#post_a = Post.find(1)
I've been working on several rails tutorial and I don't understand why the method new and method create both have
#example = Example.new
What is the point for the new method to have #example = Example.new when all this controller action does is to render the new page?
New
#example = Example.new meaning to prepare your form so user can fill the data in form and #example instance will hold the value.
Create
#example = Example.new meaning create new object to save your data into database inside create usually #examle.save, this will save your data that passed from new (through params)
Within Rails' implementation of REST new and create are treated differently.
An HTTP GET to /resources/new is intended to render a form suitable for creating a new resource, which it does by calling the new action within the controller, which creates a new unsaved record and renders the form.
An HTTP POST to /resources takes the record created as part of the new action and passes it to the create action within the controller, which then attempts to save it to the database.
From the Ruby on Rails documentation about create:
create(attributes = nil) {|object| ...}
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
From the Ruby on Rails documentation about new:
new(attributes = nil) {|self if block_given?| ...}
New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table — hence you can‘t have attributes that aren‘t part of the table columns.
So create instantiates the new object, validates it, and then saves it to the database. And new only creates the local object but does not attempt to validate or save it to the DB.
What is the point for the new method to have #example = Example.new when all this controller action does is to render the new page?
Because it renders the page using the attributes of the #example object.
You may be assuming that on a newly initialised object, all the attributes will have a value of nil. This is not necessarily the case, because code in the model (an after_initialize callback, or an enum, for example) may be providing default values.
The controller code itself might provide default values also in some cases, either inferred from the current user or passed in through parameters.
You will be having create method like this:
def create
#example = Example.new(example_params)
if #example.save
redirect_to #example
else
render 'new'
end
end
Notice that inside the create action render is getting used instead of redirect_to when save returns false. The render method is used so that the #example object is passed back to the new template when it is rendered.
You need to tell the user that something went wrong. To do that, you'll modify app/views/examples/new.html.erb to check for error messages:
<% if #example.errors.any? %>
<div id="error_explanation">
...
</div>
<% end %>
We check if there are any errors with #example.errors.any?, and in that case we show a list of all errors with #example.errors.full_messages.
The reason why #example = Example.new in the ExamplesController is that otherwise #example would be nil in our view, and calling #example.errors.any? would throw an error.
Is there a way to check if it was a POST request in Grails, I mean like we do in PHP (if (isset($_POST))). I need it for form submission code in the same controller action which renders a form.
def myform {
if (POST) {
myModel.save
}
render view: myView, model: [user: myModel]
}
I cannot use params, because there are always some parameters and it's not empty.
You can do if(request.method == 'POST')
In a Grails controller you have access to request which is an HttpServletRequest. Using the getMethod() method you should be able to do something like this:
if (request.getMethod().equals('POST'))
Simple question - I have a form where I create an instance of an object. After I create that object (aka submit the form), I want to redirect to a new form that is associated with a different controller's action and carry that instance variable to populate some of that form's fields.
I know I typically have 2 options, store that instance variable in the session or pass it via params. I can't use sessions (for a variety of reasons I won't bore you with). The params option I am confused on.
I should know this. :( How would you go about doing this? Any examples greatly appreciated!!
Betsy
You'll have two methods on your controller. One for each form (rendered by the associated template). The first form should post to the second action. The second action can then transfer the request parameters into instance variables, to be available within the second template.
class FooController
def bar
# setup instance variables and render first form
end
def baz
#bar_values = params[:bar]
# setup other instance variables and render second form
end
end
UPDATE0 Do it across two controllers using session.
class FooController
def new_baz
# setup instance variables and render the first form
end
def create_baz
# respond to posting of form data
session[:current_baz_values] = params
redirect_to :action => "baq", :controller => "bar"
end
end
class BarController
def baq
#baz_values = session[:current_baz_values]
# setup other instance variables and render the second form
end
end
Could you somehow just do a find of the newly created record in the other controller, and then use that to populate the info you need?
Also, unless you are using AJAX you usually don't want to have modification actions on the show page for a record. Those belong on the edit or update page. If you always want people to be able to edit a record on the same page I would either use some AJAX on the show page, or just always return the edit/update page instead...
If you do not want to use sessions, you could use the flash variable to store your parameter. Something like, flash[:my_params] = params, and then reading it back in the next request with params = flash[:my_params]. The good thing about flash, is that persists for only the next request, and auto-clears after that.
If you are looking for passing values from the client side when using Ajax, then probably setting a hidden field with the parameters is going to pass them on to the next request.
I'm trying to pass a model attribute to a view, after successfully setting it to a new value from inside an action in my controller. But this variable is always nil by the time it gets to the view, so I can't use it to conditionally display stuff. I should add that this attribute is not a field in the database. What am I missing/doing wrong?
Here is the code in my model:
attr_accessor :mode
#getter
def mode
#mode
end
#setter
def mode=(val)
#mode = val
end
...in the controller:
#report.mode = "t"
redirect_to edit_report_path(#report)
...and in my view:
<%= build_report(#report.mode) %>
...but this helper method never gets the variable I just set in the controller. It is nil. What gives? Clearly I'm missing something basic here because this seems like it should be straightforward. Any insight would be greatly appreciated.
Thanks.
edit_report_path generates a URL with the ID of #report in it.
redirect_to essentially creates a whole new request, and goes to that URL. When it gets to edit, all it has is the ID. Usually that's fine - it looks up the object and keeps going, but of course it's not going to have the non-db field you set.
There are a couple ways to fix this. You can use :render instead to get to the edit page - then #report will have the field set.
#report.mode = "t"
render :action => edit and return
Or, you can make mode a database field.
The problem here is in the redirect_to. When you redirect somewhere else all instance variables are lost. So when you set #report.mode = "t" it sets the attribute. But when you redirect that data is lost.
I am assuming the <%= build_report(#report.mode) %> is in edit_report.html.erb and the code from when you set 'mode' is not in the edit action. If this is the case you may be able to pass the report.mode to the edit action in the url like so:
build_report(#report.mode, :mode => "t")
The problem is the redirect_to; you're returning a response to the client that causes it to redo the request with a different url. In that second request the mode isn't set because you didn't save it before wrapping up the first request.