I have most common in rails code
def new
#company = Company.new
#companies = Company.order(:name).pluck(:name, :id)
end
def create
#company = Company.find(params["company"]["id"]) rescue nil
unless #company
render action: 'new'
return
end
status = #company.update_attributes(total_licenses: params["company"]["total_licenses"].to_i, assigned_licenses: 0)
if status == true
redirect_to users_super_admin_index_path, flash: {notice: "License has been allocated to company."}
else
render action: 'new'
end
end
but when somthing wrong it should render action new, but it directly render template hence #company remains nil and throws an error
ActionView::Template::Error (First argument in form cannot contain nil or be empty)
I want to find the permanent and right solution, no hacks please :) . And the reason why i am facing this problem.
In my view -
<%= form_for #company, url: licenses_path, method: "post" do |f| %>
<%= f.label :id, 'Select Company' %><br/>
<%= f.select :id, #companies, :include_blank => "Select Company", required: true %><br/><br/>
<%= f.label :total_licenses, 'License' %><br/>
<%= f.text_field :total_licenses, required: true%><br/><br/>
<%= f.submit 'Assign'%>
<% end %>
Remember that render(action: ...) does not actually run the method in question, it just renders out the template. You will need to manually trigger the new method to do this.
Related
I am trying to get the id of selected option from dropdown but i get the nill id when i create the project
def create
developer_id = params[:developer_id]
parameters = project_params.merge({ user_id: current_user.id, developer_id: developer_id })
#project = Project.new(parameters)
respond_to do |format|
if #project.save
format.html { redirect_to projects_path, flash: { success: 'Project added successfully ' } }
else
format.html { render :new }
end
end
end
def project_params
params.require(:project).permit(:name, :user_id)
end
new.html.erb
<%= form_for :project, :html => {:class=>"form-group"}, url: projects_path do |f| %>
Add task: <%=f.text_field :name, class:"form-control" %><br>
<h2>Select Developer</h2>
<%= f.select :developer_id, options_for_select(#users.collect {|user|["#{user.name}","#{user.id}"]}) %><br>
<%= f.submit "Add" %>
<% end %>
The developer_id is nested under project just like the other params.
developer_id = params[:project][:developer_id]
or
developer_id = params.dig(:project, :developer_id)
When you are unsure where to find certain params or if they even exist then the easiest way is certainly to look into your applications log file. There you should see the incoming request and all the params and how they are nested.
I have a standard create method in the controller responsible for creating a new Transaction record. The Transaction record has a mandatory transaction_type field which I hide in the view and automatically assigning it a value by passing it a params[:filter] so I have one _form for both withdrawal and deposit transactions, like below:
#index.html.erb
<%= link_to 'Add funds', new_transaction_path(filter: 'deposit') %>
<%= link_to 'Withdraw Funds', new_transaction_path(filter: 'withdrawal') %>
#new.html.erb
<%= render 'form', transaction: #transaction, transaction_type: params[:filter] %>
#_form.html.erb
<%= simple_form_for #transaction do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<%= f.text_field :transaction_type, value: transaction_type, type: "hidden" %>
<%= f.input :amount, placeholder: 'Amount', label: false %>
<%= f.button :submit, 'Submit' %>
<% end %>
If for some reason the validation fails, to properly display the errors, the :new view will be rendered. Unfortunately, in this case, if the user fills out the entire form again (after first failed), the record will not be created because params[:filter] was not passed. Is there any way to pass original params[:filter] directly to the view?
#controller
# POST /transactions
def create
#transaction = wallet.transactions.new(transaction_params)
if #transaction.save
redirect_to :index, notice: 'Transaction was successfully created.'
else
render :new
end
end
While I understand the aspect of reusing the view code you really should consider creating separate routes and controllers and solving the code duplication issues by using inheritance and locals instead of by sneaking along a hidden parameter.
resources :deposits, :withdrawls, only: [:new, :create]
class TransactionsController < ApplicationController
helper_method :create_transaction_path
def new
#transaction = Transaction.new
render 'transactions/new'
end
def create
#transaction = Transaction.new(transaction_params) do |t|
t.transaction_type = transaction_type
end
if #transaction.save
yield #transaction if block_given?
success_response
else
yield #transaction if block_given?
failure_response
end
end
private
def transaction_type
controller_name.singularize
end
def create_transaction_path
polymorphic_path(controller_name)
end
def transaction_params
params.require(:transaction)
.permit(:foo, :bar, :baz)
end
def success_response
redirect_to transactions_path,
notice: 'Transaction was successfully created.'
end
def failure_response
render 'transactions/new'
end
end
class DepositsController < TransactionsController
# POST /deposits
def create
super do |transaction|
# do something just when making a deposit
end
end
end
class WithdrawlsController < TransactionsController
# POST /withdrawls
def create
super do |transaction|
# do something just when making a withdrawl
end
end
end
# app/views/transactions/_form.html.erb
<%= simple_form_for transaction, url: create_transaction_path do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<%= f.input :amount %> # Do not use placeholders instead of labels
<%= f.button :submit, 'Submit' %>
<% end %>
<%= link_to 'Add funds', new_deposit_path %>
<%= link_to 'Withdraw Funds', new_withdrawl_path %>
Why?
Because it gives you endpoints that do a single job and it also gives you the obvious structure for your code when the requirements diverge as they most certainly will.
Stashing the value as a hidden field in the form is the right idea, but you're using two different parameter names for the same thing.
Your link_to call passes the transaction type as filter:
<%= link_to 'Add funds', new_transaction_path(filter: 'deposit') %>
In your form, you are putting it in a hidden field called :transaction_type. Thus, when the form submits the value now goes to your controller in params[:transaction_type]. One simple fix is to change the name of your hidden field:
<%= hidden_field_tag :filter, params[:filter] %>
Goal: Update existing records with a modal without needing to link_to a new page.
Issue: I assume my issue is that I am unable to identify the exact record on the page with the form because I can't know this until the form is submitted.
ShopProduct Controller:
def new
#shop_product = ShopProduct.new
end
def create
#shop_product = ShopProduct.new(shop_product_params)
#shop = Shop.find_by(params[:shop_id])
product = Product.find_by(params[:product_id])
#shop_product.product_id = product.id
#shop_product.shop_id = #shop.id
if #shop_product.save!
redirect_to '/'
flash[:notice] = "saved"
else
redirect_to '/'
flash[:notice] = "no saved"
end
end
def update
#shop_product = ShopProduct.find_by(store_variant_id: params[:store_variant_id])
respond_to do |format|
if #shop_product.update_attributes!(product_id: params[:product_id], sync: params[:sync])
format.html { redirect_to #shop_product, notice: 'Shop product was successfully updated.' }
format.json { render :show, status: :ok, location: #shop_product }
else
format.html { render :edit }
format.json { render json: #shop_product.errors, status: :unprocessable_entity }
end
end
end
Aside from linkingto a new page, I can only think of defining directly on the
I load the form from this ShopDashboardController:
def product_variants
#shop = Shop.find(params[:shop_id])
session = ShopifyAPI::Session.new(domain: #shop.shopify_domain, token: #shop.shopify_token, api_version: '2019-04')
ShopifyAPI::Base.activate_session(session)
#in_store_products = ShopifyAPI::Product.find(:all)
#in_store_product = ShopifyAPI::Product.find(params[:shopify_product_id])
#in_store_variants = ShopifyAPI::Variant.find(:all, params: { product_id: params[:shopify_product_id]})
#shop_products = ShopProduct.where(shop_id: #shop)
#products = Product.all
#shop_product = ShopProduct.find_or_create_by(store_variant_id: params[:store_variant_id])
end
Now, as mentioned above, the only unique record for any ShopProduct is the id and the store_variant_id... If i use find_by in the def product_variants, the page won't load due to not being able to identify the #shop_product. I am unable to pass those params through because there may be multiple store_variant_ids, so I pass the Shop.id and ShopProduct.store_product_id only. But the store_product_id isn't a unique identifier as multiple records can have the same one. The only unique records are the id and store_variant_id.
Form (the variant is from a do loop):
<% #in_store_variants.each do |variant| %>
...
<%= form_for #shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
...
<% end %>
I am able to create new records only.
When i use the form again to update I get:
ActiveRecord::RecordInvalid (Validation failed: Store variant has already been taken):
app/controllers/shop_products_controller.rb:61:in `create'
Model ShopProduct:
belongs_to :product
has_one :shop
has_one :order
validates :store_variant_id, uniqueness: true, on: :create
If the record exists, shouldn't it update? Or is there something I am missing here?
It is possible to pursue my goal with rails/ruby alone or is javascript needed?
UPDATE:
I tried defining the ShopProduct on the front-end like so:
<% #in_store_variants.each do |variant| %>
<% shop_product = #shop_products.find_by(store_variant_id: variant.id) %>
<%= form_for shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
<% end %>
When submitting:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"gaMboYCSE8v63TVzmgx4pZDMhoz205f1MV+VMhmFA/WWhVh5Pcu6u/qayU8lDmjeRXw==", "shop_product"=>{"product_id"=>"1", "store_product_id"=>"1965345", "store_variant_id"=>"19364273", "shop_id"=>"1", "sync"=>"true"}, "commit"=>"Sync", "id"=>"12"}
Error:
NoMethodError (undefined method `update' for nil:NilClass):
or with update attributes:
NoMethodError (undefined method `update_attributes!' for nil:NilClass):
If it's finding it, shouldn't it be working? The param is being passed
It's because that form only caters the create action. Usually, if you need to update a resource, you go to /shop_products/:id/edit.
But if you really wanted to reuse that form, it's a little bit complicated adding more conditions, but what you want is to send a PUT request to /shop_products/:id and it would call the #update action of your controller. A form, by default, sends a POST request so consider that.
I have a combined/nested signup form, which registers a new organization plus a member (two models with a 1:many relationship).
When submitting invalid information to the form (that is, I fill in invalid info for the organization and no info at all for the member) it should render the combined signup form again with the error message. Instead in this scenario after submitting the invalid information, it only renders the part of the signup form for signing up the organization (without the member part of the form). If I enter invalid information for both organization and member, then it does render correctly the full combined form.
Does anyone have an idea what is wrong with the code?
The controller includes:
def new
if (logged_in?)
flash[:danger] = "You're already logged in"
redirect_to root_url
end
#organization = Organization.new
#member = #organization.members.build
end
def create
#organization = Organization.new(organizationnew_params)
if #organization.save
#organization.members.each do |single_member|
single_member.send_activation_email
end
flash[:success] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new' # This is the relevant render line.
end
end
The new view:
<%= render partial: "registrationform", locals: { url: organizations_path } %>
The partial (registrationform):
<% if local_assigns.has_key? :url %>
<%= form_for #organization, url: url do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<h4>Details of the organization:</h4>
<%= f.text_field :name, class: 'form-control' %>
<%= f.fields_for :members do |p| %>
<h4>Your personal details:</h4>
<%= p.email_field :email, class: 'form-control' %>
<%= p.password_field :password, class: 'form-control' %>
<%= p.password_field :password_confirmation, class: 'form-control' %>
<% end %>
<%= f.submit "Sign up" %>
<% end %>
<% else %>
<%= flash.now[:danger] = "Error: no url defined" %>
<% end %>
Routes:
get 'signup/organization' => 'organizations#new', as: 'register'
In your new action, you are doing #organization.members.build, which is not in your edit action. When the validation fails, it will render your new action, but won't run your new action. You could try putting #organization.members.build in the else clause of your create action like:
else
#organization.members.build if #organization.members.blank?
render 'new'
end
I've get a little problem.
My controller:
def new
#company = Company.new
#title = "Create company"
end
def create
#company = Company.new(params[:company])
#company.admin_id = current_user.id
if #company.save
flash[:success] = "Company created!"
redirect_to admin_path
else
#title = "New company"
render 'new'
end
end
new.html.erb
<%= debug params[:company] %>
<% form_for #company, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.submit "Submit company!" %>
</div>
<% end %>
Company.rb model
validates :name,
:presence => true,
:length => { :maximum => 20 }
validates_attachment_presence :logo
But after submitting form I've get anyway only one error:
Name can't be blank
Of course I'm filling name and logo fields.
Any ideas? Thanks.
You didn't include your _fields partial, and that's probably where your problem is.
Make sure your inputs have appropriate name attributes.
Try to instantiate your model in console and see if validations really work.
You can try something like this: c = Company.new; c.valid?; c.errors and you'll see your errors hash in console.