implement "create another like this" functionality in grails - grails

After saving a new instance of a domain class in grails, I have a "create another like this" button that brings up another create screen where the fields are populated with the values of the instance I just created.
In the first try, I passed all the existing field values as params in an alternate create button:
<g:link class="create" action="create"
params="[app:volInstance.app.id,
ass:volInstance.assessment.id,
name:volInstance.volName,
type:volInstance.volType.id,
note:volInstance.volNote,
recommendation:volInstance.recommendation,
discovered:volInstance.dateFound,
url:volInstance.urlParam]">
Create Another like this
</g:link>
and then doing a lot of <g:if> on the next create.gsp to see if the parameters are present. I then advanced to just sending the instance id as a param
<g:link class="create" action="create"
params="[vid:volInstance.id]">
and changed the create method in the controller. This simplified things (no longer have a huge params list):
def create() {
if (params.vid) {
def id = params.vid
def v = Vol.findById(id)
params.volNote = v?.volNote
params.volType = v?.volType
etc......
}
respond new Vol(params)
}
This works nicely and eliminates all the <g:if>s but still have a lot of lines of params.x = v.x
would there be a way to get rid of those lines and just pass the object as a param?

Looks like a good place for a Command Object. You can declare it in your controller, and then pass it as an argument to your action. You can even add validation if you want.
class MyCommand {
Long id
String volNote
String volType
static constraints = {
volNote (blank: false)
//...
}
}
Then in your action:
def create(MyCommand cmd) {
Long id = cmd.id
//...

Related

Grails: can we pass string as id in g:link? Or any other way?

Scenario:
I want to create g:link which will redirect to
www.xyz.com/controller/action/title_of_question
instead
www.xyz.com/controller/action/id_of_question
I know that later one can be created easily configuring id param of the g:link.
Please note that title_of_question is String separated with hyphen (-)
Can anybody suggest me how to achieve it?
Short answer: yes, we can :)
If you have no rule in UrlMappings.groovy which defines id as Long or Integer you can create links with String identifier like below:
<g:link controller="controllerName" action="show" id="someName">Link!</g:link>
And define controllers action with identifier as String:
def show(String id) {
...
}
I think the best thing to do is create a new Action in your controller that takes your ID, and redirects your to the URL you are trying to goto.
<g:link resource="book" action="name" id="${id}">New Book</g:link>
then in your controller.
def name(int id) {
//take the ID, get the name, and redirect to the proper .gsp.
}
I would suggest doing the following:
def someAction() {
if(params.id.matches(/[0-9]+/)) {
//logic for id of question
} else {
//you could check if it is separated with dashes
//logic for title of question
}
}
This would trigger the id logic:
www.xyz.com/controller/someAction/15
And this the 'title' logic:
www.xyz.com/controller/someAction/title-of-question

how to make Drop Down Lists in gsp from BootStrap.groovy

