Using grails 2.3.7. I am trying to use Grails controller action parameter binding. If I have this code:
class TestController {
def test(MyClass1 myClass1) {
log.debug(myClass1)
}
}
myClass1 is correctly fetched from DB using http://locahost:8080/myapp/test/test/1.
But now I want to pass two domain classes. I have tried this code:
class TestController {
def test(#RequestParameter('obj1') MyClass1 myClass1,
#RequestParameter('obj2') MyClass2 myclass2) {
log.debug(myClass1)
log.debug(myClass2)
}
}
And access using http://localhost:8080/myapp/test/test?obj1.id=1&obj2.id=3, nothing is fetched. Is this the right way to use data binding in controller actions? Or is this impossible?
Regards and thanks in advance.
You can use this and one of in your controller :
// binds request parameters to a target object
bindData(target, params)
// exclude firstName and lastName
bindData(target, params, [exclude: ['firstName', 'lastName']])
// only use parameters starting with "author." e.g. author.email
bindData(target, params, "author")
bindData(target, params, [exclude: ['firstName', 'lastName']], "author")
// using inclusive map
bindData(target, params, [include: ['firstName', 'lastName']], "author")
def User bindUser(params) {
def User user = new User()
def Human human = new Human()
bindData(user, params["user"])
bindData(human, params["humna"])
if(!human)
human.save(failOnError:true)
if(!user)
user.save(failOnError:true)
}
//alloha~
}
Related
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)
If we have one controller, let's call it document, that has two methods, one that uploads file and another that shows the uploaded file.
I would like to define a new string in the upload method that checks the size of the file and store a specific type name inside that string.
However I would like to access that string in another method which is the list method to be able to show it.
Here is my code:
Class DocumentController {
def list() {
//Here I would like to access that String to show it on the page
[fileSizeType: fileSizeType]
}
def upload {
//define the new String variable
String fileSizeType = ""
if(fileSize < 1000) {
fileSizeType = "type1.."
} else {
fileSizeType = "type2.."
}
}
}
In the gsp page I would like to access the string this way:
<td><g:link>\${fileSizeType}</g:link></td>
I am getting this error when I try the code above:
No such property: fileSizeType for class: file_down.DocumentController
You need to redirect to the list action while passing your argument in the params.
def upload() {
// simplify with ternary expression
def fileSizeType = (fileSize < 1000) ? "type1.." : "type2.."
redirect action:'list', params:[fileSizeType: fileSizeType]
}
// in your list action
def list() {
[fileSizeType: params.fileSizeType]
}
In a Grails project I am looking at here, a filter puts a Domain object on the request...
class TokenFilters {
def filters = {
all( uri: '/hiphop/**' ) {
before = {
MyToken myToken = ...
request.myToken = myToken
MyToken looks like:
class MyToken {
String id
String token
static mapping = {
token( index: true )
id( generator: 'uuid' )
}
...
}
In my controller, the myToken is pulled off the request.
MyController {
myaction {
MyToken accessToken = request.myToken
All fine. I wish to write an integration test for the controller.
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
mc.request.parameters = [myToken:myToken];
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}
When I run this, I get:
Failure: testLogin(MyTests)
| java.lang.IllegalArgumentException: Parameter map value must be single value or array of type [java.lang.String]
at testLogin(MyTests.groovy:40)
So it looks like Grails will only let me a String or a single value and doesn't like me putting an object on the request in the Filter. Even thou it lets me put on the same object type in a Filter.
I'd really like to test this without going to Functional tests. Please help. I am using Grails 2.2.1
Thanks
The problem is that your code is passing parameters to the controller. Your emulating an HTTP request which can't handle objects. What you can do is:
mc.request.parameters = [myToken: '1234']
and then you're controller/filter would pull out the 1234 and look up MyToken. If you were testing the controller forwarding then you can put objects in the request. Not the other way around.
I see now that part of the problem is that you're trying to test a controller that is assuming data coming from a filter.
You've omitted some code, but assuming you are extending ControllerUnitTestCase then you have access to a mock request object. You should be able to simply do:
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
request.myToken = myToken
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}
I have one gsp file which calls a method like this:
<g:link id="${child.id}" action="callChildProfile" controller="profile">${child.firstname}</g:link>
which calls this method
def callChildProfile(Long id){
childInstance = Child.get(id)
System.out.println(childInstance.firstname + " child instance")
redirect(action: "index")
}
this method set a child instance to a public variable called child instance but when the redirect happens the variable is reset.
The reason I redirect is because I want to load up the index page from this controller.
Index looks like this:
def index() {
def messages = currentUserTimeline()
[profileMessages: messages]
System.out.println(childInstance + " child here")
[childInstance : childInstance]
}
By default controllers are prototype scoped, which means the instance of ProfileController used will be different between the request which calls callChildProfile and the request which calls index. Thus, the object level childInstance variable won't be available between requests.
To use the Child instance in the index call, look at the chain method:
callChildProfile(Long id){
// do usual stuff
chain(action:"index", model:[childInstance:childInstance])
}
def index() {
// do other stuff
[otherModelVar:"Some string"]
}
When returning a Map from index the model of the chain call will be automatically added, so your childInstance from the callChildProfile will be available for the gsp.
Variables in controller methods (actions) have a local scope, thus, only can be used in that method. You should pass the id from new instance and use that id for retrieve the object.
redirect action: "index", id: childInstance.id
and index could be
def index(Long id){
childInstance = Child.get(id)
Then you can conclude that you don't need the callChildProfile method
or you can use params
def index(){
childInstance = Child.get(params.id)
if(childInstance){
doSomething()
}
else{
createOrGetOrDoSomethingElse()
}
}
I have a User class which has a List field namely pt. This field is not initialized when User register his account. But when user goes this controller action :
def updatePt() {
//performs some action
def user = User.get(springSecurityService.principal.id) //find the user
user.pt = []
//on certain conditions i put values into user.pt like this
user.pt << "E"
//at last I save it
user.save()
}
But using user/show action via scaffolding I found that pt field is not saved on users object. Where I'm making a mistake?
You have to provide a static mapping in the Users domain class so that Grails knows the field must be persisted:
class User {
static hasMany = [pt: String]
}
It's possible because of validation error. Try with
if (!user.save()) {
log.error('User not saved')
user.errors.each {
log.error('User error: $it')
}
}
PS or you can use println instead of log.error