How t o display uploaded image in react - javascript

I have uploaded an image with active storage in rails and attached it to a model but am stuck with how i an display it with the rest of the data. Am using react and ruby at the back end both combined together. when i hit in the console Home.first.image_url it shows that the image is uploaded but when i console.log the data coming from the server the image is not included.`
here is code
here is my react front end
useEffect(() => {
let path = 'api/v1/homes'
axios.get(path)
.then(res => {
console.log(res)
setItems(res.data)
setIsLoading(false);
})
.catch(err => {
setIsLoading(false);
console.log(err)
})
}, [])
return (
<div>
}}
dataSource={items}
renderItem={(home, index) => (
<List.Item>
<Card
hoverable
title={home.title}
key={index}
cover={
<img src="${home.image_url}" />
// <img className='itemCardImage' src={home.image_url}
// onClick={() => navigate(`/details/${home.id}`)}
// />
}
am using rails 6 with active storage and am uploading image from active admin form. I am making an e-commerce website where i want admins to create products with images . below is my active admin form where image is uploaded from
form title: 'Create a new property' do |f|
f.inputs do
f.input :image, as: :file
end
f.actions
end
and here is my controller
class Api::V1::HomesController < ApplicationController
def index
#homes = Home.all.order(created_at: :desc)
render json: #homes
end
# GET /homes/1 or /homes/1.json
def show
if #home
render json: #home
else
render json: #home.errors
end
end
# POST /homes or /homes.json
def create
#home = Home.new(home_params)
if #home.save
render json: HomeSerializer.new(#home).serializable_hash[:data][:attributes]
else
render json: #home.errors
end
end
private
# Only allow a list of trusted parameters through.
def home_params
params.require(:home).permit(:title, :description, :price, :availability, :image)
end
end
here is my console from the terminal
Started GET "/$%7Bhome.image_url%7D" for ::1 at 2023-01-31 18:04:27 +0300
Processing by HomesController#index as */*
Parameters: {"unmatched"=>"${home"}
am really a new junior developer . am intention is to make a big e-commerce website that sells and rents houses and plots of land. I want admin to manage the site like creating items with images and uploads them. am using active storage in and am still in development mode.

Mutebi you have everything nearly fine, only issue you are facing why you are unable to display images is possibly a line in your routes file for unmatched routes get "*unmatched", to: "homes#index" so this is blocking routes from rails/activestorage.
If your change that code to something like:
match '*unmatched_route', via: :all, to: 'homes#index', constraints: lambda { |request|
request.path.exclude? 'rails/active_storage'
}
Should work perfectly fine.

Use activerecord serializer, add attribute :image. Then with url_for rails helper you will be able to generate a url for image url.

Related

How to redirect from React front-end with Rails routes?

I am new to React and struggling to redirect after making a post request to my api.
In an erb view, I am using a javascript pack tag to render some React components on a new resource page. I am using a button onClick to send my post request (axios) to api/v1/resources. I'd like to redirect to admin/resources after the request is sent.
I am wondering why redirecting from the create action in my Api::V1::ResourcesController doesn't work.
Can I redirect without setting up react router? If not, how can I set that up? (All of my routes are defined in Rails).
Everything I can find seems to pertain to an entire React front-end when I am only using bits of React in a predominantly Rails front-end.
This is my handleSubmit function that gets called onClick:
const handleSubmit = () => {
let newResourceParams = {
title: title,
description: description,
occurred_on: occurredOn,
resource_type: resourceType
}
Api.postNewResource(newResourceParams)
}
Here is Api.postNewResource:
postNewResource(data) {
return AxiosApi.post('/api/v1/resources', data)
.then(({data}) => data['data'])
.catch((errors) =>
console.log(errors)
)
}
And here is my create action in Api::V1::ResourcesController:
def create
Resource.create(resource_params)
redirect_to admin_resources_path
end
Any guidance or help on this would be greatly appreciated. Thanks
You could try something like this:
redirect_to admin_resources_path, format: :json
Let me know if any luck with that?
Source
P.S. Very well formulated question. ;)

How to serve image from my server with a MERN stack

Broad overview of my app: Users can submit posts with images and text.
I am using MERN stack.
Currently, images are being saved into my localhost:3001 server at /public/uploads/<images> and the route is then saved into my database.
I am now trying to render the posts of screen.
// example value of what gets stored into my db
// post.img.url = "public\uploads\user-5cc37dda4142ff49a8c903d2-1557200552890.png"
posts.map( ( post, i ) => {
return <img src={ post.img.url } alt='post image'} />
} )
When I hover over the chrome console to view the route that it returns, this is the route that shows up:
http://localhost:3000/profile/admin/posts/public/uploads/user-5cc37dda4142ff49a8c903d2-1557174873119.png
To actually view the image, you would have to go to this route: http://localhost:3001/uploads/user-5cc37dda4142ff49a8c903d2-1557200552890.png
How can I get the correct route for my images?
Some addition details that might matter
// this line is in my server.js
app.use( express.static( path.join( __dirname, 'public' ) ) )
Just set some global or .env variable in your React app which contain a http route to your server like this: SERVER_HOST = 'http://localhost:3001'. Then you need to store the relative path to your images in database like /uploads/user-*.png. Now you can get access to images like this:
posts.map( ( post, i ) => {
return <img src={ SERVER_HOST + post.img.path } alt='post image'} />
})

rails remove element in real time

Got a link that removes a message from the current_users screen:
= link_to '[x]', msg, method: :delete, remote: true, class: "del-link"
It triggers this coffeescript-funktion:
delete_message = () ->
$('.del-link').on 'ajax:success', (event) ->
event.preventDefault()
$(this).closest('.message').remove()
return
and this rails-method:
def destroy
#msg = Msg.find(params[:id])
#msg.destroy
respond_to do |format|
format.html { redirect_to chat_path }
format.json { head :no_content }
format.js { render :layout => false }
end
end
But how would it be done, that the message is removed on every users screen, e.g. using ActionCable?
The coffeescript for the chat:
App.proom = App.cable.subscriptions.create "ProomChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
unless data.msg.blank?
$('#messages-table').append data.msg
scroll_bottom()
$(document).on "turbolinks:load", ->
submit_msg()
scroll_bottom()
delete_message()
the channel.rb
class ProomChannel < ApplicationCable::Channel
def subscribed
stream_from "proom_channel"
end
def unsubscribed
end
end
Approach
So I added this to the destroy-action of the controller
if msg.destroy
ActionCable.server.broadcast 'proom_channel', msg: #msg
end
Also I added an ID to every message-div-element containing the id of the message-record to find and remove it from the DOM with the following line
$("msg_#{msg.id}").remove()
But now I don't know where to put it.
I am not able to give you right now the complete answer, but I will update my answer later if you do not find a solution:
Like when you create a message with action cable, after saving the message in the DB, you trigger an ActionCable.server.broadcast 'messages' that will execute some JS included for example in the User Browser asset pipeline file app/assets/javascripts/channels/messages.js
This is my message controller, once I save the message, I start the Server.broadcast process. It will trigger the process in my messages_channel.rb that will update all clients that are subscribed to that channel.
def create
message = Message.new(message_params)
message.user = current_user 
chatroom = message.chatroom
if message.save
ActionCable.server.broadcast 'messages',
message: message.content,
user: message.user.name,
chatroom_id: message.chatroom_id,
lastuser: chatroom.messages.last(2)[0].user.name
head :ok
end
end
Image and text is from the following article of Sophie DeBenedetto
We add our new subscription to our consumer with
App.cable.subscriptions.create. We give this function an argument of
the name of the channel to which we want to subscribe,
MessagesChannel.
When this subscriptions.create function is invoked, it will invoke the
MessagesChannel#subscribed method, which is in fact a callback method.
MessagesChannel#subscribed streams from our messages broadcast,
sending along any new messages as JSON to the client-side subscription
function.
Then, the received function is invoked, with an argument of this new
message JSON. The received function in turn calls a helper function
that we have defined, renderMessage, which simply appends new messages
to the DOM, using the $("#messages") jQuery selector, which can be
found on the chatroom show page.
The channel will call the messages.js function received, that will append the div to the DOM, you will need to call an alternative action in messages.js.
App.messages = App.cable.subscriptions.create('MessagesChannel', {
received: function(data) {
// code executed - the parameters will be in data.chatroom_id and
// data.user, data.message etc...
}
});
So you will need to call the channel from your action messages#destroy, have in messages.js a specific function to remove the div from the DOM.
I don't know if you will need to create a specific server side channel or you can just edit your present messages_channel.rb to include a specific function.. You need to read the guides and figure this out.. I may try this in the future, but right now I can't
Or an easy alternative, just write some js in messages.js to solve this and have the div removed when required.. for example the messages#destroy action can pass a parameter and if that parameters is present, you will remove the message, instead of adding it
https://github.com/rails/rails/tree/master/actioncable#channel-example-1-user-appearances

How can I troubleshoot Facebook login in my Heroku production app?

I am having trouble diagnosing why my app's Facebook login flow, which worked fine in development, is failing when deployed to Heroku.
In brief, the flow works like this: Facebook's javascript sdk web login code is rendered onto website pages by a React component. When Facebook responds to a login request, it goes to the callback controller /users/auth/facebook/callback. Devise-omniauth lets Devise know to create a registration or session, and callback code in my app updates the view.
Here is the relevant output of heroku logs:
Started GET "/users/auth/facebook/callback" for 23.243.29.123 at 2016-04-24 14:38:36 +0000
I, [2016-04-24T14:38:36.279311 #8] INFO -- omniauth: (facebook) Callback phase initiated.
Processing by Users::OmniauthCallbacksController#facebook as JSON
User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2 ORDER BY "users"."id" ASC LIMIT 1 [["provider", "facebook"], ["uid", "10239847019823470"]]
(3.2ms) BEGIN
User Exists (2.7ms) SELECT 1 AS one FROM "users" WHERE "users"."username" = 'Fake Name' LIMIT 1
(3.1ms) ROLLBACK
Redirected to https://fakeapp.herokuapp.com/users/sign_up
So most things that I've seen covered in other questions have gone right:
I'm reaching Facebook, and FB is responding, so the Heroku Config Vars supplying the FACEBOOK_APP_ID and FACEBOOK_SECRET appear to be set successfully.
Facebook is transmitting user data to the omniauth callback
controller, so I've got the right URL listed in Facebook's whitelist
of callback URLs.
The omniauth controller seems to be properly
parsing the uid and username from the hash that Facebook is sending,
and trying to sign the user in.
And then the attempt to persist the user in the database is getting rolled back. Instead of creating or updating a record in the user table, my production app is saying 'User Exists,' and doing a ROLLBACK. (The users do NOT exist in the database, though. I've run heroku run rails c and User.all() does not include the user I'm trying to log in through Facebook.)
For comparison, here is a 'healthy' login from my development app:
Processing by Users::OmniauthCallbacksController#facebook as JSON
User Load (6.0ms) SELECT "users".* FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2 ORDER BY "users"."id" ASC LIMIT 1 [["provider", "facebook"], ["uid", "102983457091284357"]]
(0.1ms) BEGIN
SQL (6.8ms) UPDATE "users" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["last_sign_in_at", "2016-04-24 13:59:24.953190"], ["current_sign_in_at", "2016-04-25 13:33:48.756553"], ["sign_in_count", 55], ["updated_at", "2016-04-25 13:33:48.758631"], ["id", 8]]
(5.3ms) COMMIT
Completed 200 OK in 23ms (Views: 0.4ms | ActiveRecord: 18.3ms)
This is omniauth_callbacks_controller.rb:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
respond_to do |format|
format.json do
sign_in #user
return render :json => #user
end
format.html do
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
end
end
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
Here is the User.from_omniauth method that the callback controller is calling to try to sign in the Facebook user:
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.username = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
end
end
So I've got no idea what's causing the attempt to persist the user to fail. Any thoughts on what's causing the failure, or just on how I can get better visibility into the problem than I can get just by viewing the Heroku app logs, would be very welcome.

How do I use req.flash() with EJS?

I want to be able to flash a message to the client with Express and EJS. I've looked all over and I still can't find an example or tutorial. Could someone tell me the easiest way to flash a message?
Thanks!
I know this is an old question, but I recently ran across it when trying to understand flash messages and templates myself, so I hope this helps others in my situation. Considering the case of Express 4, the express-flash module, and an ejs template, here are 2 routes and a template that should get you started.
First generate the flash message you want to display. Here the app.all() method maps to /express-flash. Request baseurl/express-flash to create a message using the req.flash(type, message) setter method before being redirected to baseurl/.
app.all('/express-flash', req, res ) {
req.flash('success', 'This is a flash message using the express-flash module.');
res.redirect(301, '/');
}
Next map the message to the template in the res.render() method of the target route, baseurl/. Here the req.flash(type) getter method returns the message or messages matching the type, success, which are mapped to the template variable, expressFlash.
app.get('/', req, res ) {
res.render('index', {expressFlash: req.flash('success') });
}
Finally, display the value of expressFlash, if it exists, in index.ejs.
<p> Express-Flash Demo </p>
<% if ( expressFlash.length > 0 ) { %>
<p>Message: <%= expressFlash %> </p>
<% } %>
Then start the server and visit baseurl/express-flash. It should trigger a redirect to baseurl/ with the flash message. Now refresh baseurl/ and see the message disappear.
<% if ( message ) { %>
<div class="flash"><%= message %></div>
<% } %>
Is this what you want? You can use some client-side JS to have it fading out. jQuery example:
var message = $( '.message' );
if ( message.length ) {
setTimeout( function() {
message.fadeOut( 'slow' );
}, 5000 );
}
req.flash() can be used in two ways.
If you use two arguments
req.flash(type, message);
where type is a type of message and message is actual message (both strings), then it adds message to the queue of type type. Using it with one argument
req.flash(type);
returns array of all messages of type type and empties the queue. Additionally this method is attached to the session, so it works per session. In other words, each user has its own set of flash queues. So in your view you can do something like this:
var messages = req.flash('info');
and then send the messages variable to the template and use it there (note that messages is an array and you can iterate it). Remember that using req.flash('info', 'test'); will append a test message of type info only for a current user (associated with the req object).
Keep in mind that this mechanism is quite weak. For example if a user double clicks on link (two requests send to the server), then he will not see messages, because the first call will empty the queue (of course unless the request generates messages).
I was struggling with this as well; Getting my flash message to appear in my .ejs.
BUT, I finally got it to WORK and this is what I understand.
Once the getter req.flash('_msgName_'); is called it is cleared!
Also when you app.use(session()); cookie must equal cookie: {maxAge: 720}, essentially a big number.
I was using a console.log() to test the getter, and it was displaying in my console, but not in my .ejs. I took the console.log() out and it worked.
Here is some of my code.
Server.js ~
app.get('/', function(req, res) {
// This is where we will retrieve the users from the database and include them in the view page we will be rendering.
User.find({},function(err, allUsers){
if(err){
console.log("Oh no we got an error\n ERROR :: "+err);
} else {
// console.log(allUsers);
res.render('index',{users : allUsers, msg : req.flash('vError')});
}
});
});
// Add User Request
app.post('/users', function(req, res) {
console.log("REQUESTED DATA:\t", req.body);
var user = new User(
{
name: req.body.name,
age: req.body.age
}
);
// Saves user to DB.
user.save(function(err) {
if(err) {
console.log('OOPS, Something went Wrong... \n ERROR :: '+err+'\n');
for(var key in err.errors){
// console.log(err.errors[key].message);
req.flash('vError', err.errors[key].message);
}
// **HERE I WAS ACCIDENTALLY CLEARING IT!!!** \\
// console.log(req.flash('vError'));
// res.render('index',{users : [], msg : req.flash('vError')});
res.redirect('/');
} else {
console.log('\tSuccessfully added a new User to the DB!');
res.redirect('/');
}
})
});
index.ejs ~
<% if(msg.length > 0) { %>
<% for (var error in msg) { %>
<h3><%= msg[error] %></h3>
<% } %>
<% } %>
If you use visionmedia's express-messages helper module, it becomes very simple. Github link
As it says in the module's docs:
Install the module with npm
npm install express-messages
You then assign a dynamic helper for messages in the app.config:
app.dynamicHelpers({ messages: require('express-messages') });
In your EJS file, insert the following where you want your messages
<%- messages() %>
EJS renders this into:
<div id="messages">
<ul class="info">
<li>Email queued</li>
<li>Email sent</li>
</ul>
<ul class="error">
<li>Email delivery failed</li>
</ul>
</div>
(of course, you can alter what it renders to in the module's code)
Then, to actually flash a message, include a call to:
req.flash('info', 'Your message goes here');

Categories