I have an "Accept/Reject" workflow for tasks (created with nested forms via Cocoon). Basically,
if 1) the current user is the assignee (current_user.email == :assignee), 2) the current user is not the creator (current_user.email != :creator), and 3) the task has not been accepted (true) or rejected (false) i.e. nil (:accepted != nil),
then 1) show the creator (:creator) and 2) the enabled field to accept or reject (f.select :accepted)
When I am using these if statements, the fields (creator & accept/reject) are showing regardless of whether the condition is true or false. In addition, I am not allowed to use current_user.email in my if statement even though I can print it just fine.
Here's my code:
_task_fields.html.haml (views > projects)
.nested-fields
.field
= f.label :description
= f.text_field :description
.field
= f.label :done
= f.check_box :done
- if (:accepted != nil) && (:creator != nil)
.field
= f.label :creator
= f.text_field :creator, :disabled => true
.field
= f.label :accepted, "Accept or Reject"
= f.select :accepted, options_for_select([nil,['Accept',true],['Reject',false]])
.field
= current_user.email
= f.label :priority
- if :priority == nil
= f.select :priority, options_for_select(["None","Low","High"],"None")
- else
= f.select :priority, options_for_select(["None","Low","High"],:priority)
.field
= f.label :assignee
= f.select :assignee, User.pluck(:email), :prompt => "Select One"
= link_to_remove_association "remove task", f, :class => "btn btn-default btn-sm"
_form.html.haml (views > projects)
= form_for #project do |f|
.jumbotron
%h3 Project Settings
.field
= f.label :title
= f.text_field :title
#category
.form_row
= f.label :category
- if #project.category == "process"
= f.radio_button :category, "process", :checked => 'checked'
= "process"
= f.radio_button :category, "checklist", :checked => #project.category == "checklist"
= "checklist"
- else #project.category == "checklist"
= f.radio_button :category, "process", :checked => #project.category == "process"
= "process"
= f.radio_button :category, "checklist", :checked => 'checked'
= "checklist"
.field
= f.label :description
= f.text_field :description
- if #project.category == "process"
%h3 Steps
#steps
= f.fields_for :steps do |step|
%ul.list-group
= render 'step_fields', :f => step
.links
= link_to_add_association 'add step', f, :steps
- if #project.category == "checklist"
#tasks
= f.fields_for :tasks do |task|
.jumbotron
= render 'task_fields', :f => task
.links
.jumbotron
= link_to_add_association 'add task', f, :tasks, :partial => 'projects/new_task_fields', :class => "btn btn-default btn-sm"
= f.submit
projects_controller.rb (just the relevant parts)
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
format.js
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
format.js
end
end
end
def project_params
params.require(:project).permit(:title, :category, :description, tasks_attributes: [:id, :description, :done, :priority, :assignee, :creator, :created_at, :accepted, :completed_at, :_destroy])
end
schema.rb (just the relevant parts)
create_table "tasks", force: true do |t|
t.string "description"
t.boolean "done"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "due"
t.boolean "started"
t.boolean "active"
t.string "repeat"
t.integer "time_limit"
t.string "priority"
t.string "assignee"
t.string "creator"
t.datetime "started_at"
t.datetime "completed_at"
t.datetime "paused_at"
t.datetime "resumed_at"
t.integer "time_spent"
t.boolean "accepted"
end
add_index "tasks", ["project_id"], name: "index_tasks_on_project_id"
I can add more files if necessary but I doubt my controller/model seems to be the issue. Thanks and appreciate any input.
- if (:accepted != nil) && (:creator != nil)
The above statement will always be true, because you are checking if a symbol is not equal to nil which is always true
You should check something like
- if (#project.accepted != nil && #project.creator != nil)
Related
now i'm doing creating a worker with role. I have worker , role and worker_role. Now i have done the create worker form and have a selection to choose role. After done the form i need to role_id and worker_id store in worker_role table. I have try and it return me some error.
worker controller
def new
#worker = Worker.new
#worker.company_id = params[:company_id]
#role = Role.all
end
def create
#worker = Worker.new(worker_params)
#company_id = Company.find(params[:worker][:company_id])
#role_id = Role.all
if #worker.save
#worker_role = Role.find(params[:id]).worker_roles.create(worker: worker, returned: true)
#log_in #worker
flash[:success] = "Welcome to the Areca Supermarket System!"
redirect_to #worker
else
render 'new'
end
end
new.html.erb(the form)
<% provide(:title, "Register worker") %>
<h1>Create Worker</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #worker, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :phone_number %>
<%= f.number_field :phone_number, class: 'form-control' %>
<%= f.label :address %>
<%= f.text_field :address, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :role_id %>
<%= f.select(:role_id, Role.all.collect { |l| [ l.name, l.id] }, {class: "form-select"}) %>
<%= f.hidden_field :company_id , value: 2%>
<%= f.submit "Create worker", class: "btn btn-primary" %>
<% end %>
</div>
</div>
worker_role model
class WorkerRole < ApplicationRecord
belongs_to :worker
belongs_to :role
end
worker_role migration
create_table "worker_roles", force: :cascade do |t|
t.integer "worker_id"
t.integer "role_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
worker migration
create_table "workers", force: :cascade do |t|
t.string "name"
t.string "email"
t.integer "phone_number"
t.string "address"
t.integer "company_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.boolean "admin", default: false
end
Error show in website
parameter in website
Hi as mentioned in request params, I can see that role_id you are passing in params as
Parameters:
params = {"authenticity_token"=>"[FILTERED]",“worker"=>{"name"=>"Daniel", "“email"=:“daniel#example.com", “phone_number"=:"Q134657891",“address"=>"Alor Setar", “password"=>"[FILTERED]", “role id"=>"1", “company _id"=:"2",“commit"=>"Create worker"}
#you can access the role ID as below:
params[:worker][:role_id]
But you are using it in the controller as
#worker_role = Role.find(params[:id])
#this should be as below:
#worker_role = Role.find(params[:worker][:role_id]).worker_roles.create(worker: #worker)
# OR
#if you are permitting role id
#worker_role = Role.find(worker_params[:role_id]).worker_roles.create(worker: #worker)
I have admin account to edit some posts from other users, mostly typo stuff. When I use the controller below, it updates user_id since I get that from current_user (admin in my case), so it updates the post with my account, and post looks like submitted by me. it overwrites actual user id.
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
btw this is my _form.html.erb which is used both for new and edit.
<%= simple_form_for(#post) do |f| %>
<%= f.error_notification %>
<%= f.input :user_id, input_html: { value: current_user.id }, as: :hidden %>
<%= f.input :title, required: true, autofocus: true, placeholder: "Post title: *", label: false, input_html: { maxlength: 140, size: 40 } %>
<%= f.input :link, required: true, placeholder: "Post link: *", label: false, input_html: { maxlength: 300, size: 40 } %>
<%= f.input :description, placeholder: "Summary of the Post", label: false, input_html: { maxlength: 600, :rows => 5 } %>
<%= f.button :submit, "Post" %>
<% end %>
and my schema
create_table "posts", force: :cascade do |t|
t.string "title"
t.string "link"
t.text "description"
t.integer "user_id"
t.string "slug"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["slug"], name: "index_posts_on_slug"
t.index ["user_id"], name: "index_posts_on_user_id"
end
How can I skip updating user_id? I tried to code below just to define the fields I wanted to update, but it doesn't work. Thank you
if #post.update(post_params[:title, :link, :description])
Try this instead, thanks!
#post.title = post_params[:title]
#post.link = post_params[:link]
#post.description = post_params[:description]
Put this before respond do and replace if #post.update(post_params) to if #post.save
Another newb question. A Band has_many Albums. I'm getting the error:
ActiveRecord::AssociationTypeMismatch (Band(#70076964285020) expected,
got "1" which is an instance of String(#12787800)):
app/controllers/albums_controller.rb:20:in `create'
... which is the build line in #create
albums controller
def new
binding.pry
#band = Band.find(params[:band])
authorize #band, :admin?
#album = Album.new
respond_to do |format|
format.js
end
end
def create
binding.pry
#band = Band.find(params[:album][:band].to_i)
authorize #band, :admin?
#album = #band.albums.build(album_params)
if #album.save
#albums = #band.albums
##eps = #band.eps
#songs = #band.songs
respond_to do |format|
format.js
end
else
#fail = "fail"
respond_to do |format|
format.js
end
end
end
def album_params
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
end
the form:
<%=simple_form_for(#album, remote: true, :authenticity_token => true, format: :js) do |f| %>
<%= f.hidden_field :band, :value => #band.id %>
<%= f.input :album_name %>
<%= f.input :album_release_date %>
<%= f.input :etc %>
<div id="albumsubmit">
<div class="form-actions">
<%= f.button :submit, "Create Album", class: "btn btn-primary" %>
</div>
</div>
schema
create_table "albums", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "band_id"
t.string "album_name"
t.string "album_release_date"
t.index ["band_id"], name: "index_albums_on_band_id"
end
when you passing to save method param with that have has_many Association he expect the instance of "band", just set the param name to band_id
here:
<%= f.hidden_field :band, :value => #band.id %>
to:
<%= f.hidden_field :band_id, :value => #band.id %>
and here:
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
to:
params.require(:album).permit(:band_id, :album_name, :album_release_date, :etc)
I am doing an e-commerce.
I have products which have many options of products and at the same time they only have one variant.
I try to make the view to create the product have the option of add a block where appears the fields of the model and the changes of the variant which is associated to it. The problem is, for example, when i create a product with 5 options, when i update it increases to 10, and if i update it again, there will be 20. I can't find the problem why they are duplicating. I leave the codes below.
Schema of Product, option-product and variant
create_table "options_products", force: :cascade do |t|
t.integer "product_id"
t.float "price"
t.integer "stock"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "products", force: :cascade do |t|
t.string "name", default: "", null: false
t.text "description"
t.integer "category_id"
t.integer "vendor_id"
t.string "state", null: false
t.boolean "shippingInside", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "priceComparison"
t.string "image1_file_name"
t.string "image1_content_type"
t.integer "image1_file_size"
t.datetime "image1_updated_at"
t.float "price"
end
create_table "variants", force: :cascade do |t|
t.string "tipoVariant"
t.integer "options_product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "controlStock"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
end
Script for add and remove fields
$(document).on 'ready page:load', ->
$('form').on 'click', '.remove_field', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_field', (event) ->
time = new Date().getTime()
regular_expression = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regular_expression,time))
event.preventDefault()
Product create and update
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product}
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product}
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Product Params
def product_params
params.require(:product).permit(:name, :description, :state, :shippingInside, :vendor_id, :category_id, :priceComparison, :image1, :price, offer_attributes: [:status], options_products_attributes: [:price, :stock, variant_attributes: [:tipoVariant, :controlStock, :image]])
function in application helper to add association
def link_to_add_association(name, field, association)
new_object = field.object.send(association).klass.new
new_object_id = new_object.object_id
fields = field.fields_for(association, new_object, child_index: new_object_id) do |builder|
new_object.build_variant
render(association.to_s.singularize + '_field', f: builder)
end
link_to(name, '#', class: 'add_field', data: { id: new_object_id, fields: fields.gsub("\n", "") })
end
Product model
class Product < ActiveRecord::Base
#relations
belongs_to :category
belongs_to :vendor
has_one :offer, :dependent => :destroy
has_many :options_products, :dependent => :destroy
#accepts
accepts_nested_attributes_for :offer, allow_destroy: true
accepts_nested_attributes_for :options_products, allow_destroy: true
#validations
validates :name, presence:true
validates :name, uniqueness:true
validates :state, presence:true
validates :category_id, presence:true
validates :vendor_id, presence:true
has_attached_file :image1, styles: {medium: "300x300>", thumb: "150x150#" }
validates_attachment_content_type :image1, content_type: /\Aimage\/.*\z/
end
Option Product Model
class OptionsProduct < ActiveRecord::Base
belongs_to :product
has_one :variant, :dependent => :destroy
accepts_nested_attributes_for :variant, allow_destroy: true
end
Variant model
class Variant < ActiveRecord::Base
belongs_to :options_product
has_attached_file :image,
styles: {medium: "300x300>", thumb: "150x150#" }
validates_attachment_content_type :image,
content_type: /\Aimage\/.*\z/
end
_form of Product
= form_for #product, html: { multipart: true } do |f|
.row
.form-group.col-lg-6
.field
= f.file_field :image1
.row
.form-group.col-lg-6
.field
= f.text_field :name, :placeholder => 'Nombre', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.text_area :description, :placeholder => 'Descripcion', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.number_field :price, :placeholder => 'Precio a mostrar', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.label :Estado
%br/
= f.select :state, options_for_select(['Disponible', 'No disponible'])
.row
.form-group.col-lg-6
.field
= f.label :Envio
%br/
= f.check_box :shippingInside
.row
.form-group.col-lg-6
.field
= f.text_field :priceComparison, :placeholder => 'Precio anterior', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.label :vendor_id
%br/
= f.select :vendor_id, Vendor.all.collect { |vendor| [vendor.name, vendor.id] }
.row
.form-group.col-lg-6
.field
= f.label :category_id
%br/
= f.select :category_id, Category.all.collect { |category| [category.name, category.id] }
= f.fields_for :offer, #product.build_offer do |o|
= o.label :Oferta
%br/
= o.check_box :status
%br/
.row
= f.fields_for :options_products do |op|
= render 'options_product_field', f: op
= link_to_add_association 'Agregar variante', f, :options_products
%br/
.actions
= f.submit "Enviar", :class => 'button btn btn-primary bold'
options_product_field file
%fieldset
.row
.form-group.col-lg-6
.field
= f.text_field :price, :placeholder => 'Precio', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.text_field :stock, :placeholder => 'Stock', :class => 'form-control input-border-left'
= f.fields_for :variant do |v|
.row
.form-group.col-lg-6
.field
= v.text_field :tipoVariant, :placeholder => 'Tipo de variante', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= v.label :ControlarStock
%br/
= v.check_box :controlStock
.row
.form-group.col-lg-6
.field
= v.label :ImagenDeVariante
%br/
= v.file_field :image
= f.hidden_field :_destroy
= link_to 'Remover variante', '#', class: 'remove_field'
In product_params, you should specify id of options_products_attributes. Without id, attributes will be newly added to product model.
So, try
... options_products_attributes: [ :id, price, :stock, variant_attributes: [ :id, :tipoVariant, ...
I have a new Match form:
EDIT:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
When I choose two players I want set for each one match result like this:
id: 1, match_id: 1, player_id: 1, result: 4
id: 2, match_id: 1, player_id: 2, result: 10
I'm new in rails and I don't know how to fix that
MatchController
class MatchesController < ApplicationController
respond_to :html
def index
#matches = Match.all
end
def show
#match = Match.find(params[:id])
#results = #match.match_edits
end
def new
#match = Match.new
#players = Player.all
2.times {#match.match_edits.build}
end
def create
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render :show, status: :created, location: #match }
else
format.html { render :new }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
private
def match_params
params[:match].permit :match_date, player_ids: [], :match_edits_attributes => [:id, :result]
end
end
MatchEdit model
class MatchEdit < ActiveRecord::Base
belongs_to :match
belongs_to :player
end
Match model
class Match < ActiveRecord::Base
has_many :match_edits
has_many :players, through: :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true, reject_if: proc { |attrs| attrs['result'].blank? }
end
Schema.rb
ActiveRecord::Schema.define(version: 20150629144534) do
create_table "match_edits", force: :cascade do |t|
t.integer "match_id"
t.integer "player_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "result"
end
add_index "match_edits", ["match_id"], name: "index_match_edits_on_match_id"
add_index "match_edits", ["player_id"], name: "index_match_edits_on_player_id"
create_table "matches", force: :cascade do |t|
t.date "match_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "players", force: :cascade do |t|
t.string "firstname"
t.string "lastname"
t.string "picture"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
You need to update your form to remove redundant code i.e.:
= ff.number_field :result, in: 0..10
Your form will look like:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
Your controller's new method is responsible to to provide multiple fields for result:
class MatchsController << ApplicationContoller
...
def new
...
2.times { #match.match_edits.build }
...
end
...
end
Your model should allow to accept nested attributes as following:
class Match
...
has_many :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true,
reject_if: proc { |attrs| attrs['result'].blank? }
...
end
class MatchEdit
...
belongs_to :match
...
end
I found solution. Form should look like this:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.fields_for :match_edits do |ff|
= ff.label :player_id, 'Select player'
= ff.collection_select :player_id, #players, :id, :lastname, {}, { multiple: false }
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
and in matches_controller:
def match_params
params[:match].permit :match_date, :match_edits_attributes => [:id, :result, :player_id]
end