Wrong controller action invoked? - ruby-on-rails

Rails 3.2
This is my second attempt at solving this problem.
I inherited an application for processing customer tickets. The main ticket view, is large and complicated, so it's split up into multiple sections.
I am trying to add a new section to enter customers information.
I have a scaffolding for CustomerInfo, with a controller and a model, in the normal locations:
controllers/customer_infos_controller.rb and models/customer_info.rb.
I put the views under views/tickets/sections
In my views/tickets/show.html.slim, I have:
- #customer_info = customer_info #ticket
h4.form-header Customer Information
.form-section.attachments
- if #customer_info.nil?
= render partial: 'tickets/sections/customer_info', locals: {ticket: #ticket }
In my , views/tickets/sections/_customer_info.html.slim, I have:
= form_for(CustomerInfo.new, url: customer_info_path) do |f|
- f.hidden_field :ticket_id, :value => ticket.id
.form-horizontal-column.customer-info
.form-group
= f.label :first
= f.text_field :first, maxlength: 50
.form-group
= f.label :last
= f.text_field :last, maxlength: 50
.actions = f.submit 'Save'
.clear
In my routes.rb, I have:
post '/customer_infos' => 'customer_infos#create', as: 'customer_info'
When I run the application, the form displays properly. But, when I enter the first name, and last name, the ticket gets updated.
Looking at the log file, I see that when the Save button in the Customer Information section, I see:
Processing by TicketsController#update as HTML
In the log, I also saw the params:
{
"ticket":
{
.......................
},
"customer_info":
{
"first":"John",
"last":"Doe"
},
"commit":"Save"
}
rake routes gives me:
customer_info POST /customer_infos(.:format) customer_infos#create
This is the HTML:
<div class="form-horizontal-column customer-info">
<div class="form-group">
<label for="customer_info_first">First *</label>
<input id="customer_info_first" type="text" size="50" name="customer_info[first]" maxlength="50">
</div>
<div class="form-group">
<label for="customer_info_last">Post tax total *</label>
<input id="customer_info_last" type="text" size="50" name="customer_info[last]" maxlength="50">
</div>
<div class="actions">
<input type="submit" value="Save" name="commit">
</div>
</div>
This is the portion:
<form accept-charset="UTF-8" action="http://xxx.xxxx.xxxx.com/admin/tickets/159384" class="form form-horizontal tickets-form context-form" data-admin="true" enctype="multipart/form-data" id="edit_ticket_159384" method="post">
So, there is no customer_info portion, with the proper action and method.
Why is the tickets controller update method is invoked, instead of the CustomerInfo create method? I also don't see the ticket_id param in the params.

Related

Rails & Stimulus for dynamic nested attribute forms

there, I'm coming back to Rails after years of only using it for APIs. There all sorts of new things and I'm trying to figure out how to accomplish some stuff with the new frameworks. One example is creating a form with accept_nested_attributes and adding dynamic has_many associations.
I have Company model and Partner models.
class Company < ApplicationRecord
# ... name, size, registration_type, etc
has_many :partners, dependent: :destroy
accepted_nested_attributes_for :partners
end
class Partner < ApplicationRecord
# ... name, email, phone
belong_to :company
end
In my form I have:
<%= form_with(model: company) do |form} %>
<!-- ... -->
<div class="hidden" data-company-form-target="partnersForm">
<%= form.fields_for :partners, company.partners do |partner_form| %>
<div data-company-form-target="partnerFormInner">
<div class="form-group">
<div>
<%= partner_form.label :name, "Name" %>
<%= partner_form.text_field :name %>
</div>
<div>
<%= partner_form.label :email, "Email" %>
<%= partner_form.text_field :email %>
</div>
<div>
<%= partner_form.label :phone_number, "Phone Number" %>
<%= partner_form.text_field :phone_number %>
</div>
</div>
</div>
<% end %>
<div>
<button data-action="click->company-form#addPartner">
+
</button>
</div>
</div>
<% end %>
I have a js controller that uses the button to add a new line to the form to add more partners:
import { Controller } from "stimulus";
export default class extends Controller {
static targets = [ "partnerFormInner" ]
addPartner(e) {
e.preventDefault(); e.stopPropagation();
this.partnerFormInnerTarget.insertAdjacentHTML('beforeend', this.formTemplate)
this.count++
}
connect() {
this.count = 1
}
get formTemplate() {
return `<div>
<div>
<label for="company_partners_attributes_${this.count}_name">Name</label>
<input type="text" name="company[partners_attributes][${this.count}][name]" id="company_partners_attributes_${this.count}_name">
</div>
<div>
<label for="company_partners_attributes_${this.count}_email">Email</label>
<input type="text" email="company[partners_attributes][${this.count}][email]" id="company_partners_attributes_${this.count}_email">
</div>
<div>
<label for="company_partners_attributes_${this.count}_phone_number">Phone Number</label>
<input type="text" phone_number="company[partners_attributes][${this.count}][phone_number]" id="company_partners_attributes_${this.count}_phone_number">
</div>
</div>`
}
}
Now this all works fine, however I feel like the js controller is a little hacky, copying the HTML output from a single partner and pasting it into my controller w/ some interpolation... I feel like there's probably some way for me to get that template directly from the Rails backend so that I have it all defined in one place in a partial or something, but I'm not sure how to connect those dots.
Is there a way to move the partner form to a partial and dynamically pull code for the next line in the form and insert it via JS, or do I need to just keep doing what I'm doing with the copy/paste?

