I have a model Declaration which has many Costs:
class Declaration < ActiveRecord::Base
has_many :costs
accepts_nested_attributes_for :costs
end
class Cost < ActiveRecord::Base
belongs_to :declaration
end
I want a form where I have 10 cost lines for a declaration, so in the Declaration controller I have the follwing, with the permit params for strong parameters:
def new
#declaration = Declaration.new
#costs = Array.new(10) { #declaration.costs.build }
end
def create
#declaration = Declaration.new(declaration_params)
if #declaration.save
redirect_to user_declarations_path, notice: I18n.t('.declaration.message_create')
else
render action: "new"
end
end
private
def declaration_params
params.require(:declaration).permit(:approval_date, :submit_date, :status, :user_id, :declaration_number,
costs_attributes: [:id, :description, :amount_foreign, :rate, :amount, :cost_date, :projectuser_id])
end
And there is the form of course, so when I submit the form I see this in the log:
Started POST "/users/3/declarations" for 127.0.0.1 at 2013-09-05 19:12:38 +0200
Processing by DeclarationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mhaznOuBy/zj7LA/nIpDTy7X2u5UrR+0jleJsFid/JU=", "declaration"=>{"user_id"=>"3", "cost"=>{"cost_date(3i)"=>"", "cost_date(2i)"=>"", "cost_date(1i)"=>"", "projectuser_id"=>"", "description"=>"", "amount_foreign"=>"", "rate"=>"", "amount"=>""}}, "commit"=>"Opslaan", "user_id"=>"3"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id" ASC LIMIT 1
Unpermitted parameters: cost
So why do I get an unpermitted parameter cost??
Update: declaration form added below:
- if can? :create, Declaration
= form_for [current_user, #declaration] do |f|
= f.hidden_field :user_id, value: current_user.id
.row
.page-header
.span7
%h1.title
%i{ class: "icon-coffee icon-large" }
= I18n.t('.declaration.add_title')
.span5
.action
- if can? :create, Declaration
= link_to I18n.t('.general.cancel'), user_declarations_path(current_user), class: 'btn'
= f.submit(class: 'btn', value: I18n.t('.general.save'))
.row
.span12
= render "layouts/error_messages", target: #declaration
.row
.span12
= render "form", f: f
And the rendered form:
.row
.span12
%table.table.table-striped#declarations
%thead
%tr
%th= I18n.t('.cost.cost_date')
%th= I18n.t('.cost.project')
%th= I18n.t('.cost.description')
%th= I18n.t('.cost.amount_foreign')
%th= I18n.t('.cost.rate')
%th= I18n.t('.cost.amount')
%tbody
- #costs.each do |cost|
= f.fields_for cost, html: { class: "form-inline"} do |c|
%tr
%td{ "data-title" => "#{I18n.t('.cost.cost_date')}" }= c.date_select :cost_date, { include_blank: true, default: nil }
%td{ "data-title" => "#{I18n.t('.cost.project')}" }= c.collection_select :projectuser_id, #projectusers, :id, :full_name, include_blank: true
%td{ "data-title" => "#{I18n.t('.cost.description')}" }= c.text_field :description, class: "input-large"
%td{ "data-title" => "#{I18n.t('.cost.amount_foreign')}" }= c.text_field :amount_foreign, class: "input-small", type: :number, step: "any"
%td{ "data-title" => "#{I18n.t('.cost.rate')}" }= c.text_field :rate, class: "input-small", type: :number, step: "any"
%td{ "data-title" => "#{I18n.t('.cost.amount')}" }= c.text_field :amount, class: "input-small", type: :number, step: "any"
With permit! I get this error message:
Started POST "/users/3/declarations" for 127.0.0.1 at 2013-09-09 09:29:44 +0200
Processing by DeclarationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jQwy7psQwixneWF8DezrR/Wo5VKU/dpfz+sosiatm9c=", "declaration"=>{"user_id"=>"3", "cost"=>{"cost_date(3i)"=>"", "cost_date(2i)"=>"", "cost_date(1i)"=>"", "projectuser_id"=>"", "description"=>"", "amount_foreign"=>"", "rate"=>"", "amount"=>""}}, "commit"=>"Opslaan", "user_id"=>"3"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id" ASC LIMIT 1
Completed 500 Internal Server Error in 6ms
ArgumentError - wrong number of arguments (6 for 0):
app/controllers/declarations_controller.rb:70:in `declaration_params'
app/controllers/declarations_controller.rb:21:in `create'
First impression is you are returning three cost_date parameters. I think this needs to be returned as an array. Your params would then be:
def declaration_params
params.require(:declaration).permit(:approval_date, :submit_date, :status, :user_id, :declaration_number,
costs_attributes: [:id, :description, :amount_foreign, :rate, :amount, :projectuser_id, :cost_date =>[]])
end
Then instead of your web server getting back:
... "cost"=>{"cost_date(3i)"=>"", "cost_date(2i)"=>"", "cost_date(1i)"=>"",...
it should get:
"cost"=>{"cost_date"=>["","",""],...
though without seeing the form I don't know if this is what you are trying to achieve.
It seems that changing this:
- #costs.each do |cost|
= f.fields_for cost, html: { class: "form-inline"} do |c|
to this:
= f.fields_for(:costs) do |c|
Does the trick, because now all costs records are being saved. In the controller I have now this:
#declaration = Declaration.new
10.times do |n|
#declaration.costs.build
end
The only issue I have now left is that it saves empty cost records.
Related
I want that my rails controller index action to render multiple output at once , my controller:
class Api::V1::Ola::OlaBookingsController < ApplicationController
def index
lat = params[:lat].to_s
long = params[:long].to_s
drop_lat = params[:drop_lat].to_s
drop_lng = params[:drop_lng].to_s
ola_query = {
"pickup_lat" => lat,
"pickup_lng" => long,
"drop_lat" => drop_lat ,
"drop_lng" => drop_lng
}
ola_body = {
"pickup_lat" => lat,
"pickup_lng" => long,
"drop_lat" => drop_lat,
"drop_lng" => drop_lng,
"pickup_mode" => "NOW",
"category" => "auto"
}
ola_headers = {
"Authorization" => "Bearer ",
"X-APP-TOKEN" => ""
}
#ola_products = HTTParty.get(
"http://sandbox-t.olacabs.com/v1/products",
:query => ola_query,
:headers => ola_headers
).parsed_response
#ola_booking = HTTParty.post(
"http://sandbox-t.olacabs.com/v1/bookings/create ",
:body => ola_body,
:headers => ola_headers
).parsed_response
render :json => #ola_booking
render :json => #ola_products
end
end
I want both responses to be coming on controller without generating a viw.
But it gives error "multiple render not possible" , how to fix it?
You can not have 2 renders what you can do is combine the 2 objects one after the other like
render :json => #ola_booking.to_json + #ola_products.to_json
you should try it out and let me know how it worked
you can try this.
respond_to do |format|
format.json { render :json => {:ola_booking => #ola_booking,
:ola_products => #ola_products }}
end
I am trying to render the errors in the form if the ajax call to the form URL fails. Below is my Admin component:
#app/assets/javascripts/components/admin.js.coffee
#Admin = React.createClass
# propTypes: ->
# emailVal: React.PropTypes.string.isRequired
getInitialState: ->
edit: false
errorTexts: []
handleToggle: (e) ->
e.preventDefault()
#setState edit: !#state.edit
#setState errorTexts: []
handleDelete: (e) ->
e.preventDefault()
# yeah... jQuery doesn't have a $.delete shortcut method
$.ajax
method: 'DELETE'
url: "/admins/#{ #props.admin.id }"
dataType: 'JSON'
success: () =>
#props.handleDeleteAdmin #props.admins
handleEdit: (e) ->
e.preventDefault()
data = email: ReactDOM.findDOMNode(#refs.email).value
# jQuery doesn't have a $.put shortcut method either
$.ajax
method: 'PUT'
async: false
url: "/admins/#{ #props.admin.id }"
dataType: 'JSON'
data:
admin: data
error: (data, status, xhr) =>
errorTexts = []
for key, value of data.responseJSON
errorText = "#{key} #{value.toString()}"
errorTexts.push errorText
#replaceState errorTexts: errorTexts
#setState edit: true
success: (data, status, xhr) =>
#setState edit: false
#props.handleEditAdmin #props.admin, data
adminRow: ->
dom.tr null,
dom.td null, #props.admin.email
dom.td null,
dom.a
className: 'btn btn-default'
onClick: #handleToggle
'Edit'
dom.a
className: 'btn btn-danger'
onClick: #handleDelete
'Delete'
adminForm: ->
dom.tr null,
dom.td null,
dom.input
className: 'form-control'
type: 'text'
defaultValue: #props.admin.email
ref: 'email'
for errorText, index in #state.errorTexts
React.createElement AdminError, key: index, errorText: errorText
dom.td null,
dom.a
className: 'btn btn-default'
onClick: #handleEdit
'Update'
dom.a
className: 'btn btn-danger'
onClick: #handleToggle
'Cancel'
render: ->
if #state.edit
#adminForm()
else
#adminRow()
The corressponding AdminError component is:
#app/assets/javascripts/components/adminerror.js.coffee
#AdminError = React.createClass
getDefaultProps: ->
errorText: ""
render: ->
dom.div
className: 'help-block'
#props.errorText
While debugging I am getting the correct value of #props.errorText as "email is invalid". But it is not getting rendered on the page and I am geeting this warning in console: "Warning: input is a void element tag and must not have children or use props.dangerouslySetInnerHTML. Check the render method of null."
Attached is the screenshot of both the error and the page.
I tried changing the AdminError component as follows, but it didn't work:
#app/assets/javascripts/components/adminerror.js.coffee
#AdminError = React.createClass
getDefaultProps: ->
errorText: ""
render: ->
dom.div
className: 'help-block'
dangerouslySetInnerHTML: __html: marked(#props.errorText.toString(), {saitize: true})
When I set a debug point at the line returning dangerouslySetInnerHTML, I correctly get the value of #props.errorText as "email is invalid" and value of marked(#props.errorText.toString()) as "email is invalid".. but still the help-block for error is not rendered at all.
UPDATE:
Made the following changes in the AdminError component
app/assets/javascripts/components/adminerror.js.coffee
#AdminError = React.createClass
getDefaultProps: ->
# errorText: ""
errorTexts: []
render: ->
for errorText in #props.errorTexts
dom.div
className: 'help-block'
errorText
and in the Admin component, made the following changes to the adminform method:
if(#state.errorTexts.length>0)
dangerouslySetInnerHTML: {
__html: ReactDOMServer.renderToString(
# for errorText, index in #state.errorTexts
React.createElement AdminError, errorTexts: #state.errorTexts
)
}
not getting the warning anymore, but instead getting the following error:
Uncaught Invariant Violation: ReactCompositeComponent.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
Warning: input is a void element tag and must not have children ...
A dom.input may not have child elements.
But this code is trying to render error messages as children of a dom.input:
dom.td null,
dom.input
className: 'form-control'
type: 'text'
defaultValue: #props.admin.email
ref: 'email'
# These are being rendered as children of the input:
for errorText, index in #state.errorTexts
React.createElement AdminError, key: index, errorText: errorText
Can you render those error messages somewhere else? For example, as siblings of the input:
dom.td null,
dom.input
className: 'form-control'
type: 'text'
defaultValue: #props.admin.email
ref: 'email'
for errorText, index in #state.errorTexts
React.createElement AdminError, key: index, errorText: errorText
My goal is to render a partial view after delete using Ajax to regenerate a table and its pagination. But when I try to do this in my schedules.js.coffee:
$(".delete_schedule").bind "ajax:success", ->
$(this).closest('tr').fadeOut "slow", ->
$("table tr.numbered_row:visible").each (i) ->
$(this).children(".seq").text i + 1
$("#schedules").html('#{ escape_javascript render("schedules")}')
$("#paginator").html('#{ escape_javascript(paginate(#schedules, :remote => true).to_s)}')
the page source of the output is like:
<tbody id="schedules">#{ escape_javascript render(:partial => "schedules")}</tbody>
<div id="paginator">#{ escape_javascript(paginate(#schedules, :remote => true).to_s)}</div>
I wonder why the escape_javascript printed as text instead of run as a command? My suspect was because of there is " inside the $("#schedules").html('#{ escape_javascript render("schedules")}'), but I have to use the " inside my code.
Thanks!
I am using Rails 4, HAML, Coffeescript and Kaminari.
Below is the code of my controller for delete:
schedules_controller.rb
def destroy
#schedule = Schedule.find(params[:id])
#location = Location.find(#schedule.location_id)
#schedules = Schedule.where(:doctor_id => current_doctor.id,
:location_id => params[:location_id] ).order(days_id: :asc).page(params[:page]).per(5)
respond_to do |format|
if #schedule.destroy
format.json { head :no_content, status: :ok }
end
end
end
my main view:
index.html.haml
%h2 Your schedules
- #i = 0
.table-responsive
%table.table.table-striped{:id => "schedules_table"}
%thead
%tr
%th No
%th Day
%th Start
%th End
%th Away?
%th Action
%tbody{:id => "schedules"}
= render :partial => 'schedules'
%br/
%div{:id => "paginator"}
= paginate #schedules, :remote => true
%br/
= link_to 'New Schedule', new_location_schedule_path
|
\#{link_to 'Back', doctor_locations_path(current_doctor)}
And my partial view:
_schedules.html.haml
- #schedules.each_with_index do |schedule, i|
%tr.numbered_row
%td.seq= i + 1
%td
= schedule.find_day_name()
%td= schedule.start_time.strftime("%H:%M")
%td= schedule.end_time.strftime("%H:%M")
%td{:id => "#{schedule.id}"}
%a.status_link.btn.btn-danger.btn-sm{"data-href" => set_schedule_status_path(location_id: params[:location_id], id: schedule.id), :style => "#{schedule.is_away ? '' : 'display: none'}", :id => "#{schedule.id}"}
%i.fa.fa-times
%span Away
%a.status_link.btn.btn-success.btn-sm{"data-href" => set_schedule_status_path(location_id: params[:location_id] ,id: schedule.id), :style => "#{schedule.is_away ? 'display: none' : '' }", :id => "#{schedule.id}"}
%i.fa.fa-check-square-o
%span Available
%td
- concat link_to icon('pencil'), edit_schedule_path(schedule, location_id: params[:location_id])
- concat " | "
- concat link_to icon('remove'), [#location, schedule], method: :delete, remote: :true, data: { confirm: 'Are you sure?' }, :class => 'delete_schedule'
I want to create a "details" row dynamically like below:
I'd like to be able to toggle details for every item on the grid. Can you advise how I can achieve this functionality?
I'm using rails 4 with wice_grid gem
I found out how to do this:
VIEW:
<%= grid(#items_grid) do |g|
g.after_row do |fill, number_of_columns|
content_tag(:tr, class: 'extra-row') do
content_tag(:td,
content_tag(:div) do
# without buffer only the last tag will appear
buffer = content_tag(:p,"data1: #{item.add_data1}")
buffer += content_tag(:p,"data2: #{item.add_data2}")
raw buffer
end,
colspan: number_of_columns)
end
g.column name: "ID", attribute: 'id' do |item|
item.id
end
g.column name: "Data", attribute: 'data' do |item|
item.data
end
g.column do |item|
button_tag("Details", class: "btn btn-default toggle-trigger")
end
end -%>
.JS:
$(document).on("page:load ready", function(){
$(".toggle-trigger").click(function(){
$(this).parents('tr').next('.extra-row').slideToggle("fast");
return false;
});
});
.CSS:
.extra-row {
display: none;
}
I have model
class Building
include Mongoid::Document
include Geocoder::Model::Mongoid
field :address, :type => String, :default => ""
field :location, :type => Array, spacial: {lat: :latitude, lng: :longitude, return_array: true }
## Building index
index({location: "2d"})
def latitude
location[1]
end
def longitude
location[0]
end
def latitude=( lat )
location[1] = lat
end
def longitude=( lng )
location[0] = lng
end
end
this form in the view
= f.text_field :latitude
= f.text_field :longitude
and this is the controller
...
def create
#building = Building.new(building_params)
respond_to do |format|
if #building.save
format.html { redirect_to #building, notice: 'Building was successfully created.' }
format.json { render action: 'show', status: :created, location: #building }
else
format.html { render action: 'new' }
format.json { render json: #building.errors, status: :unprocessable_entity }
end
end
end
private
def building_params
params.require(:building).permit(:address, :latitude, :longitude)
end
...
The latitude and longitude are populated in the form, but when I change the value and save it, it didn't work, even the other field like address is changed in the database. But it's working flawlessly in console
> b = Building.first
> b.latitude = -7.27094221115
> b.save
=> true
i think problem on params, from form you have string latitude and longitude, but need float.