Rails Partial Image Rendering - ruby-on-rails

I'm getting up to speed on rails and ran into an odd problem. I'm rendering some images from the database (Image models attached to another model, Plants). I'm having some trouble when attempting to do it via a partial. I've got show.html.erb
<fieldset class="fieldset">
<legend>Images</legend>
<%= unless #plant.images.blank? then
for image in #plant.images
debugger
render :partial => 'show_image', :object => image
end
else
"This plant has no images to display."
end
%>
</fieldset>
And the partial _show_image.html.erb:
<div class="image_container">
<% debugger %>
<img src="<%= url_for(:action => 'picture', :id => object.id) %>"/>
<p class='image_caption'><%= object.comment %></p>
</div>
When this is rendered it just renders a "#" for each image, rather than the actual image. It seems to be just rendering the object as a string, as in the source I get:
<fieldset class="fieldset">
<legend>Images</legend>
#<Image:0x242c910>
</fieldset>
When running through the debugger locally:
/Users/*****/dev/plantmanager/app/views/plants/show.html.erb:54
debugger
(rdb:241) image
#<Image id: 40, comment: "Test", name: "Ixia.gif", content_type: "image/gif", data: "GIF89a2\0002\000####$\205\233\tI\250\"x\224\b?\206\031d|ju####\v\031###\247\bI\257G\222\232\222\227\263\262...", plant_id: 55, thumbnail: nil>
(rdb:221) #plant
#<Plant id: 55, name: "Test", description: "Test", created_at: "2009-05-07 07:19:44", updated_at: "2009-05-07 07:19:44", planted: "2009-05-07 00:00:00", sprouted: "2009-05-15 00:00:00", plant_type_id: 1, finished: "2009-05-27 00:00:00">
(rdb:221) #plant.images
[#<Image id: 40, comment: "Test", name: "Ixia.gif", content_type: "image/gif", data: "GIF89a2\0002\000####$\205\233\tI\250\"x\224\b?\206\031d|ju####\v\031###\247\bI\257G\222\232\222\227\263\262...", plant_id: 55, thumbnail: nil>]
(rdb:221) continue
/Users/*****/dev/plantmanager/app/views/plants/_show_image.html.erb:2
<% debugger %>
(rdb:221) object
#<Image id: 40, comment: "Test", name: "Ixia.gif", content_type: "image/gif", data: "GIF89a2\0002\000####$\205\233\tI\250\"x\224\b?\206\031d|ju####\v\031###\247\bI\257G\222\232\222\227\263\262...", plant_id: 55, thumbnail: nil>
(rdb:221) object.id
40
(rdb:221) object.comment
"Test"
(rdb:221) continue
Here are my models [snipped a bit]:
class Plant < ActiveRecord::Base
has_many :images
validates_presence_of :name
validates_presence_of :plant_type_id
validates_associated :images
after_update :save_images
def image_attributes=(image_attributes)
image_attributes.each do |attributes|
# TODO: Probably handle validation in the image model?
if attributes[:id].blank?
unless attributes[:uploaded_picture].blank?
tmpImg = images.build()
tmpImg.uploaded_picture=attributes[:uploaded_picture]
tmpImg.comment = attributes[:comment]
end
else
img = images.detect { |i| i.id == attributes[:id].to_i }
img.attributes = attributes
end
end
end
def save_images
images.each do |i|
if i.should_destroy?
i.destroy
else
i.save(false)
end
end
end
end
class Image < ActiveRecord::Base
validates_format_of :content_type,
:with => /^image/,
:message => "--- you can only upload pictures"
attr_accessor :should_destroy
def should_destroy?
should_destroy.to_i == 1
end
def uploaded_picture=(picture_field)
self.name = base_part_of(picture_field.original_filename)
self.content_type = picture_field.content_type.chomp
self.data = picture_field.read
#image = MiniMagick::Image.from_blob self.data
#self.thumbnail = resize_and_crop(image, 100).to_blob
end
def base_part_of(file_name)
File.basename(file_name).gsub(/[^\w._-]/, '')
end
end

Try this instead:
<% unless #plant.images.blank?
for image in #plant.images
%>
<%= render :partial => 'show_image', :object => image %>
<% end
else %>
This plant has no images to display.
<% end %>
Sorry about the formatting, but you get the idea.

Build a method for the pic in your Image model
class Image < ActiveRecord::Base
belongs_to :plant
validates_presence_of :uploaded_picture
...
end
In your plants controller
def plant_pic
img = Image.find(params[:id])
# THE NEXT LINE IS THE ONE I THINK YOU ARE MISSING:
send_data img.uploaded_picture, :filename =>"plant#{img.plant_id}_#{img.id.to_s}+'.jpg',
:type => 'image/jpeg',
:disposition => 'inline'
end
...
And then in your plants view:
<fieldset class="fieldset">
<legend>Images</legend>
<% if plant.images.blank? -%>
<p>This plant has no images to display.</p>
<% else %>
<%= #plant.images.each do |image| %>
<div class="img_container">
<img src="<%= url_for(:action => 'plant_pic', :id => image.id) %>"/>
</div>
<% end -%>
<% end -%>
</fieldset>
You may want to encode/decode the uploaded_picture binary field someway (I use Base64.encode and Base64.decode) in your images controller, but that's another issue.
Hope that helps you

Related

ActionView::Template::Error (undefined method `image_url' for #<ActionView::Helpers::FormBuilder:0x00007f921c2ac9b0>)

I'm using the shrine gem in form object,
I want to clip images, so, I followed https://github.com/shrinerb/shrine/wiki/Image-Cropping,
but, happed above error,
I think that it seems to be don't recognize photos models
views
<%= form_with model: #blog_form , url: user_blogs_path ,local: true do |f| %>
<div class="field">
<% f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<% f.label :content %>
<%= f.text_area :content %>
</div>
<div class="field">
<% f.label :user_id %>
<%= f.hidden_field :user_id, value: current_user.id %>
</div>
<div class ="field">
<%= f.fields_for :photos, #blog_form do |photos_fileds| %>
<%= photos_fileds.label :image %>
<%= photos_fileds.hidden_field :image, value: photos_fileds.cached_image_data if defined?
(photos_filed.cached_image_data) %>
%= photos_fileds.file_field :image %><br/>
<div>
<img id="image" src="<%= photos_fileds.image_url %>"
</div>
<% end %>
</div>
image_uploader
require "vips"
require "image_processing/vips"
class ImageUploader < Shrine
plugin :derivatives
THUMBNAILS = {
large: [800, 800],
medium: [600, 600],
small: [300, 300],
}
Attacher.derivatives do |original|
vips = ImageProcessing::Vips.source(original)
vips = vips.crop(*file.crop_points) # apply cropping
THUMBNAILS.transform_values do |(width, height)|
vips.resize_to_limit!(width, height)
end
end
plugin :derivation_endpoint, prefix: "derivations/image"
# Default URLs of missing derivatives to on-the-fly processing.
Attacher.default_url do |derivative: nil, **|
next unless derivative && file
file.derivation_url :transform, shrine_class.urlsafe_serialize(
crop: file.crop_points,
resize_to_limit: THUMBNAILS.fetch(derivative),
)
end
# Generic derivation that applies a given sequence of transformations.
derivation :transform do |file, transformations|
transformations = shrine_class.urlsafe_deserialize(transformations)
vips = ImageProcessing::Vips.source(file)
vips.apply!(transformations)
end
end
class UploadedFile
# convenience method for fetching crop points from metadata
def crop_points
metadata.fetch("crop").fetch_values("x", "y", "width", "height")
end
end
shrine.rb
require "shrine"
require "shrine/storage/file_system"
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new("app/assets/images", prefix: "uploads/cache"), # temporary
store: Shrine::Storage::FileSystem.new("app/assets/images", prefix: "uploads"), # permanent
}
Shrine.plugin :activerecord # loads Active Record integration
Shrine.plugin :cached_attachment_data # enables retaining cached file across form redisplays
Shrine.plugin :restore_cached_data # extracts metadata for assigned cached files
Shrine.plugin :determine_mime_type
Shrine.plugin :derivatives
Shrine.plugin :backgrounding
Shrine::Attacher.promote_block { PromoteJob.perform_later(record, name, file_data) }
Shrine::Attacher.destroy_block { DestroyJob.perform_later(data) }
Shrine.plugin :derivation_endpoint, secret_key: Rails.application.secret_key_base
Shrine.plugin :default_url
cropbox.js
import 'cropperjs/dist/cropper.css'
import Cropper from 'cropperjs'
function cropbox(image, url, { onCrop }) {
image.src = url
new Cropper(image, {
aspectRatio: 1,
viewMode: 1,
guides: false,
autoCropArea: 1.0,
background: false,
zoomable: false,
crop: event => onCrop(event.detail)
})
}
export default cropbox
fileUpload.js
import cropbox from 'cropbox'
// ...
uppy.on('upload-success', (file, response) => {
// retrieve uploaded file data
const uploadedFileData = response.body['data']
// set hidden field value to the uploaded file data so that it's submitted
// with the form as the attachment
hiddenInput.value = JSON.stringify(uploadedFileData)
cropbox(imagePreview, response.uploadURL, {
onCrop(detail) {
let fileData = JSON.parse(hiddenInput.value)
fileData['metadata']['crop'] = detail
hiddenInput.value = JSON.stringify(fileData)
}
})
})
promote_job.rb
class PromoteJob < ApplicationJob
def perform(record, name, file_data)
attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)
attacher.create_derivatives
attacher.atomic_promote
end
end
destroy_job.rb
class DestroyJob < ApplicationJob
def perform(data)
attacher = Shrine::Attacher.from_data(data)
attacher.destroy
end
end
route.rb
Rails.application.routes.draw do
mount ImageUploader.derivation_endpoint => "/derivations/image"
get 'sessions/new'
devise_for :users
resources :users do
resources :blogs
end
# For details on the DSL available within this file, see
https://guides.rubyonrails.org/routing.html
end
blog_form.rb
class BlogForm
include ActiveModel::Model
attr_accessor :title, :content, :user_id , :photos, :tag_list
def blog_builder
#user = User.find(user_id)
#blogs = #user.blogs.create(title: title , content: content )
end
concerning :PhotosBuilder do
attr_reader :photos_attributes
def photos
#photos ||= Photo.new
end
def photos_attributes=(attributes)
debugger
#photos ||= Photo.new(attributes)
end
end
def build_association
#blogs.photos << #photos if photos?
#user.photos << #photos if photos
end
def save
return false if invalid?
blog_builder
#blogs.save
#photos.save if #photos != nil
build_association
end
def photos?
return true if #photos != nil
end
end
controller
def new
#user = params[:user_id]
#blog_form = BlogForm.new
end
def create
debugger
#blog_form = BlogForm.new(add_params) if has_a_tags? == true
#blog_form = BlogForm.new(blog_form_params)
if #blog_form.save
redirect_to user_blogs_path
else
#False action
end
end
I appreciate you, janko-m, arieljuod
I rewrite views, then happened new error
before
ActionView::Template::Error (undefined method `image_url' for #
<ActionView::Helpers::FormBuilder:0x00007f9228150380>):
<%= photos_fileds.hidden_field :image, value: photos.cached_image_data if
defined?(photos.cached_image_data) %>
<%= photos_fileds.file_field :image %><br/>
<div>
<img id="image" src="<%= photos_fileds.image_url %>"
</div>
<% end %>
after
ActionView::Template::Error (undefined method `image_url' for #
<BlogForm:0x00007f9228090a58>):
<%= photos_fileds.hidden_field :image, value:
photos_fileds.cached_image_data if defined?
(photos_filed.cached_image_data) %>
<%= photos_fileds.file_field :image %><br/>
<div>
<img id="image" src="<%= photos_fileds.object.image_url %>" />
</div>
<% end %>
environment
rails 6.0.2 ruby 2.5.3
This line is trying to call #image_url on the form object:
<img id="image" src="<%= photos_fileds.image_url %>" />
Instead it needs to be calling #image_url on the ActiveRecord model:
<img id="image" src="<%= photos_fileds.object.image_url %>" />

Rails 5.2 ActiveStorage upload picture and how to crop the picture by user

Once I used carrierwave and Jcrop to upload and crop user picture , it works well . User can free clipping after then upload the picture.But now I want to chang the upload method from carrierwave to ActiveStorage. At this time I also want to free clipping picture. Then how could I chang my codes? Thanks so much!!
My part of gems :
gem 'rails', '~> 5.2'
gem 'mini_magick'
# gem 'carrierwave', '~> 1.2', '>= 1.2.2'**(No use in ActiveStorage)**
Part of my user models :
# user.rb
has_one_attached :picture
has_one_attached :avatar
attr_accessor :remember_token, :activation_token, :crop_x, :crop_y, :crop_w, :crop_h
after_update :crop_picture
def crop_picture
picture.recreate_versions! if crop_x.present?
end
# mount_uploader :picture, PictureUploader**(No use in ActiveStorage)**
# mount_uploader :avatar, PictureUploader**(No use in ActiveStorage)**
Part of my user_controllers:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Almost success"
redirect_to confirmation_path(#user.name)
else
flash.now[:danger] = 'Failure'
render :new
end
end
def update
#user = User.friendly.find(params[:id])
if #user.update_attributes(user_params)
if params[:user][:picture].present?
render :crop
else
redirect_to #user
flash[:success] = "Update success"
end
else
flash[:warning] = "Update failue"
render :edit
end
end
private
def user_params
params.require(:user).permit(... :avatar, :picture)
end
My crop.html.erb:
<div class="container">
<div class="col-md-12 pbt20 bgCo">
<div class="col-md-8">
<h4 class="pageItemTitle mbt20">Crop Picture</h4>
<%#= image_tag #user.picture.url(:large), :id => "user_preview" %>
<%= image_tag(#user.picture.variant(combine_options: { gravity: 'Center', crop: '600x800+0+0' }),width: 600,id: "cropImage") %>
</div>
<div class="col-md-4">
<h4 class="pageItemTitle mbt20">Crop Preview</h4>
<div style="width:160px; height:160px; overflow:hidden; border-radius: 50%;">
<%= image_tag #user.picture, :id => "user_preview" %>
</div>
<div class="crop-footer pull-right mbt20 pbt20">
<%= form_for #user do |f| %>
<% %w[x y w h].each do |attribute| %>
<%= f.hidden_field "crop_#{attribute}" %>
<% end %>
<%= link_to "Close", #user, class: "btn btn-default mr10" %>
<%= f.submit "Crop", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
</div>
My crop.js.coffee:
jQuery ->
new PictureCropper()
class PictureCropper
constructor: ->
$('#cropImage').Jcrop
aspectRatio: 1
setSelect: [0, 0, 400, 400]
onSelect: #update
onChange: #update
update: (coords) =>
$('#user_crop_x').val(coords.x)
$('#user_crop_y').val(coords.y)
$('#user_crop_w').val(coords.w)
$('#user_crop_h').val(coords.h)
#updatePreview(coords)
updatePreview: (coords) =>
$('#user_preview').css
width: Math.round(160/coords.w * $('#cropImage').width()) + 'px'
height: Math.round(160/coords.h * $('#cropImage').height()) + 'px'
marginLeft: '-' + Math.round(160/coords.w * coords.x) + 'px'
marginTop: '-' + Math.round(160/coords.h * coords.y) + 'px'
My picture.uploader:(No use in ActiveStorage)
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploaders/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :large do
process :crop
resize_to_limit(600, nil)
end
def crop
if model.crop_x.present?
resize_to_limit(600, nil)
manipulate! do |img|
x = model.crop_x.to_i
y = model.crop_y.to_i
w = model.crop_w.to_i
h = model.crop_h.to_i
img.crop([[w, h].join('x'),[x, y].join('+')].join('+'))
end
end
end
def extension_white_list
%w(jpg jpeg gif png)
end
def filename
s = "#{model.id}"
t = s.to_i * 365
t.to_s + ".jpg" if original_filename
end
end
If I chang the upload method from carrierwave to ActiveStorage and at this time I also want to free clipping picture by user. Then how could I chang my codes? Thanks so much!!
I am storing crop coordinates as a json field named crop_settings in PostgreSQL.
Below is the working code to generate thumbnail variant and cropped image variant.
# Event.rb
class Event < ApplicationRecord
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
has_one_attached :cover_image
before_save :check_cropping
def check_cropping
self.crop_settings = {x: crop_x, y: crop_y, w: crop_w, h: crop_h} if cropping?
end
def cropping?
!crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
end
def cropped_image
if cover_image.attached?
if crop_settings.is_a? Hash
dimensions = "#{crop_settings['w']}x#{crop_settings['h']}"
coord = "#{crop_settings['x']}+#{crop_settings['y']}"
cover_image.variant(
crop: "#{dimensions}+#{coord}"
).processed
else
cover_image
end
end
end
def thumbnail(size = '100x100')
if cover_image.attached?
if crop_settings.is_a? Hash
dimensions = "#{crop_settings['w']}x#{crop_settings['h']}"
coord = "#{crop_settings['x']}+#{crop_settings['y']}"
cover_image.variant(
crop: "#{dimensions}+#{coord}",
resize: size
).processed
else
cover_image.variant(resize: size).processed
end
end
end
end
views/events/show.html.erb
...
<div>
Original Image:
<%= image_tag(#event.cover_image) if #event.cover_image.attached? %>
</div>
<div>
Cropped Image:
<%= image_tag(#event.cropped_image) if #event.cropped_image %>
</div>
<div>
Thumbnail:
<%= image_tag(#event.thumbnail) if #event.thumbnail %>
</div>
...
If the variant was previously created and stored, then it will be used instead of recreating.

Prawn PDF and muliplte records

I'm looking to extract all records to a single pdf using Prawn PDF and rails 4.2.
Currently I have them generating by id for individual pdf's and works well.
Show
def show
#agreement = Agreement.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = AgreementPdf.new(#agreement)
send_data pdf.render, filename: "Agreement - #{#agreement.entity}", type: "application/pdf", disposition: "inline"
end
end
end
Index in table
<% #agreements.each do |agreement| %>
<tr>
<td><%= agreement.entity %></td>
<td><%= link_to 'Download', agreement_path(agreement.id, format: 'pdf'), :target => "_blank" %></td>
</tr>
<% end %>
First, you have to add a route with the path of the new method:
get '/agreements/all_records' => 'agreements#all_records', :as => :agreement_all_records
Then call the methods with in the view:
<td><%= link_to 'Download All Records', agreement_all_records_path(), :target => "_blank" %></td>
It will look something like this in the controllers:
def all_records
#agreements = Agreement.all
pdf = AgreementsPdf.new(#agreements)
send_data pdf.render,filename:'agreements.pdf',type:'application/pdf', disposition: 'inline'
end
And the Report may look like this ( assuming that agreement model has id,name fields ):
require 'prawn/table'
class AgreementsPdf < PdfReport
TABLE_WIDTHS = [100, 100]
PAGE_MARGIN = [40, 40, 40, 40]
def initialize(agreements=[])
super(:page_size => "LEGAL", margin: PAGE_MARGIN, :page_layout => :landscape)
#agreements = agreements
display_table
end
private
def display_table
if table_data.empty?
text "None data"
else
table(table_data,column_widths: TABLE_WIDTHS, :cell_style => { size: 10 } )
end
end
def table_data
#table_data ||= #agreements.map { |e| [e.id, e.name] }
end
end
Hope this will give you an idea

Rails4 advice: How should I create multiple records in a model from single view?

Been struggling with this query for a few days. I have 3 models Books, Children and Hires. I have created a view for hires which allows a user to select 2 books and a single child and what i'm looking to do is insert two rows to reflect this into the 'hires' table. I have some JS that populates the hidden fields with the values that they require. Now, I don't think nested attributes is the way to go, because i'm trying to insert directly into the joining table.
So, what i'm trying now is the following:
hires/_form.html.erb
<%= form_for(#hire) do |f| %>
<% 2.times do %>
<%= f.hidden_field :child_id %>
<%= f.hidden_field :book_id %>
<% end %>
<%= f.submit 'Take me home' %>
<% end %>
and then what I want to do is to run through the 'create' function twice in my controller and thus create two rows in the 'hires' model. Something like this:
hires_controller.rb
def create
hire_params.each do |hire_params|
#hire = Hire.new(hire_params)
end
end
Is this completely the wrong way to go? I'm looking for advice on the right way to do this? If this will work, what's the best way to format the create statement?
** Edit **
I have 3 models. Each Child can have 2 books. These are my associations:
class Child < ActiveRecord::Base
has_many :hires
has_many :books, through: :hires
end
class Hire < ActiveRecord::Base
belongs_to :book
belongs_to :child
accepts_nested_attributes_for :book
accepts_nested_attributes_for :child
end
class Book < ActiveRecord::Base
has_many :hires
has_many :children, through: :hires
belongs_to :genres
end
hires/new.html.erb
<div class="form-inline">
<div class="form-group">
<h1><label for="genre_genre_id">Pick Book 1:
<%=collection_select(:genre1, :genre_id, #genres.all, :id, :Name, {prompt: true}, {:class => "form-control dropdown"})%></label></h1>
</div>
</div>
<div id="book1-carousel" class="owl-carousel owl-theme">
<% #books.each do |book| %>
<div data-id = "<%= book.id %>" class="tapbook1 tiles <% #genres.each do |g|%> <% if g.id == book.Genre_id %> book1genre<%=g.id %> <% end end%> <%= %>"><a class="item link"><% if book.bookimg.exists? %><%= image_tag book.bookimg.url(:small), :class => "lazyOwl", :data => { :src => book.bookimg.url(:small)}%> <%end%></br><p class="tile_title" ><%= book.Title %></p></a></div>
<% end %>
</div>
<div class="form-inline">
<div class="form-group">
<h1><label for="genre_genre_id">Pick Book 2:
<%=collection_select(:genre2, :genre_id, #genres.all, :Name, :Name, {prompt: true}, {:class => "form-control dropdown"})%></label></h1>
</div>
</div>
<div id="book2-carousel" class="owl-carousel owl-theme">
<% #books.each do |book| %>
<div data-id = "<%= book.id %>" id="<%= book.id %>" class="tapbook2 tiles <% #genres.each do |g|%> <% if g.id == book.Genre_id %> book2genre<%=g.id %> <% end end%> <%= %>"><a class="item link"><% if book.bookimg.exists? %><%= image_tag book.bookimg.url(:small) , :class => "lazyOwl", :data => { :src => book.bookimg.url(:small)}%> <%end%></br> <p class="tile_title"><%= book.Title %></p></a></div>
<% end %>
</div>
<h1 class="child_heading1" >Now choose your name:</h1>
<div id="children-carousel" class="owl-carousel owl-theme">
<% #children.each do |child| %>
<div data-id = "<%= child.id %>" class="tapchild tiles"><a class="item link"><% if child.childimg.exists? %><%= image_tag child.childimg.url(:small), :class => "lazyOwl", :data => { :src => child.childimg.url(:small)} %> <%end%></br> <p class="tile_title"><%= child.nickname %></p></a></div>
<% end %>
</div>
<%= render 'form' %>
and the coffeescript:
hires.coffee
$(document).on 'ready page:load', ->
book1carousel = $("#book1-carousel")
book2carousel = $('#book2-carousel')
book1carousel.owlCarousel items: 5, lazyLoad : true
book2carousel .owlCarousel items: 5, lazyLoad : true
$('#children-carousel').owlCarousel items: 5, lazyLoad : true
book1clickcounter = 0
book2clickcounter = 0
childclickcounter = 0
book1selection = 0
book2selection = 0
$('.tapbook1').on 'click', (event) ->
$this = $(this)
book1id = $this.data('id')
book1selection = book1id
if $this.hasClass('bookclicked')
$this.removeAttr('style').removeClass 'bookclicked'
book1clickcounter = 0
$('#hire_book_id').val("");
book1selection = 0
else if book1clickcounter == 1
alert 'Choose one book from this row'
else if book1selection == book2selection
alert "You've already picked this book"
else
$('#hire_book_id').val(book1id);
$this.css('border-color', 'blue').addClass 'bookclicked'
book1clickcounter = 1
return
$('.tapbook2').on 'click', (event) ->
$this = $(this)
book2id = $this.data('id')
book2selection = book2id
if $this.hasClass('book2clicked')
$this.removeAttr('style').removeClass 'book2clicked'
book2clickcounter = 0
book1selection = 0
else if book2clickcounter == 1
alert 'Choose one book from this row'
else if book1selection == book2selection
alert "You've already picked this book"
else
$this.css('border-color', 'blue').addClass 'book2clicked'
book2clickcounter = 1
return
$('.tapchild').on 'click', (event) ->
$this = $(this)
childid = $this.data('id')
if $this.hasClass('childclicked')
$this.removeAttr('style').removeClass 'childclicked'
childclickcounter = 0
$('#hire_child_id').val("");
else if childclickcounter == 1
alert 'Choose one child from this row'
else
$this.css('border-color', 'blue').addClass 'childclicked'
childclickcounter = 1
$('#hire_child_id').val(childid);
return
jQuery ($) ->
$('td[data-link]').click ->
window.location = #dataset.link
return
return
return
My approach to this would be what's called a form object, a class that acts like a model but exists only to handle the creation of multiple objects. It provides granular control, but at the expense of duplicating validations. In my opinion (and that of many others), it's a much better option than nested attributes in most cases.
Here's an example. Note that I don't have any idea what your application does, and I didn't look at your associations close enough to make them accurate in this example. Hopefully you'll get the general idea:
class HireWithBookAndChild
include ActiveModel::Model
attr_accessor :child_1_id, :child_2_id, :book_id
validates :child_1_id, presence: true
validates :child_2_id, presence: true
validates :book_id, presence: true
def save
if valid?
#hire = Hire.new(hire_params)
#child_1 = #hire.child.create(id: params[:child_1_id])
#child_2 = #hire.child.create(id: params[:child_2_id])
#book = #hire.book.create(id: params[:book_id])
end
end
end
By including AR::Model, you get validations and an object you can create a form with. You can even go into your i18n file and configure the validation errors messages for this object. Like an ActiveRecord model, the save method is automatically wrapped in a transaction so you won't have orphaned objects if one of them fails to persist.
Your controller will look like this:
class HireWithBookAndChildController < ApplicationController
def new
#hire = HireWithBookAndChild.new
end
def create
#hire = HireWithBookAndChild.new(form_params)
if #hire.save
flash['success'] = "Yay"
redirect_to somewhere
else
render 'new'
end
end
private
def form_params
params.require(:hire_with_book_and_child).permit(:child_1_id, :child_2_id, :book_id)
end
end
Your form will look like this:
form_for #hire do |f|
f.hidden_field :child_1_id
...
f.submit
end
You'll notice right away that everything is flat, and you aren't having to mess with fields_for and nested nested parameters like this:
params[:child][:id]
You'll find that form objects make your code much easier to understand. If you have different combinations of children, books and hires that you need to create, just make a custom form object for each one.
Update
A solution that might be more simple in this case is to extract a service object:
class TwoHiresWithChildAndBook < Struct.new(:hire_params)
def generate
2.times do
Hire.create!(hire_params)
end
end
end
And from your controller:
class HiresController
def create
generator = HireWitHChildAndBook.new(hire_params)
if generator.generate
*some behavior*
else
render :new
end
end
end
This encapulates the knowledge of how to create a hire in one place. There's more detail in my answer here: Rails 4 Create Associated Object on Save

Parameter in AJAX request

I have a view which contain multiple links:
<% a.each do |q| %>
<%= link_to "stock it",
{ :action => "stock",
:qid => q.question_id,
:qur => q.question_answers_url,
:qti => q.title } ,
:remote => true %>
<div id="<%= "stock" + q.question_id.to_s %>"></div>
<% end %>
Each link generate AJAX-request. Here is a controller:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
respond_to do |format|
format.js { render :layout=>false }
end
end
Now return to the view. Each link has a 'div' with unique id='stock'. When user press the link I need to add text to specific div with corresponding id.
I have a stock.js.erb file:
$("#stock<number>").html("some text");
How can I pass div-id to stock.js.erb and how can I use it ?
Common use is to add object.id to your DOM id. That what you exactly did:
<div id="<%= "stock_#{q.question_id}" %>"></div>
Then in your controller you shoud define your question_id or your exact question:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
#question_id = params[:qid]
end
Now it will be shared with your stock.js.erb file:
$("#stock_<%= #question_id %>").html("some text");

Resources