Sending POST request in Rails

How would I go about sending an HTTP POST request in the following format in a Rails setup:
curl https://api.venmo.com/v1/payments -d access_token=4e4sw1111111111t8an8dektggtcbb45 -d email="someemail#gmail.com" -d amount=5 -d note="Delivery."
I'd like to have users input the email/amount/note parameters into a form on a webpage, then pass that data (when the user clicks the submit button) into a Controller where the access_token parameter is stored to then fire off the POST request.
So far I've tried setting up the Controller with this method (and calling it from the view html.erb):
def make_payment
if !params[:access_token].nil?
form_data = {
"email" => #email_to,
"amount" => #amount_to,
"note" => #note_to,
"access_token" => :access_token
}
url = "https://api.venmo.com/v1/payments"
request = Net::HTTP::Post.new(url, form_data)
response = http.request(request)
end
end
Here's my current view setup:
<div class="box box-warning">
<div class="box-header">
<h3 class="box-title">Pay Bill using Venmo Account</h3>
<div id="payment_note_input">
<input id="payment_note" type="text" class="form-control" required placeholder="add a note to your payment">
</div>
<div id="payment_target_input" >
<input id="payment_target" type="text" class="form-control" required placeholder="pay an email address">
</div>
<div id="payment_amount_input">
<input id="payment_amount" type="text" class="form-control" required placeholder="enter the payment amount! ">
</div>
</br>
<button class="btn btn-danger" type="button" onClick= <%=make_payment%> > Make payment</button>
</div>
I feel like I'm close to a solution here...
You can use httpparty gem to achieve that, its easy to use in only 1 line:
response = HTTParty.post("https://example.com?param1=value1",
:body => {:text => data}.to_json,
:headers => {'Content-Type' => 'application/json'}
)
You can remove Body and Header if you don't have specific Body or Header,
and if you want to achieve Get request its even easier:
responce = HTTParty.get('http://example.com.json')
json=JSON.parse(responce.body) # in case you expect json responce
You need to use a form in order to generate a POST request from the web page. Rails provides you with Form helpers that would help you achieve this.
<div class="box box-warning">
<div class="box-header">
<%= form_tag make_payment_path do %>
<%= hidden_field_tag "access_token", #access_token %>
<h3 class="box-title">Pay Bill using Venmo Account</h3>
<div id="payment_note_input">
<%= text_field_tag "payment_note", nil, class: "form-control", placeholder: "add a note to your payment" %>
</div>
<div id="payment_target_input" >
<%= text_field_tag "payment_target", nil, class: "form-control", placeholder: "pay an email address" %>
</div>
<div id="payment_amount_input">
<%= text_field_tag "payment_amount",nil, class:"form-control", placeholder: "enter the payment amount! ">
</div>
</br>
<%= sumbit_tag "Make payment", class:"btn btn-danger" %>
<% end %>
</div>
And then you can access the form variables in your controller by...
def make_payment
access_token = params[:access_token]
note = params[:payment_note]
target = params[:payment_target]
...
end