I want to make a drop down using the data given in the BootStrap.groovy.
My City domain class
package city.model
class City {
String cityName
static constraints = {
cityName(maxSize: 50) }
}
In my Service class
public class CityService {
def citySelect(String cityName) //this is just a sample
{
City city = new City()
city.cityName = city.findByCityName(cityName)
}
}
controller
public class CityController {
def cityService
def
def selCity() {
def selectCity = cityService.citySelect(params.cityName){
if(selectCity != null){
render view // my view
}
else{
render view // error select again view
}
}
}
BootStrap.groovy
import city.City;
class BootStrap {
def init = { servletContext ->
for(String cityName in ['Addis Ababa', 'Semera','Asosa','Gondar', 'Jijiga','Harar', 'Dire Dawa', 'Bahir Dar',
'Hawassa', 'Arba Minch', 'Adama', 'Mekelle']) {
City.findOrSaveByCityName(cityName).save()
}
}
def destroy = {
}
}
i used this
<g:select name="cities" from="${City.list()}" optionKey="id" optionValue="cityName"/>
in my view but showing error cannot envoke method list on null object
What is wrong within mu code and what should i do to make it work. please any suggestions
<g:select name="city" from="${city.model.City.list()}" optionValue="${cityName}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
this could work fine.
Maybe you have misunderstood the point of Bootstrap.
Bootstrap as per the naming convention is the part that is triggered when your site is booting up.
You would typically use it to ensure required db table records are generated before it has booted up i.e. admin account or in your case generation of some cities.
You would not be using Bootstrap to interact with the records you generated in the way of editing or selecting.
Once this has all be done and saved - you would have also used the Controllers/Views to list/view/update/add cities.
You would create your g:select tags in these views and matching controllers that would query the records you have saved via bootstrap
E2A:
Ok Just read your comment
Either use an import on the top of your gsp
<%# page import="city.City" %>
or call full packaged path to City domainClass city.City.list
<g:select name="cities" from="${city.City.list()}" optionKey="id" optionValue="cityName"/>
To create a list of the Cities you inserted into the Database in the Bootstrap.groovy (in a view i.e [viewName].gsp)
Mark up is like so
<g:select name="city" from="${city.model.City.list()}" value="${city.name}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
However, the Bootstrap should be for initialization of Database and Application defaults, Also to perform start (Startup in the init closure while shutdown in the destroy Closure).
This should work:
<g:select name="cities" from="${city.model.City.list()}"
optionKey="id" optionValue="cityName"/>
However, executing queries like city.model.City.list() in a GSP is not a recommended practice. Instead you should retrieve your data (the list of cities) in a controller action or a service and pass that via the model to the GSP.

No save when forwarding an action?

Grails 2.2.0
I'm exploring grails and ajax, and maybe I'm an overzealous ajax adaptor, but I really think its a gamechanger, so I'm going in headfirst.
Datamodel is a master detail (1:n). A table in the client shows a piece of the list of master's properties. And upon clicking, the line folds open showing all the details (An attribute domain) and an option to add a new detail to the unfolded master. All well, opening this line calls
def show (Long id) {
def product = Product.get(id)
log.info('show ' + (request.xhr ? 'xerhr' : 'normal') + "product is ${product.title}")
def c = Attribute.createCriteria();
def attributeTypes = LookupValue.findByType('m_attr') //list of values for dropdown menu.
def result = c {
eq("movie", product)
}
for (attr in result) {
log.info('value: ${attr.value}')
}
render ([template: "attributes", model:[focus:params.id
, attributeList: result, attributeTypes: attributeTypes]])
}
Where the attributes templates displays all the related attributes (details) and an add option at the end where you can enter a new value and select a value from a dropdown menu. The client does do an ajax post back to the controller's saveAttribute method to save this new Attribute:
def saveAttribute(Long id, Long luv_id, String value) {
def attribute = new Attribute(movie : Product.get(id)
, label : LookupValue.get(luv_id)
, value : value);
attribute.save();
show();
}
and again calls show() to render the newly created list of attributes back to the client. Now show() does render all the existing attributes but it fails to retrieve the just created attribute whereas it definitely did end up in the database. It also shows up when I reload the page. Where do I go wrong? With hibernate I think I would do a session.flush() to get over this.
And maybe my architecture is wrong and I need to define a service and introducing some transaction boundaries?
def saveAttribute(Long id, Long luv_id, String value) {
Attribute attribute = new Attribute(movie: Product.get(id)
, label: LookupValue.get(luv_id)
, value: value).save(flush: true)
forward(action: "show", id: id)
}

Grails: Dynamically call another action

Is it possible to call another controller-action with the name of the controller-action being passed as a parameter. Something like this:
View:
<g:createLink controller="book" action="list"
params="[id: '1', onCompleteController='nextCon', onCompleteAction='nextAct']"/>
Controller (Book):
def list = {
... //Do something like save book
**execute onCompleteController/onCompleteAction**
return render(text: [success:true] as JSON))
}
you can do that by using forward or redirect (which ever fits in your case).
Some thing like
redirect(controller:onCompleteController, action: onCompleteAction, model:[])
forward(controller:onCompleteController, action: onCompleteAction, params:[])

Passing values from action class to model class in symfony

I have a action class for saving some data to a database.In action class iam getting a id through url.I have to save the id in table.
I am getting the id by $request->getParameter('id')
I used this code for saving
$this->form->bind($request->getParameter('question_answers'));
if ($this->form->isValid())
{
$this->form->save();
$this->redirect('#homepage');
}
in model class i used a override save method to save extra field
public function save(Doctrine_Connection $conn = null)
{
if ($this->isNew())
{
$now=date('Y-m-d H:i:s', time());
$this->setPostedAt($now);
}
return parent::save($conn);
so i want to get the id value here .
So how can i pass id value from action class to model class
is any-other way to save the id in action class itself
Thanks in advance
Just use
$this->form->getObject()->setQuestionId($request->getParameter('id'));
$this->form->save();
QuestionId=field name
$request->getParameter('id')= is the default value
Most probably not the best solution but you can save that id value in a session variable and use it later anywhere you need. something like:
$this->getUser()->setAttribute('id', $request->getParameter('id')); // in action class
and
sfContext::getInstance()->getUser()->getAttribute('id'); // in model class
If your model has a field for this ID already (eg in your example posted_id), then you can do one of 2 things:
1: Pass the ID to your form when you create it, as an option:
$this->form = new MyForm(array(), array("posted_id" => $id));
and then you can override your form's doSave() method to set the field:
// ...
$this->getObject()->posted_id = $this->getOption("posted_id");
// ...
or similar
2: Add a widget to your form in the configure() method with a corresponding validator, and pass in the ID when you get the values from the request:
$values = $request->getParameter('question_answers');
$values["posted_id"] = $request->getParameter("id");
$this->form->bind($values);
If you're rendering your form manually, just don't render this new field in your view template.
Either way, saving the model as a result of the form saving it will include this new field I believe. The second one is from memory, but it should technically work... :-)
Best practice I'm using consists of 2 steps:
Pass additional data to forms' options (2nd constructor's parameter or just $form->setOption('name', 'value'));
override protected method doUpdateObject in way like this:
protected function doUpdateObject($values)
{
$this->getObject()->setFoo($this->getOption('foo'));
parent::doUpdateObject($values);
}
Enjoy!
You could use (sfUser)->setFlash and (sfUser)->getFlash instead of setAttribute, it's more secure...

Resources