How to get typeahead/autocomplete text to refer to foreign id in Rails? - ruby-on-rails-3

So I have been working on getting this Twitter Bootstrap typeahead to work and right now I can get a list of all my data when I start typing it into the form, but when I submit, the values do not get passed in as IDs. Is there any way for me to pass in an id number based on the autocomplete selection?
Here's the code I'm using...
<%= f.text_field :cargo_to_load_id, :data => { provide: "typeahead", source: Cargo.order(:name).map(&:name) , items: "9" } %>

Turns out that I need to have these getter and setter methods in my model file. These will then do the work of finding the corresponding id.
def cargo_to_unload_name
cargo_to_unload(:name)
end
def cargo_to_unload_name=(name)
self.cargo_to_unload = Cargo.find_by_name(name)
end
def cargo_to_load_name
cargo_to_load(:name)
end
def cargo_to_load_name=(name)
self.cargo_to_load = Cargo.find_by_name(name)
end

Related

How can I pass content to a parent template from a child template in Silverstripe

I want to pass some content from a elemental block up to a parent template (to change the page header) - Is this possible? I don't have any example code for what I'm trying because I don't have any idea how to implement it.
This would be similar to the content_for facility in Rails / ERB templates.
Parent:
Page Title <%= yield :title %>
Child Template:
<% content_for :title do %>
<b>, A simple page</b>
<% end %>
Is there anything like this or some other way to do this in SS templates?
Silverstripe Templates are a one-way street - you can't pass values upstream.
What you can do in this case is add a method to the page object to fetch the correct title from the block:
public function getTitleForTemplate()
{
return $this->MyBlock->PageTitle;
}
And then in your template you can simply call that method or as in this example treat it like a property:
Page Title $TitleForTemplate
If you want to use a template to render out this property (e.g. if you have different styling or markup for different blocks which belong to different pages) you can render the template in that method. There are many ways to do this but perhaps the most sensible would be to call renderWith() on the block object:
public function getTitleForTemplate()
{
return $this->MyBlock->renderWith(['type' => 'Includes', 'templates' => ['MyBlockTitle']);
}
and then have a template in your theme's templates/Includes directory called MyBlockTitle.ss which renders out the title with any styling and markup you need for it.
In other words: data belongs in models, the template is just there to display that data. So if you need some specific data to be displayed in the page template, the page object should have that data available.
Disclaimer: I haven't tested this code, it was done from memory. Some of the syntax especially for the second example may be slightly different in reality.
To expand on Guy's answer, to find the block i wanted to use to add to the page title and breadcrumbs, I used the ElementalArea and it's Elements.
public function getTitleForTemplate(){
$output = "" ;
foreach( $this->ElementalArea->Elements() as $element ){
if($element->ClassName == 'MySite\AwardsElement'){
if ( $element->Award() ){
$output .= "{$element->Award()->title } ";
}
}
}
return $output;
}

gmaps4rails select scope

I have gmaps4rails working to show all locations. But, I would like to show a map with workorders using their locations. Workorder belongs_to location.
In the workorders controller, this will display all of the locations:
#json = Location.all.to_gmaps4rails
But, this won't display workorder locations:
#json = Workorder.location.all.to_gmaps4rails
This is the view code:
<%= gmaps("markers" => {"data" => #json, "options" => {"list_container" => "markers_list"} }) %>
<strong><ul id="markers_list"> </ul></strong>
belongs_to specifies a one-to-one association with another class (It returns only one entry).
to_gmaps4rails is a method from Enumerable (it expects to be called on an Array).
Also, you can't call the relation directly from the class (it's not a class method, it's an instance method):
Workorder.location.all.to_gmaps4rails
should be:
a_work_order = WOrkorder.first
a_work_order.location
and to use it with to_gmaps4rails
[a_work_order.location].to_a.compact.to_gmaps4rails
It isn't pretty, but it covers both when location is nil and when it returns something.

Render a template to the database

I'm trying to wrap my around a task and haven't found anyone who has address this issue.
I'm created an awards nomination system.
Anon user submits nomination
Admin user generates a merged letter from template and edits letter
Letter text is saved to database to later generate PDF
I read that you can render an ERB text to a variable. This is working great but then I'm stuck with the text because of the Double Render Error.
def generate_letter
#submission = Submission.find(params[:id])
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
#submission.save
redirect_to #submission
end
Is there a better way to generate this text for the database or a workaround to redirecting? I was trying to avoid keeping my merge template in the code and ERB seems a nice way to handle it.
You need to use render_to_string. This line
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
should be
#submission.letter_text = render_to_string( template: 'submissions/generate_letter.text.erb', layout: false )

detect if a form is submitted with ruby on rails?

Is there a way to detect if a form is submitted? Im trying to set a class based on a custom validation something like below example, is that possible?
.control-group{ :class => ("error" if form_is_submitted ) }
Now trying :
.control-group{ :class => ("error" if params[:user][:profile_attributes][:gender] == nil) }
This fails if the form is not submitted because then the params are nill and throws an error
If your form data is submitted through fields with name attributes like user[profile_attributes][gender] (all having the user prefix), you can check if the :user exists in params.
... if params.include?(:user)
If for some reason (like coming from the route) params[:user] is already going to have a value even for GET requests, you can look for a specific form field having a value. For example, you could add a hidden field
<%= f.hidden_field :some_field, :value => true %>
and check for it in your condition
... if params[:user].include?(:some_field)
You can alternatively check if the request is via the POST method
... if request.post?
This works for other methods as well, like request.put? for an update method.

Duplicating a record in Rails 3

I have a prescription model in my Rails 3 application. I am trying to work out the best method of allowing records to be duplicated, but allowing the user to "review" the duplicate before it's saved.
I have read a number of questions/answers on SO (such as this one) which explain how to duplicate/clone the record and then save it - but none which explain how to show the form before save.
Reading the Rails API is appears the clone method is available.
Reading other questions and answers shows that is can be done but there is no example code apart from:
new_record = old_record.dup
The controller code I am currently working with is as follows (the model doesn't have any relationships):
# POST /prescriptions
# POST /prescriptions.json
def create
#prescription = Prescription.new(params[:prescription])
#prescription.localip = request.env['REMOTE_ADDR']
#prescription.employee = #prescription.employee.upcase
respond_to do |format|
if #prescription.save
format.html { redirect_to #prescription, notice: 'Prescription was successfully created.' }
format.json { render json: #prescription, status: :created, location: #prescription }
else
format.html { render action: "new" }
format.json { render json: #prescription.errors, status: :unprocessable_entity }
end
end
end
I am going to be linking to this clone action from the view with:
<%= link_to "Create another like this?", clone_prescription_url(#prescription), :method => :put %>
Is it as simple as adding an action to my controller like this?
def clone
#prescription = Prescription.find(params[:id])
#prescription.dup
#prescription.save
end
Apologies if the above code is completely wrong, I'm trying to get my head around it! I've seen someone do exactly what I'm trying to achieve with the cloning - but not with the editing before save.
The user that's duplicating won't have permission to edit a record once saved. It's purely for the intial data entry.
To do this, you're going to have to create a new instance of your Prescription class. "dup" works, but you're assuming it overwrites the existing record. Only methods that end with a bang(!) tend to do that.
Your code should be:
def clone
#prescription = Prescription.find(params[:id])
#new_prescription = #prescription.dup
#new_prescription.save
end
or
def clone
#prescription = Prescription.find(params[:id]).dup
#prescription.save
end
This isn't testing for times when the :id isn't found.
If you want the clone action to allow the user to review the duplicate before it is saved (AKA created), then it is almost like the "new" action, except with filled in fields already.
So your clone method could be a modification of your new method:
def new
#prescription = Prescription.new()
end
def clone
#prescription = Prescription.find(params[:id]) # find original object
#prescription = Prescription.new(#prescription.attributes) # initialize duplicate (not saved)
render :new # render same view as "new", but with #prescription attributes already filled in
end
In the view, they can then create the object.
I was looking for logic to clone an existing record. I had to modify the logic posted by ronalchn slightly because when it tried to execute the second statement of clone I got an mass assignment error because it tried to copy id, created_at, updated_at which are not included in my attr_accessible list. This is how I modified the logic to get it to work in my application using my model:
#old_event = Event.find(params[:id]) # find original object
#event = Event.new
#event.field_1 = #old_event.field_1 (statement for each field in attar_accessible)
render :new # render same view as "new", but with #prescription attributes already filled in