Defining field label in a block - on error, field_with_errors div tag not in right place

I am using validates_acceptance_of :terms, :message => "must be accepted" in my user.rb model, and am using bootstrap-sass.
My check box code looks like this in the view:
<div class="control-group">
<%= f.label :terms, :class => "control-label" do %>
Accept <%= link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal") %>
<% end %>
<div class="controls">
<%= f.check_box :terms %>
</div>
</div>
For some reason, when the terms check box isn't selected on form submission, the appropriate error message shows up at the top of the form, but there is a problem with the field_with_errors div class wrapping around the check box label.
The HTML for the rendered page looks like this:
<div class="control-group">
<label class="control-label" for="user_terms">
Accept Terms of Use *
</label>
<div class="controls">
<input name="user[terms]" type="hidden" value="0" />
<div class="field_with_errors">
<input id="user_terms" name="user[terms]" type="checkbox" value="1" />
</div>
</div>
</div>
The result is that the check box field label isn't highlighted on error. Is there a way to force the div tag placement for the field_with_errors class to show up just after the <div class="control-group"> tag? Why does using a block to define a field label throw off the field_with_errors tag placement? Does anyone have experience with this?
Thank you
This is a bug i think. The problem is in block. Define your label without block and everything works.
Try something like:
<% modal_html = capture do >
Accept <%= link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal") %>
<% end %>
<%= f.label :terms, modal_html, :class => "control-label" %>
Or helper:
def modal_html
#Q{Accept #{link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal")} }.html_safe
end

Simple_form bootstrap style inline-form not working properly

I have a working twitter bootstrap install and simple form generates the following:
<form accept-charset="UTF-8" action="/find_map" class="simple_form form-inline" id="new_location" method="post" novalidate="novalidate"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="p5CSoidWaoGMfHY0/3ElWi0XJVg6Cqi9GqWRNlJLBQg=" /></div>
<div class="control-group string required"><div class="controls"><input class="string required" id="location_address" name="location[address]" placeholder="Address" size="50" type="text" /></div></div><input class="btn" name="commit" type="submit" value="Find!" />
</form>
Somehow the "Find!" button won't appear on the same line as the search box. Any ideas?
Thanks!
UPDATE:
Sorry I should have mentioned that all the markup is generated by simple_form based on the following:
<%= simple_form_for #location, :url => find_map_path, :html => { :class => 'form-inline' } do |f| %>
<%= f.input :address, :label => false, :placeholder => "Address" %>
<%= f.submit "Find!", :class => 'btn' %>
<% end %>
So, really, there seems to be an issue with the generated markup, even though I have run the bootstrap install for simple_form, etc.
The above image shows a straight html form
<form class="form-inline">
<input type="text" class="input-small" placeholder="Email">
<button type="submit" class="btn">Sign in</button>
</form>
...above the one generated by simple_form.
I think there are a couple issues here. One is the formatting, and the way simple_form adds a <div> around the input field. #Ron's suggestion of using input_field works for me with simple_form 2.0.1. My example is searching for name in a Contacts table. The following makes the text box and button appear side by side:
<%= simple_form_for :contact, :method => 'get',
:html => { :class => 'form-search' } do |f| %>
<%= f.input_field :search, :placeholder => "Name",
:class => "input-medium search-query" %>
<%= f.submit "Find!", :class => "btn" %>
<% end %>
The other issue is that it seems simple_form usually assumes you want to work with model and field names. The example above uses a :symbol instead of a #model as the first argument as suggested here. But that still generates an input field named contact[search] so you'd have to tell your controller how to deal with that.
I think in this case it may be simpler to not use simple_form and instead use something like the form near the beginning of Ryan Bates' Railscast #240, Search, Sort, Paginate with AJAX:
<%= form_tag contacts_path, :method => 'get', :class => "form-search" do %>
<%= text_field_tag :search, nil, :placeholder => "Name",
:class => "input-medium search-query" %>
<%= submit_tag "Find!", :name => nil, :class => "btn" %>
<% end %>
Now the field is just named "search" and I can consume it in my controller's #index method something like this:
#contacts = #contacts.search(params[:search])
assuming I have this in my model:
def self.search(search)
if search
where('lower(name) LIKE ?', "%#{search.downcase}%")
else
scoped
end
end
It's creating subforms because you're passing input to simple_form. Use input_field instead. (BTW, this also works with simple_fields_for).
You need to customize the control-group and controls div classes to display as inline-block when they are under a form-inline form:
form.form-inline div.control-group { display: inline-block; }
form.form-inline div.control-group div.controls { display: inline-block; }
Adding to Mark's reply:
So, input_field exists to create the input component only, it won't give you any sort of label/error/wrapper. That means you won't get any or tag wrapping the field, you should do that manually in case you want to.
Now about using the form with an object, SimpleForm is a FormBuilder, which means it was created to work with a namespace, either an object or a symbol. SimpleForm's simple_form_for == Rails' form_for, you always need an object namespace to work with.
For such a simple case as a search form, you're better off with simple form helpers such as form_tag, as you've pointed out. There's no need to rely on simple_form_for or form_for for that, I agree and I usually go down that path.
I hope that helps, let me know if you still have doubts.
Change the :html => { :class => 'form-inline' } to :html => { :class => 'form-horizontal' }
can't you move the input button next to input address? I think it will solve the problem
<form accept-charset="UTF-8" action="/find_map" class="simple_form form-inline" id="new_location" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="p5CSoidWaoGMfHY0/3ElWi0XJVg6Cqi9GqWRNlJLBQg=" />
</div>
<div class="control-group string required">
<div class="controls">
<input class="string required" id="location_address" name="location[address]" placeholder="Address" size="50" type="text" />
<!-- move the button to here -->
<input class="btn" name="commit" type="submit" value="Find!" />
</div>
</div>
</form>
Please, all people with this problem, don't use fluid layout and be sure you are specifying the HTML5 doctype to the documents.

