Paperclip Cropping rails - ruby-on-rails

I referred this episode of railscasts
http://railscasts.com/episodes/182-cropping-images
But whenever I press on the crop button
I get an error
convert.exe: invalid argument for option `-crop': 'YYYxYYY+YYY+YYY'-auto-orient
# error/convert.c/ConvertImageCommand/1124.
Y being the values depending on the selected area.
Please suggest me to rectify this bug.
This is my controller code:
def crop_photo
#artist_profile=ArtistProfile.find(params[:id])
#artist_profile.update_attributes(params[:artist_profile])
if #artist_profile.save
format.js
end
My cropping view file :
<%= stylesheet_link_tag "jquery.Jcrop" %>
<%= javascript_include_tag "jquery.Jcrop" %>
<div style="max-width: 720px;">
<%= nested_form_for #artist_profile, :url => cropped_photo_path ,:html => { :method => :post } ,:remote => true do |f| %>
<%= image_tag #artist_profile.photo.url(:large), :id => "cropbox" %>
<%= hidden_field_tag "artist_profile_id", #artist_profile.id %>
<h4>Preview:</h4>
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag #artist_profile.photo.url(:thumb), :id => "preview_image" %>
</div>
<% for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] %>
<%= f.hidden_field attribute, :id => attribute %>
<% end %>
<p><%= f.submit "Crop" %></p>
<% end %>
</div>
<script type="text/javascript" charset="utf-8">
$(function() {
$('#cropbox').Jcrop({
onChange: update_crop,
onSelect: update_crop,
setSelect: [0, 0, 500, 500],
aspectRatio: 1
});
});
function update_crop(coords) {
var rx = 100.00/coords.w;
var ry = 100.00/coords.h;
$('#preview_image').css({
width: Math.round(rx * <%= #artist_profile.photo_geometry(:large).width %>) + 'px',
height: Math.round(ry * <%= #artist_profile.photo_geometry(:large).height %>) + 'px',
marginLeft: '-' + Math.round(rx * coords.x) + 'px',
marginTop: '-' + Math.round(ry * coords.y) + 'px'
});
var ratio = <%= #artist_profile.photo_geometry(:original).width %> / <%= #artist_profile.photo_geometry(:large).width %>;
$("#crop_x").val(Math.round(coords.x * ratio));
$("#crop_y").val(Math.round(coords.y * ratio));
$("#crop_w").val(Math.round(coords.w * ratio));
$("#crop_h").val(Math.round(coords.h * ratio));
}
</script>

Looks like your system is either not sending or processing the required crop co-ordinates:
Javascript is not updating the hidden fields
JCrop is not capturing / processing the right data
Rails is not reading the params properly
JS
You may want to write your JS code like this:
var jCrop = function() {
$('#cropbox').Jcrop({
onChange: update_crop,
onSelect: update_crop,
setSelect: [0, 0, 500, 500],
aspectRatio: 1
});
};
$(document).ready(jCrop);
$(document).on("page:load", jCrop);
Your JCrop could be prevented from loading due to not loading correctly
JCrop
The other issue you may have is JCrop not capturing the right data
You need to make sure your hidden fields are actually getting populated with the data. Your update_crop code looks okay - so I would look at view_source > hidden fields
Rails
The likely problem here is probably Rails' processing of the params
You're passing your form_for var as #artist_profile
I would do this in your controller (considering you're using Rails 4):
def crop_photo
#artist_profile = ArtistProfile.find(params[:id])
#artist_profile.update_attributes(crop_params)
if #artist_profile.save
format.js
end
end
private
def crop_params
params.require(:artist_profile).permit(:crop_x, :crop_y, :crop_h, :crop_w)
end

Related

Ruby on Rails | Collection_Select [Value] -> Param

Got a (hopefully) easy one:
Ruby partial view with a collection_select, need the value of that selection (var name = 'submod') passed as a param up to controller. Literally just stuck on how to get from collection_select/onchange() to named param.
html.erb code:
<% #modName = locals[:moduleName] %>
<% #id = locals[:id] %>
<%= form_with url: admin_command_path() do |f| %>
<%= collection_select(#refcode, :Code, Command.where(FLD: #modName), :Code, :Definition, options ={prompt: true}, html_options = {:onchange => "updateSubMod(this.value)"}) %>
<br /><br />
<button class="btn_new">
<%= link_to "Execute", new_admin_command_path(mod: #modName, submod: #refcode, id: #id) %>
</button>
<% end %>
The onchange function works as expected, so feel free to make use of that to solve for param:
<script>
function updateSubMod(Code) {
var submod = Code
console.log(submod, '********************')
}
</script>

Preview multiple images right after selection with stimulus javascript (ruby on rails)

I am dealing with a rails project where I am trying to upload multiple images and have them previewed right after image selection. I am using stimulus, ruby on rails, js6
The HTML is a form used to create a new product - I am also using cloudinary - to upload multiple pictures and simpleformfor
app/views/products/_form.html.erb
<%= simple_form_for product do |m| %>
<div class="d-flex justify-content-between" data-controller="upload">
<%= m.input :photos, as: :file, input_html: { multiple: true, class: 'hidden', id: 'photo-input',
data: {action: 'change->upload#displayPreview'} },
label_html: { class: 'upload-photo'}, label: ':camera: Upload a photo' %>
<% if #product.photos.attached? %>
<% #product.photos.each do |photo| %>
<%= cl_image_tag photo.key, height: 100, width: 200, crop: :fill, data: { target: 'upload.image'} %>
<% end %>
<% end %>
</div>
<%= m.button :submit, class: 'btn-primary' %>
<% end %>
the HTML form code for 1 image is as follow
<div data-controller="upload">
<label class="file optional upload-photo" for="photo-input">Upload photo</label>
<input class="form-control-file file optional hidden" id="photo-input" data-action="change->upload#displayPreview" type="file" name="product[photos][]">
<%= cl_image_tag "", height: 100, width: 200, crop: :fill, data: { 'upload-target': 'image', 'upload-index-value': 0 } %>
</div>
</div>
The code for 1 image is as follow and working fine if the form was just for 1 image, but how would i transform the following code to accommodate for multiple image uploading?
I tried several things using for loops and this.imageTargets.forEach((element) => {.. and even indexes but at no avail..
javascript/controllers/upload_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ['image']
displayPreview(event) {
const input = event.target
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = (event) => {
this.imageTarget.src = event.currentTarget.result;
}
reader.readAsDataURL(input.files[0])
this.imageTarget.classList.remove('hidden');
}
}
}
I appreciate your comments and alternative solutions..
For file fields with multiple images, you need to use the for loop in your JS which will detect the input files length. For your reference you can update your js as like.
image_controller.js
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = ["input"]
preview() {
var input = this.inputTarget
var files = input.files
var imgLoc = document.getElementById("Img")
for (var i = 0; i < files.length; i++) {
let reader = new FileReader()
reader.onload = function() {
let image = document.createElement("img")
imgLoc.appendChild(image)
image.style.height = '100px'
image.src = reader.result
}
reader.readAsDataURL(files[i])
}
}
}
As you are displaying the output by replacing the preview.png, for this you don't need the image preview. This will create images.
_form.html.erb
<div class="mb-3" data-controller="image" id = "Img">
<%=f.label :images, class: "form-label" %>
<%= f.file_field :images, multiple: true, class: "form-control", accept: "image/png, image/jpeg, image/jpg", "data-image-target": "input", "data-action": "image#preview" %>
</div>

Using js.erb in rails

In Rails 5 app with devise, I need to use a new.js.erb file to update select tag in my registrations view and controller. I cant seem to figure out why my new.js.erb file isn't working.
I've tried to use respond_to in controller as below,
registrations-controller.rb
def new
super
#cities = CS.get(:us,params[:state])
respond_to do |format|
format.js { render '/new.js.erb' }# layout: false }
format.html
end
end
new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), :remote => true) do |f| %>
<div class="signup-input-container">
<div class="field">
<%= f.text_field :firstname, autofocus: true, autocomplete: "firstname", placeholder: "First name", class: "signup-input-container--input" %>
</div>
<div class="field">
<%= f.select :state, options_for_select(CS.states(:us).map { |code, name| [name, code] }),{:prompt => "State"}, {:class => "signup-input-container--input", :id => "state-picker"} %>
</div>
<div class="field">
<%= f.select :city, options_for_select([]),{}, {:class => "signup-input-container--input", :id => "city-picker"} %>
</div>
</div>
<% end %>
new.js.erb
var city = document.getElementById("city-picker");
while (city.firstChild) city.removeChild(city.firstChild);
var placeholder = document.createElement("option");
placeholder.text = "Choose a city";
placeholder.value = "";
city.appendChild(placeholder);
<% #cities.each do |c| %>
city.options[city.options.length] = new Option('<%= c %>');
<% end %>
main.js
var state = document.getElementById("state-picker");
state.addEventListener("change", function() {
$.ajax({
url: "/states?state=" + state.value,
type: "GET"
})
})
I'm expecting this to create select tag options with my array of cities in my controller. Does anyone know how to get this to work?
To solve this you should just setup a separate controller where you can fetch the data from asynchronously and alternatively there are also several free API's which can be used for geographical lookup such as Googles Geocoding API and Geonames.
To setup a separate controller you can do it by:
# /config/routes.rb
get '/states/:state_id/cities', to: 'cities#index'
# /app/controllers/cities_controller.rb
class CitiesController < ApplicationController
# GET
def index
#cities = CS.get(:us, params[:state_id])
respond_to do |f|
f.json { render json: #cities }
end
end
end
I would skip using a .js.erb template altogether and just return JSON data which you can use directly in your JS or with one of the many existing autocomplete solutions. .js.erb only makes sense for extensive HTML templating (like for example rendering an entire form) where you want to reuse your server side templates - it greatly increases the complexity and generally makes a mess of your javascript which is not worth it just to output a list of option tags.
// If you are using jQuery you might as well setup a delegated
// handler that works with turbolinks,
$(document).on('change', '#state-picker', function(){
$.getJSON("/states/" + $(this).value() + "/cities", function(data){
// using a fragment avoids updating the DOM for every iteration.
var $frag = $('<select>');
$.each(data, function(city){
$frag.append$('<option>' + data + '</option>');
});
$('#city-picker').empty()
.append($('frag').children('option'));
});
});

How to display a selection based on user input using ajax and jquery

Think of the below as a bike rental. Someone fills out a form and gets a bike assigned to them which they can rent and borrow for a certain amount of time.
The problem I am having is I am trying to show the person who wants to rent the bikes what bikes are available before they submit the form. Below is my attempt using ajax. I have no errors but also my select is not updating.
request controller methods below
def new
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
#new_request = Request.new
end
create method below (with a temporary workaround, that reloads the form with a warning about availability.)
def create
#request = Request.new(request_params)
available_bikes = #request.new_request(current_user.id)
if (available_bikes >= #request.number_of_bikes_wanted) && #request.save
redirect_to root_path
else
flash[:warning] = "You have requested more bikes than available. There are only #{available_bikes} bikes available"
redirect_to new_request_url
end
end
params in request controller
def request_params
params.require(:request).permit(:Borrow_time, :Borrow_date,
:Return_date, :Return_time,
:number_of_bikes_wanted, bike_ids: [])
end
new.html.erb view
<div class="form" align = "center">
<%= render 'form.js.erb' %>
</div>
_form.js.erb below
<script type="text/javascript">
$(document).ready(function() {
$('.my-date').on('change', function() {
var data = {}
$('.my-date').each(function() {
if($(this).val()) {
data[$(this).attr("id")] = $(this).val();
}
});
if(Object.keys(data).length > 1) {
$.ajax({
type: "POST",
url: <%= new_request_path %>,
data: data
});
}
});
});
var options = "";
<% #bikes.each do |bike| %>
options += "<option value='<%= bike.id %>'><%= bike.name %></option>"
<% end %>
$('#request_number_of_bikes_wanted').html(options);
</script>
<div class="block-it" align=center>
<br>
<%= form_for #new_request do |request| %>
<%= request.label :Borrow_date, 'Borrow on' %>
<%= request.date_field :Borrow_date, id: 'Borrow_date', class: 'my-date', min: Date.today, :required => true %>
<%= request.label :Borrow_time, 'Borrow at' %>
<%= request.time_field :Borrow_time, value: '10:00', min: '9:00 AM', max: '4:30 PM', default: '10:00 AM', :ignore_date => true, :required => true %>
<br><br>
<%= request.label :Return_date, 'Return On' %>
<%= request.date_field :Return_date, id: 'Return_date', class: 'my-date', min: Date.today, :required => true %>
<%= request.label :Return_time, 'Return at' %>
<%= request.time_field :Return_time, value: '10:00', min: '9:00 AM', max: '4:30 PM', default: '10:00 AM', :ignore_date => true, :required => true %>
<br><br>
<br><br>
<%= request.label :NumberOfBikesWanted, 'Number of bikes' %>
<%= request.select :number_of_bikes_wanted, %w(select_bike), :required => true %>
<br>
<%= request.submit 'Submit' %>
<%= request.submit 'Reset', :type => 'reset' %>
<% end %>
<br>
</div>
There are a two main problems with your code:
Controller
Use a different action to set the endpoint that you will call with ajax, so instead of this:
def new
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
#new_request = Request.new
end
Try this:
def bikes
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
def new
#new_request = Request.new
end
If you want to keep REST routes, then create a new controller and use the index action within that controller.
Form
This code:
var options = "";
<% #bikes.each do |bike| %>
options += "<option value='<%= bike.id %>'><%= bike.name %></option>"
<% end %>
$('#request_number_of_bikes_wanted').html(options);
doesn't belong here, it must be deleted from your file and instead put it on a new file called bikes.js.erb; also rename your form to _form.html.erb.
And update your ajax call to use your new route:
$.ajax({
type: "POST",
url: <%= bikes_path %>,
data: data
});
What you want to setup is a new endpoint but instead of returning html, it will return a js. But you must treat it as an independent action, just as any other action in rails. The only difference is how you call that action (ajax) and how you respond to it (js).

Add character counter in simple_form in Ruby on Rails

I use the simple_form gem and I want a simple character counter on a text field. I was told, this might work:
add this to the form:
<%= f.input :body, id: "body-field" %>
<span id="body-count">0 characters</span>
and javascript:
$("#body-field").on("keyup", function(){
length = $(this).val().length;
$("#body-count").html(length);
});
I got this information from here (Attention: It is full of advertisement): http://www.sohua.xyz/questions-full/4320915/how-do-i-implement-a-basic-character-counter-in-a-simple-form
I did this, but nothing happens. Here is my actual code chapters/new.html.erb:
<%= simple_form_for([#book, #book.chapters.build]) do |f| %>
<%= f.input :chaptertitle %>
Mininmum amount of characters: <%= #book.min_length %> Maximum amount of characters: <%= #book.max_length %>
<%= f.input :chaptercontent, id: "body-field" %>
<span id="body-count">0 characters</span>
<%= f.input :author %>
<%= f.button :submit %>
<% end %>
<script>
$("#body-field").on("keyup", function(){
length = $(this).val().length;
$("#body-count").html(length);
});
</script>
Can you give me any advice, how to get it work?
You need to wrap your code in jquery document ready function:
$(function() {
$("#body-field").on("keyup", function(){
var length = $(this).val().length;
$("#body-count").html(length);
});
});
Why don't you use an existing library instead?
https://github.com/dtisgodsson/jquery-character-counter
You might want to use either js or coffee-script, I am providing a coffee script example below:
Add this piece of code to your chapters.coffee file:
ready = ->
totalChars = 100
#Total characters allowed
countTextBox = $('#body-field')
# Remaining chars count will be displayed here
charsCountEl = $('#countchars')
#initial value of countchars element
charsCountEl.text totalChars
#user releases a key on the keyboard
countTextBox.keyup ->
#get chars count in Text field
thisChars = #value.replace(/{.*}/g, '').length
if thisChars > totalChars
# total extra chars to delete
CharsToDel = thisChars - totalChars
#remove excess chars from text field
#value = #value.substring(0, #value.length - CharsToDel)
else
#count remaining chars
charsCountEl.text totalChars - thisChars
return
return
$(document).ready ready
$(document).on 'page:load', ready
# Loads javascript while loading page
Add this line to your form right below to the Text input field.
var ready;
var charsCountEl, countTextBox, totalChars;
totalChars = 100;
countTextBox = $('#body-field');
charsCountEl = $('#countchars');
charsCountEl.text(totalChars);
countTextBox.keyup(function() {
var CharsToDel, thisChars;
thisChars = this.value.replace(/{.*}/g, '').length;
if (thisChars > totalChars) {
CharsToDel = thisChars - totalChars;
this.value = this.value.substring(0, this.value.length - CharsToDel);
} else {
charsCountEl.text(totalChars - thisChars);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="text" id="body-field" />
<span id="countchars"></span>
If your file under javascript/ folder doesn't have extestion .coffee the please rename it to chapters.coffee if it does then thats it.
PS: here is the javascript version of the same http://pastebin.com/LZb1DAC4.
Reference: https://stackoverflow.com/a/24629105/2545197
This solution won't be as good as Abhinay's solution, but it works for me:
(Please note, I'm an amateur, this code may be horrendous)
Javascript code:
<script>
$(document).ready(function(){
var choosingbar = function( event ){
$(event.target).parents(".counting").children(".count").text($(event.target).val().length);
};
$(".counting textarea").keyup(choosingbar);
});
</script>
new.html.erb code:
<%= simple_form_for([#book, #book.chapters.build]) do |f| %>
<%= f.input :chaptertitle %>
Mininmum amount of characters: <%= #book.min_length %> Maximum amount of characters: <%= #book.max_length %>
<div class="counting" ><%= f.input :chaptercontent %>Characters: <span class="count"></span></div><br>
<%= f.input :author %>
<%= f.button :submit %>
<% end %>

Resources