I am getting this error
`Stripe::InvalidRequestError (This customer has no attached payment source):
app/controllers/subscriptions_controller.rb:24:in `create`
when I try to subscribe to a plan.
here is my code
`
class SubscriptionsController < ApplicationController
layout "subscribe"
before_action :authenticate_user!, except: [:new, :create]
def new
if user_signed_in? && current_user.subscribed?
redirect_to root_path, notice: "You are already a subscriber"
end
end
def create
Stripe.api_key = Rails.application.credentials.stripe_api_key
plan_id = params[:plan_id]
plan = Stripe::Plan.retrieve(plan_id)
token = params[:stripeToken]
customer = if current_user.stripe_id?
Stripe::Customer.retrieve(current_user.stripe_id)
else
Stripe::Customer.create(email: current_user.email, source: token)
end
subscription = customer.subscriptions.create(plan: plan.id)
options = {
stripe_id: customer.id,
stripe_subscription_id: subscription.id,
subscribed: true
}
options.merge!(
card_last4: params[:user][:card_last4],
card_exp_month: params[:user][:card_exp_month],
card_exp_year: params[:user][:card_exp_year],
card_type: params[:user][:card_type]
) if params[:user][:card_last4]
current_user.update(options)
redirect_to root_path, notice: "Your subscription was setup successfully!"
end
def destroy
customer = Stripe::Customer.retrieve(current_user.stripe_id)
customer.subscriptions.retrieve(current_user.stripe_subscription_id).delete
current_user.update(stripe_subscription_id: nil)
current_user.subscribed = false
redirect_to root_path, notice: "Your subscription has been canceled."
end
end
`
stripe.js
document.addEventListener("turbolinks:load", () => {
const publishableKey = document.querySelector("meta[name='stripe-key']").content;
const stripe = Stripe(publishableKey);
const elements = stripe.elements({
fonts: [{
cssSrc: "https://rsms.me/inter/inter-ui.css"
}],
locale: "auto"
});
const style = {
base: {
color: "#32325d",
fontWeight: 500,
fontFamily: "Inter UI, Open Sans, Segoe UI, sans-serif",
fontSize: "16px",
fontSmoothing: "antialiased",
"::placeholder": {
color: "#CFD7DF"
}
},
invalid: {
color: "#E25950"
}
};
const card = elements.create('card', {
style
});
card.mount("#card-element");
card.addEventListener('change', ({
error
}) => {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = "";
}
});
const form = document.getElementById('payment-form');
form.addEventListener('submit', async(event) => {
event.preventDefault();
const {
token,
error
} = await stripe.createToken(card);
if (error) {
const errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
stripeTokenHandler(token);
}
});
const stripeTokenHandler = (token) => {
const form = document.getElementById('payment-form');
const hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
["type", "last4", "exp_month", "exp_year"].forEach(function(field) {
addCardField(form, token, field);
});
form.submit();
}
function addCardField(form, token, field) {
let hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', "user[card_" + field + "]");
hiddenInput.setAttribute('value', token.card[field]);
form.appendChild(hiddenInput);
}
});
I have all the plans and stripe Api configured correctly but something is wrong with the code. i am not very good in Js. So most of the code is a copy and paste and modified to fit my needs
i have search all over i can't find a solution. i need help.
Related
Several problems. To begin with I get the following error in my devtools
WebSocket connection to 'ws://localhost:3000/cable?token=ZmllcnlAc3dhZ2dlci5jb20=' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
On my rails server, I have the following output in a doc file on the project's repo: DOWNLOAD
It was rather too large for a blockquote.
To make a long story short, the connection is timing out half the time (resetting the server fixes this). However, most of the time it seems like the subscription silently fails to fire, as even making a new article from a different browser won't update the index page. I am completely lost as to what could be causing this to fail.
Relevant code:
Rails Side ApplicationCable
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = current_user
end
private
def current_user
token = request.params[:token].to_s
email = Base64.decode64(token)
User.find_by(email: email)
end
end
end
GraphQL Channel
class GraphqlChannel < ApplicationCable::Channel
def subscribed
#subscription_ids = []
end
def execute(data)
result = execute_query(data)
payload = {
result: result.subscription? ? { data: nil } : result.to_h,
more: result.subscription?
}
#subscription_ids << context[:subscription_id] if result.context[:subscription_id]
transmit(payload)
end
def unsubscribed
#subscription_ids.each do |sid|
SwyleSchema.subscriptions.delete_subscription(sid)
end
end
private
def execute_query(data)
SwyleSchema.execute(
query: data["query"],
context: context,
variables: data["variables"],
operation_name: data["operationName"]
)
end
def context
{
current_user_id: current_user.id,
current_user: current_user,
channel: self
}
end
end
Connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = current_user
end
private
def current_user
token = request.params[:token].to_s
email = Base64.decode64(token)
User.find_by(email: email)
end
end
end
Subscription Type
module Types
class SubscriptionType < GraphQL::Schema::Object
field :article_added, Types::ArticleType, null: false, description: "An article was posted"
def article_added
end
end
end
Create Article Mutation
module Mutations
class CreateArticle < BaseMutation
argument :title, String, required: true
argument :body, String, required: true
type Types::ArticleType
def resolve(title: nil, body: nil)
snippet = body[0, 300]
article = Article.new
article.title = title
article.body = body
article.snippet = snippet
article.user = context[:current_user]
if article.save
SwyleSchema.subscriptions.trigger("articleAdded", {}, article)
# { article: article}
article
else
{ errors: article.errors.full_messages }
end
end
end
end
Apollo.js
import { HttpLink } from 'apollo-link-http';
import { ApolloLink, Observable } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { onError } from 'apollo-link-error';
import { ApolloClient } from 'apollo-client';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import introspectionQueryResultData from './fragmentTypes.json';
import ActionCable from 'actioncable';
import ActionCableLink from 'graphql-ruby-client/subscriptions/ActionCableLink';
const getCableUrl = () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.hostname;
const port = process.env.CABLE_PORT || '3000';
const authToken = localStorage.getItem('mlToken');
debugger;
return `${protocol}//${host}:${port}/cable?token=${authToken}`;
};
const createActionCableLink = () => {
const cable = ActionCable.createConsumer(getCableUrl());
return new ActionCableLink({ cable });
};
const hasSubscriptionOperation = ({ query: { definitions } }) =>
definitions.some(
({ kind, operation }) =>
kind === 'OperationDefinition' && operation === 'subscription'
);
const getTokens = async () => {
const tokens = {
"X-CSRF-Token": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content")
};
const authToken = await localStorage.getItem("mlToken");
return authToken ? { ...tokens, Authorization: authToken } : tokens;
};
const setTokenForOperation = async operation => {
return operation.setContext({
headers: {
// eslint-disable-next-line
... await getTokens(),
}
});
};
const createLinkWithToken = () =>
new ApolloLink(
(operation, forward) =>
new Observable(observer => {
let handle;
Promise.resolve(operation)
.then(setTokenForOperation)
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
})
);
const createHttpLink = () => new HttpLink({
uri: 'http://localhost:3000/graphql',
credentials: 'include',
})
const logError = (error) => console.error(error);
const createErrorLink = () => onError(({ graphQLErrors, networkError, operation }) => {
if (graphQLErrors) {
logError('GraphQL - Error', {
errors: graphQLErrors,
operationName: operation.operationName,
variables: operation.variables,
});
}
if (networkError) {
logError('GraphQL - NetworkError', networkError);
}
})
export const createClient = (cache, requestLink) => {
const client = new ApolloClient({
link: ApolloLink.from([
createErrorLink(),
createLinkWithToken(),
ApolloLink.split(
hasSubscriptionOperation,
createActionCableLink(),
createHttpLink(),
)
// createHttpLink(),
]),
cache,
});
return client;
};
export const createCache = () => {
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
});
const cache = new InMemoryCache({fragmentMatcher});
if (process.env.NODE_ENV === 'development') {
window.secretVariableToStoreCache = cache;
}
return cache;
};
ARticles Index Component
import React, {Component} from 'react';
import articles from './queries/articles';
import { Query } from "react-apollo";
import {Link} from 'react-router-dom';
import ArticleTags from './article_tags';
import Subscription from './subscription';
class ArticlesIndex extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const date = Date.now();
return (
<Query query={articles}>
{({ loading, error, data, subscribeToMore }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
const articles = data.articles;
return (
<div className="article-index-page">
<h1>Newest Articles</h1>
{ articles.map((article) => (
<div className="article-index-card" key={`${article.id}${article.title}${date}`}>
<h2 className="article-index-title">{article.title}</h2>
<h3 className="article-index-subtitle">by {article.author.username}</h3>
<p className="article-index-snippet">{article.snippet}<Link className="article-index-show-link" to={`/articles/${article.id}`}>{"...more"}</Link></p>
<ArticleTags tags={["lookAtThisTag", "othertag"] } />
<h4>{article.count} Comments {article.likeCount} Likes</h4>
</div>
))}
<Subscription subscribeToMore={subscribeToMore} />
</div>
)
}}
</Query>
);
}
}
export default ArticlesIndex;
Subscription Component (as per evilmartians)
import React, { useEffect } from 'react';
import ArticleSubscription from './subscriptions/article_added';
import { graphql } from 'react-apollo';
const Subscription = ({ subscribeToMore }) => {
useEffect(() => {
return subscribeToMore({
document: ArticleSubscription,
updateQuery: (prev, { subscriptionData }) => {
console.log("subdata: ", subscriptionData.data)
if (!subscriptionData.data) return prev;
const { articleAdded } = subscriptionData.data;
if (articleAdded) {
const alreadyInList = prev.articles.find(e => e.id === articleAdded.id);
if (alreadyInList) {
return prev;
}
return { ...prev, articles: prev.articles.concat([articleAdded]) };
}
return prev;
},
});
}, []);
return null;
};
export default Subscription;
And the subscriptions themselves
import gql from 'graphql-tag';
const ArticleSubscription = gql`
subscription ArticleSubscription {
articleAdded {
id
title
body
likers
likeCount
author {
id
username
},
currentUser {
id
username
email
}
}
}`
export default ArticleSubscription;
When I try to make a post I'm getting a 400 (Bad Request) Error.
I'm trying to post a new Player into a Team.
My controller
def create
#teams = Team.find(params[:team_id])
#players = #teams.players.new(player_params)
render json: #players
end
private
def player_params
params.require(:player).permit(:name, :photo, :nationality)
end
The function
_handleSubmit = async (e) => {
e.preventDefault();
const teamId = this.props.match.params.id;
const payload = this.state.players;
console.log(payload);
try {
const res = await axios.post(`/api/teams/${teamId}/players`, payload);
} catch (err) {
console.log(err);
}
};
_handleChange = (e) => {
const newState = { ...this.state.players };
newState[e.target.name] = e.target.value;
this.setState({ players: newState });
};
You need to strong compare what the body you send, and what the body you expect to receive on the server?
On backend you can send not only error code, and error message, or field identifier which error occurs.
And don't forget to set await statement if you will work with query result:
const res = await axios.post(`/api/teams/${teamId}/players`, payload);
I am attempting to create a stripe user upon user creation for firebase, I keep receiving this error (error displayed below). the code for the function is also displayed below. if I need to post the database structure I will do so, I currently do not have any structure for stripe customer (this might be where issue occurs). if anyone can assist I would greatly appreciate it.
Error:
Error: Reference.child failed: First argument was an invalid path = "/stripe_customers/${data.uid}/customer_id". Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]"
at Object.exports.validatePathString (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/validation.js:282:15)
at Object.exports.validateRootPathString (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/validation.js:293:13)
at Reference.child (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/api/Reference.js:72:30)
at Database.ref (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/api/Database.js:60:54)
at stripe.customers.create.then (/user_code/index.js:41:29)
at tryCatcher (/user_code/node_modules/stripe/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/user_code/node_modules/stripe/node_modules/bluebird/js/release/promise.js:512:31)
at Promise._settlePromise (/user_code/node_modules/stripe/node_modules/bluebird/js/release/promise.js:569:18)
at Promise._settlePromise0 (/user_code/node_modules/stripe/node_modules/bluebird/js/release/promise.js:614:10)
at Promise._settlePromises (/user_code/node_modules/stripe/node_modules/bluebird/js/release/promise.js:693:18)
at Async._drainQueue (/user_code/node_modules/stripe/node_modules/bluebird/js/release/async.js:133:16)
at Async._drainQueues (/user_code/node_modules/stripe/node_modules/bluebird/js/release/async.js:143:10)
at Immediate.Async.drainQueues (/user_code/node_modules/stripe/node_modules/bluebird/js/release/async.js:17:14)
at runCallback (timers.js:672:20)
at tryOnImmediate (timers.js:645:5)
at processImmediate [as _immediateCallback] (timers.js:617:5)
Functions:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const logging = require('#google-cloud/logging')();
admin.initializeApp(functions.config().firebase);
const stripe = require('stripe')(functions.config().stripe.token);
const currency = functions.config().stripe.currency || 'USD';
//[START chargecustomer]
//charge the stripe customer whenever an amount is written to the realtime database
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}').onWrite((event) => {
const val = event.data.val();
if (val === null || val.id || val.error) return null;
return admin.database().ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
const amount = val.amount;
const idempotency_key = event.params.id;
let charge = {amount, currency, customer};
if (val.source !== null) charge.source = val.source;
return stripe.charges.create(charge, {idempotency_key});
}).then((response) => {
return event.data.adminRef.set(response);
}).catch((error) => {
return event.data.adminRef.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: events.params.userId});
});
});
// [end chargecustomer]]
// when user is created register them with stripe
exports.createStripeCustomer = functions.auth.user().onCreate((event) => {
const data = event.data;
return stripe.customers.create({
email: data.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${data.uid}/customer_id`).set(customer.id);
});
});
// add a payment source (card) for a user by writing a stripe payment source token to realtime database
exports.addPaymentSource =. functions.database.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((event) => {
const source = event.data.val();
if (sourve === null) return null;
return admin.database.ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source});
}).then((response) => {
return event.data.adminRef.parent.set(response);
}, (error) => {
return event.data.adminRef.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: event.params.userId});
});
});
// when a user deletes their account, clean up after the
exports.cleanupUser = functions.auth.user().onDelete((event) => {
return admin.database().ref(`/stripe_customers/${event.data.uid}`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.del(customer.customer_id);
}).then(() => {
return admin.database().ref(`/stripe_customers/${event.data.uid}`).remove();
});
});
function reportError(err, context = {}) {
const logName = 'errors';
const lof = logging.log(logName);
const metadata = {
resource: {
type: 'cloud_function',
labels: {function_name: process.env.FUNCTION_NAME},
},
};
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
return new Promise((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error) => {
if (error) {
reject(error);
}
resolve();
});
});
}
// end [reportError]
// sanitize the error message for the user
function userFacingMessage(error) {
returnerror.type ? error.message : 'an error occurred, developers have been altered';
}
Database Structure:
In your code you have this:
ref('/stripe_customers/${event.params.userId}/customer_id')
this ${event.params.userId} should give you the value of the wildcard, but since you are using ' it is including the $ in the path also. So you need to change it like this:
ref(`/stripe_customers/${event.params.userId}/customer_id`)
by changing ' to `
I am currently working on an Ionic mobile application which will eventually take photos, attach a location and send them inside a post request to a rails endpoint. After looking at this link and this link and countless others, I have been unable to find any solid information on implementing this particular feature.
I can upload photos through the browser using a html input form, which is then added to the database and is displayed on the app via a get request.
However at the moment when taking a photo on the phone and attempting to send it via a post request directly from the app, only the location information is being received, the image is not being correctly encoded.
Here is the jSON data that has been received, its returning "image_url":"/images/main/missing.png".
{ "id":6,"city":"Greater London",
"country":"United Kingdom","created_at":"2015-05-14T21:22:22.825Z",
"updated_at":"2015-05-14T21:22:22.825Z","image_file_name":null,
"image_content_type":null,"image_file_size":null,
"image_updated_at":null,"image_url":"/images/main/missing.png" }
Here is the code:
Angular factory making post request:
.factory('Posts', function($http) {
var o = { posts: [] };
o.getAll = function() {
return $http.get('http://localhost:8100/posts').success(function(data) {
angular.copy(data, o.posts);
});
};
o.addPost = function(post) {
return $http.post('https://shielded-hamlet-4665.herokuapp.com/posts', post);
};
return o;
})
Angular Controller taking photo:
.controller("CameraCtrl", function($scope, $cordovaCamera, $http, Posts) {
var id = 0;
var options = {
quality : 75,
destinationType : Camera.DestinationType.FILE_URI,
sourceType : 1,
allowEdit : true,
encodingType: 0,
targetWidth: 380,
targetHeight: 450,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
function getLocCoords(position) {
$scope.lat = position.coords.latitude;
$scope.lon = position.coords.longitude;
$http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + $scope.lat +',' + $scope.lon + '&sensor=true')
.success(function(data) {
var home = data.results[0].address_components;
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf("administrative_area_level_2") > -1) {
$scope.city = home[i].long_name;
break;
};
};
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf('country') > -1) {
$scope.country = home[i].long_name;
break;
};
};
})
};
$scope.takePicture = function() {
navigator.geolocation.getCurrentPosition(getLocCoords);
$cordovaCamera.getPicture(options).then(function(imageData) {
$scope.imgURI = imageData;
id ++;
var post = { id: id, country: $scope.country, city: $scope.city, image: $scope.imgURI, likes: 0, comments: [] }
Posts.addPost(post);
}, function(err) {
});
}
Post Controller from the Rails Database:
class PostsController < ApplicationController
skip_before_filter :verify_authenticity_token
def index
#posts = Post.all
render json: #posts, :callback => params['callback'], :content_type => 'application/javascript', :methods => [:image_url]
end
def new
#post = Post.new
end
def create
Post.create(post_params)
redirect_to '/posts'
end
def post_params
params.require(:post).permit(:city, :country, :image)
end
end
I have not done a great deal of work with the ionic framework so please forgive my ignorance. Any help would be greatly appreciated.
Managed to solve this using the cordovaFileTransfer.upload method.
The rails end point was also filtering params and looking for a post object, with a image string, and only an image string was being provided.
The following code is now working
Angular factory making post request:
.factory('Posts', function($http, $cordovaFileTransfer) {
var o = { posts: [] };
o.getAll = function() {
return $http.get('https://shielded-hamlet-4665.herokuapp.com/posts').success(function(data) {
angular.copy(data, o.posts);
});
};
o.addPost = function(post) {
var options = {
fileKey: "image",
fileName: "image.jpeg",
chunkedMode: false,
mimeType: "image/jpeg",
params: { city: post.city, country: post.country, lat: post.lat, lon: post.lon }
};
$cordovaFileTransfer.upload('http://shielded-hamlet-4665.herokuapp.com/posts', post.image, options)
.then(function(result){
console.log("Code = ok");
}, function(error){
console.log("Code = " + error);
}, function(progress){});
};
return o;
})
Angular Controller taking photo:
.controller("CameraCtrl", function($scope, $cordovaCamera, $http, Posts) {
post = {};
var options = {
quality : 75,
destinationType : Camera.DestinationType.FILE_URI,
sourceType : 1,
allowEdit : true,
encodingType: 0,
targetWidth: 380,
targetHeight: 450,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
function getLocCoords(position) {
post.lat = position.coords.latitude;
post.lon = position.coords.longitude;
$http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + post.lat +',' + post.lon + '&sensor=true')
.success(function(data) {
var home = data.results[0].address_components;
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf("administrative_area_level_2") > -1) {
post.city = home[i].long_name;
break;
};
};
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf('country') > -1) {
post.country = home[i].long_name;
break;
};
};
})
};
$scope.takePicture = function() {
navigator.geolocation.getCurrentPosition(getLocCoords);
$cordovaCamera.getPicture(options).then(function(imageData) {
post.image = imageData;
Posts.addPost(post);
}, function(err) {});
};
});
Post controller from rails database:
class PostsController < ApplicationController
skip_before_filter :verify_authenticity_token
def index
#posts = Post.all
render json: #posts, :callback => params['callback'], :content_type => 'application/javascript', :methods => [:image_url]
end
def new
#post = Post.new
end
def create
Post.create(post_params)
redirect_to '/posts'
end
def post_params
params.permit(:city, :country, :image, :lat, :lon)
end
end
I am using PaperClip with Rails to upload files and it works fine, however it would like to implment a drag and drop fileupload that allows for uploading of multiple files. and that each file shoudn't be more than a certain size.
Edit:
Here is what is what i have so far, i have created the javascript part. However i am lost as how to create the controller part:
var $dropArea = $(".drop-area");
$dropArea.bind({
dragover: function () {
$(this).addClass('hover');
return false;
},
dragend: function () {
$(this).removeClass('hover');
return false;
},
drop: function (e) {
e = e || window.event;
e.preventDefault();
e = e.originalEvent || e;
var files = (e.files || e.dataTransfer.files);
var $img = $('<img src="" class="uploadPic" title="" alt="" />');
for (var i = 0; i < files.length; i++) {
(function (i) {
var reader = new FileReader();
reader.onload = function (event) {
var newImg = $img.clone().attr({
src: event.target.result,
title: (files[i].name),
alt: (files[i].name)
});
$("body").append(newImg);
};
reader.readAsDataURL(files[i]);
var xhr = new XMLHttpRequest();
var fd = new FormData();
fd.append(files[i].name, files[i]);
xhr.open("POST", 'url', true);
xhr.send(fd);
})(i);
}
return false;
}
});
And this is the basic controller part:
def create
#image = Image.new(params[:image])
if #image.save
respond_to do |format|
format.html { redirect_to action: 'index', :notice => 'Image saved'}
format.js { redirect_to action: 'index', :notice => 'Image saved'}
format.xml { redirect_to action: 'index', :notice => 'Image saved'}
end
else
flash[:notice] = "Error, Please try again"
redirect_to action: 'new'
end
end
How can i do this?
Thanks
After some research, i found that it could be easily done like this:
var dropArea = document.getElementById("droparea"),
viewArea = document.getElementById("previewarea");
dropArea.addEventListener("drop", function(evt){
evt.preventDefault();
evt.stopPropagation();
previewFiles(evt.dataTransfer.files);
return false;
}, false);
function previewFiles(files){
for (i=0; i < files.length; i++){
if (typeof FileReader != "undefined"){
var img = document.createElement("img");
viewArea.appendChild(img);
var reader = new FileReader();
reader.onload = (function(theImg){
return function(evt){
theImg.src = evt.target.result;
}
}(img));
reader.readAsDataURL(files[i]);
}
}
uploadFiles(files);
}
function uploadFiles(files){
var fd = FormData();
var position = 0;
var max = files.length;
if (typeof fd != "undefined"){
function queue(){
if (max >= 1 && position <= max - 1){
fd.append("file", files[position]);
upload();
}
}
function upload(){
$.ajax({
url: '/boxes/hiThere',
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data){
position = position + 1;
queue();
}
});
}
queue();
}
}
dropArea.addEventListener("dragenter", function(evt){
if (evt){
this.className = "drag-enter";
}
endFn(evt);
}, false);
dropArea.addEventListener("dragleave", function(evt){
this.className = "";
endFn(evt);
}, false);
dropArea.addEventListener("dragover", function(evt){
endFn(evt);
evt.dataTransfer.dropEffect = 'move';
return false;
}, false);
function endFn(evt){
evt.preventDefault();
evt.stopPropagation();
}
and simply add the normal save in rails like this:
def hiThere
box = Box.new(params[:box])
box.user_id = current_user.id
box.file_path = params[:file]
if box.save!
set_flash "File saved sucessfully"
else
set_flash "Error, please try again"
end
respond_to do |format|
format.js { redirect_to action: :index, :notice => "Done" }
end
end