I am trying to save the content of a html5 canvas to my Rails app. I found the
var url = canvas.toDataURL('image/png');
but I cant figure out how get that into my post/form.
Im using carrierwave to handle images but instead of uploading an image I want the user to draw one on the canvas.
I was thinking of something like setting the input file of the form to this content but that does not seem to be the way it works
Or maybe I should not be using the carrier wave?
thanks
simply place the content of url in an hidden field.
<input type="hidden" name="canvascontent" id="canvascontent" />
in javascript (with jquery):
var url = canvas.toDataURL('image/png');
$("#canvascontent").val(url);
The accepted answer didn't cover the carrierwave portion so here it is as I have it working in my app:
In my profile_images_contoller.rb I added the following functions:
# Convert base64 to binary
def split_base64(uri_str)
if uri_str.match(%r{^data:(.*?);(.*?),(.*)$})
uri = Hash.new
uri[:type] = $1 # "image/gif"
uri[:encoder] = $2 # "base64"
uri[:data] = $3 # data string
uri[:extension] = $1.split('/')[1] # "gif"
return uri
else
return nil
end
end
# Convert data uri to uploaded file. Expects object hash, eg: params[:profile_image]
def convert_data_uri_to_upload(obj_hash)
if obj_hash[:remote_image_url].try(:match, %r{^data:(.*?);(.*?),(.*)$})
image_data = split_base64(obj_hash[:remote_image_url])
image_data_string = image_data[:data]
image_data_binary = Base64.decode64(image_data_string)
temp_img_file = Tempfile.new("data_uri-upload")
temp_img_file.binmode
temp_img_file << image_data_binary
temp_img_file.rewind
img_params = {:filename => "data-uri-img.#{image_data[:extension]}", :type => image_data[:type], :tempfile => temp_img_file}
uploaded_file = ActionDispatch::Http::UploadedFile.new(img_params)
obj_hash[:image] = uploaded_file
obj_hash.delete(:remote_image_url)
end
return obj_hash
end
Then in my "create" action I passed convert_data_uri_to_upload(params[:profile_image]) to ProfileImage.new()
def create
#profile_image = ProfileImage.new(convert_data_uri_to_upload(params[:profile_image]))
#profile_image.imageable = #imageable
respond_to do |format|
if #profile_image.save
format.html { redirect_to #entity_redirect_edit_path, notice: 'Profile image was successfully created.' }
format.json { render json: #profile_image, status: :created, location: #profile_image }
else
format.html { render action: "new" }
format.json { render json: #profile_image.errors, status: :unprocessable_entity }
end
end
end
Source: http://www.davehulihan.com/uploading-data-uris-in-carrierwave/
Related
I am using a bootstrap typeahead plugin. Basically, it expects a json response when you click on one of the items in the dropdown menu. However, when the item is clicked, I want to redirect to a different page; I do not want to respond with json. I don't know how to handle this situation. This is what I have:
def populate_global_search
respond_to do |format|
if params[:name] && params[:name][:value]
model = Model.where(name: params[:name][:value]
else
model = nil
end
if model.present?
format.json { redirect_to model }
else
format.json { head :ok}
end
end
end
The action is triggered from an ajax call:
$(element).bind('typeahead:selected', function(obj, datum, name) {
$.ajax({
url: that.format_url(),
data: {
name : datum
}
}).done(function(data){
...
Since the redirect_to is not a json response, nothing happens when I click on the item. What can I do?
The main problem here, you call the populate_global_search action asynchronously, respond_to block unable to redirect an asynchronously call. To resolve your problem I suggest you render the path to redirect and change the location by window.location:
The javascript code(that's just an assumption):
$(element).bind('typeahead:selected', function(obj, datum, name) {
$.ajax({
url: that.format_url(),
data: {
name : datum
}
}).done(function(data){
// redirect to the desired path
window.location = data.location;
});
The Rails code(also assumption):
def populate_global_search
respond_to do |format|
if params[:name] && params[:name][:value]
# I use find_by, because where return a collection
model = Model.find_by(name: params[:name][:value])
else
model = nil
end
if model.present?
# render a json objects { location: '/some_path/12' }
# with path which you need
format.json { render json: { location: here_is_the_desired_path } }
else
format.json { head :ok }
end
end
end
Bonus, your can reduce your Rails code to, this also should works:
def populate_global_search
model = Model.find_by(name: params[:name].try(:[], :value))
if model.present?
render json: { location: here_is_the_desired_path }
else
head :ok
end
end
Building on the helpful and working solution presented here, I'm trying to fix my update callback as well.
Problem is, the specific unit that I'm trying to extract data from is always the old cached version, even though this callback is triggered by a successful update action.
// callback triggered by the update action
$('.best_in_place').bind("ajax:success", function () {
...
console.log(unit.duration);
// which is exactly the same as
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
// and both print the OLD duration val instead of the updated val which is in the database
});
and the unit_users_controller code...
def update
#unit = #unituser.unit
respond_to do |format|
if #unituser.update(unit_user_params)
#unit.reload
logger.info('-----------------------------------------------------------------')
logger.info('#unit.duration in the controller is ' + #unit.duration.to_s) # which is the correct value
logger.info('-----------------------------------------------------------------')
gon.unit_duration = #unit.duration # an experiment which didn't work for me
format.json {respond_with_bip(#unituser) }
else
# format.html { render :action => 'edit' }
format.json { respond_with_bip(#unituser) }
end
end
end
I've tried several versions of unit.reload, and nothing helps. Maybe I was putting it in the wrong place?
I did this one sometime ago here is my code, maybe it will help you:
Javascript:
$(document).ready(function() {
$('.price_bind').bind("ajax:success", function (event, data, status, xhr) {
var parsed_data = jQuery.parseJSON(data);
$(this).text(parsed_data.newprice);
$(this).parentsUntil('body').find(".totalpricep span").text(parsed_data.totalprice);
});
}
View:
<%= best_in_place detail, :price, :classes => 'price_bind', :path => purchase_detail_path(#purchase, detail)%>
Controller:
def update
respond_to do |format|
if #detail.update_attributes(params[:detail])
#n=#detail.mk_bal
#r=false
if #detail.purchase != nil
#p=#detail.purchase.totalprice
if params[:detail]['status'] && #purchase.step==1
#remdet = #purchase.details.where(:step => 1, :status => false)
if #remdet.empty?
#purchase.update_attribute(:step, 2)
#r=true
end
end
else
#p=nil
end
format.html { redirect_to #detail, notice: 'Detail was successfully updated.' }
format.json { render :json => {:newprice => #n, :totalprice => #p, :newstatus => #detail.status, :refresh => #r}}
else
format.html { render action: "edit" }
format.json { render json: #detail.errors, status: :unprocessable_entity }
end
end
end
This isn't about caching. Your Ruby code is evaluated server-side, before the JavaScript is ever send to the client, and it's only evaluated once, long before the AJAX request can happen.
The client never sees this line:
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
All the client will see is something like:
console.log(32); // or whatever the sum is
You cannot use <%= %> here. That will always give you the original value. Instead, you need to send the new value to the client in response to the AJAX request.
I'm new to rails, and unable to set a variable in my modal using ajax! My approach is; I first pass it from external javascript (jquery dialog) to my controller, then to my model method as a parameter. Please critique!
My javascript (ajax within jquery-dialog button):
click: function() {
var qtySelected = $("#quantity[data-id='" + prodvarid + "'] input").val();//inputValue check
qtySelected = parseInt(qtySelected); //ensure value is int
$.ajax({
type: "POST",
url: '/line_items',
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: {product_id: prodvarid, qty_selected: qtySelected, remote: true},
dataType: "script"
});
alert("input value is: " + qtySelected); //inputValue check
$("#quantity[data-id='" + prodvarid + "'] input").val(1); //reset default value on submit
$(this).dialog("close");
}
My controller - it receives the :qty_selected value through an ajax POST, to which it is passed into my model using ctlQty:
# POST /line_items
# POST /line_items.json
def create
#cart = current_cart
product = Product.find(params[:product_id])
ctlQty = :qty_selected
#line_item = #cart.add_product(product.id, ctlQty)
respond_to do |format|
if #line_item.save
format.html { redirect_to(store_index_url) }
format.js { #current_item = #line_item }
format.json { render json: #line_item, status: :created, :location => #line_item }
else
format.html { render :action => "new" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
My model receives the value and sets the local variable current_qty:
def add_product(product_id, qty_selected)
current_qty = qty_selected
if current_qty
current_qty = qty_selected
else
current_qty = 1
end
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.quantity += current_qty
else
current_item = line_items.build(:product_id => product_id)
end
current_item
end
Upon testing, I receive:
"TypeError (:qty_selected can't be coerced into Fixnum)"
app/models/cart.rb:17in '+'
app/models/cart.rb:17in 'add_product'
app/controllers/line_items_controller.rb:48in 'create'
The error message states that you're trying to use a symbol and a number. Indeed, in the controller, it should be:
ctlQty = params[:qty_selected].to_i # instead of ctlQty = :qty_selected
Also, in add_product, the first 6 lines looks complex, and could simply be written
current_qty = qty_selected || 1
I am trying to parse a JSON Object to insert values in table(MySQL). JSON is recived at server but it is unable to read the values to be inserted. Its inserting NULL values.
Below is snapshot from my rails console.
Started POST "/lists.json" for 192.168.1.9 at 2013-08-13 11:38:46 +0530
Processing by ListsController#create as JSON
Parameters: {"list"=>[{"amount"=>"120", "status"=>"done", "itemno"=>"w01", "na
me"=>"t01"}]}
WARNING: Can't verify CSRF token authenticity
(1.0ms) BEGIN
SQL (1.0ms) INSERT INTO `lists` (`amount`, `itemno`, `name`, `status`) VALUES
(NULL, NULL, NULL, NULL)
(103.0ms) COMMIT
The Create method in my lists_controller.rb is as below
def create
lists = params[:list].collect{|key,list_attributes| List.new(list_attributes)}
all_list_valid = true
lists.each_with_index do |list,index|
unless list.valid?
all_list_valid = false
invalid_list = lists[index]
end
end
if all_list_valid
#lists = []
lists.each do |list|
list.save
#lists << list
end
format.html { redirect_to #list, notice: 'List was successfully created.' }
format.json { render json: #list, status: :created, location: #list }
else
format.html { render action: "new" }
format.json { render json: #list.errors, status: :unprocessable_entity }
end
end
I am not sure why it is taking NULL values even though "Parameters" seems to have correct values. Please advise . Thanks.
See documentation for the collect method.
irb(main):008:0> parameters = {"list"=>[{"amount"=>"120", "status"=>"done", "itemno"=>"w01", "name"=>"t01"}]}
=> {"list"=>[{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}]}
irb(main):009:0> parameters["list"]
=> [{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}]
irb(main):010:0> parameters["list"].collect{|list| p list}
{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}
=> [nil]
I am trying to update a model through JSON. Right now I am able to get the put method to execute.
From my browser console, I see this so I believed that my request did go through.
PUT http://localhost:3000/slot_allocations/1 [HTTP/1.1 200 OK 7ms]
However, I am not too sure how I could get Rails to accept the data I want to update and would appreciate it if someone could help me out.
This is my Jquery codes for Ajax request. dataToServer is the data I want to send to update the model.
var url = 'slot_allocations/'+alloc_id;
var dataToServer = {slot_allocation:{timeslot:timeslot, subject:subject,slot_allocation_id:alloc_id-1, group_id:"Group 2", lesson_type:"Tutorial"}}
$.ajax({
type: "PUT",
url: url,
dataType: "json",
data: JSON.stringify(dataToServer) , // message to send goes here
success: function (data)
{
}
});
In my update method in the controller I have the following codes.
def update
#slot_allocation = SlotAllocation.find(params[:id])
respond_to do |format|
if #slot_allocation.update_attributes(params[:slot_allocation])
format.html { redirect_to #slot_allocation, notice: 'Slot allocation was successfully updated.' }
format.json { render :json=>#slot_allocation.as_json }
else
format.html { render action: "edit" }
format.json { render json: #slot_allocation.errors, status: :unprocessable_entity }
format.js { render :js => #slot_allocation.errors, :status => :unprocessable_entity }
end
end
end
And this is my as_json method. I am using it for my send request from the server to the client side but I am not too sure if I could use the same method to send information from the client to the server side.
def as_json(options={})
{
:group_id =>self.group_index,
:lesson_type =>self.lesson_type,
:timeslot =>self.timeslot_id,
:subject =>self.subject_id,
:slot_allocation_id => self.slot_allocation_id
}
end
I would appreciate it if someone could guide me along as I am not too familiar with how I should get Rails to accept the parameters to update the model.
I believe you need to parse the JSON parameters first:
attributes = ActiveSupport::JSON.decode(params[:slot_allocation])
if #slot_allocation.update_attributes(attributes)
...
end
How do I parse JSON with Ruby on Rails?