How to create a multi-edit form in rails

I need to create a multi-edit form in rails, like so:
<form>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
<input type='text' name='input1'></input>
<input type='text' name='input2'></input>
<input type='text' name='input3'></input>
<input type='text' name='input4'></input>
<input type='text' name='input5'></input>
<br>
... and so on, then the "<submit>" button will be at the very end. One click of the submit button at the end should collect all the values and parse them in the controller.
I just need to know how to generate the multi-edit form in the view. Also, each row is unique; I'd also need to know how to assign a unique identifier to each of the input tags I guess; I do have a unique ID value I could use.
This is trivial to accomplish, but we need more information. How are these fields related to your models? Is this one model with many fields, many instances of a model or something else?
What you want to do in this situation is use a form builder. It will generate input fields according to a naming convention that will be parsed into a much more useful format when it gets to the controller. Since I have no information about your models, I will use a hypothetical example:
class Post < ActiveRecord::Base
attr_accessible :title, :body, :author, :published_at
end
Create the form using the form_for helper. It will give you a formbuilder object to create the input fields.
<% form_for :post do |f| -%>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body %>
<%= f.text_area :body %>
</p>
<p>
<%= f.label :author %>
<%= f.text_field :author %>
</p>
<p>
<%= f.label :published_at %>
<%= f.datetime_select :published_at %>
</p>
<% end -%>
The key benefit of using helpers is the name attribute of the inputs it generates. Since body belongs to a form for post it will be given the name attribute post[body]. These attributes will be parsed into the following hash:
:post => {
:title => "This is the title",
:body => "this is the body",
:author => "John Doe",
:published_at => "Mon Nov 15 2010 19:23:40 GMT-0600 (CST)"
}
This means you don't need to manually copy fields into a model. You can just pass it directly to the Model#new method:
#post = Post.new(params[:post])
and then do your validation checks. This convention becomes indispensable when you start nesting models inside one another.
See here for a more thorough guide to form helpers.

Resources