How to add post via ajax, to loop in rails - ruby-on-rails

i have home page, which render all posts
= render 'posts/post'
welcome_controller.rb
def home
#user = User.all
if user_signed_in?
#posts = current_user.posts.all.page(params[:page]).per(25)
#post = current_user.posts.build
else
hello
end
end
and _post.html.haml
= div_for(#posts.each) do |p|
.content-wrap
%hr
%small= "Отправлено #{p.user.first_name} #{p.user.last_name}"
= image_tag(p.user.img_url, class: 'img-responsive thumbnail user-image-home')
%p.text-content= p.content
%small.text-muted= "Добавлено #{time_ago_in_words(p.created_at)} назад"
%br
- if user_signed_in?
= link_to "Удалить", p, method: :delete, remote: true, data: { confirm: 'Удалить?', 'confirm-button-text' => 'Удалить', 'cancel-button-text' => 'Нет', 'sweet-alert-type' => 'warning' }, class: 'btn btn-danger btn-xs', id: 'wow' if current_user.id == p.user_id
%hr
= paginate #posts
and create.js.erb
$("<%= escape_javascript(render #post) %>").appendTo(".content-wrap");
But it didnt work, in console i have a error like "undefined method each", i have no idea how to add a post, and i need to add it with all divs and styles to posts loop, not only text. Help.

I think for starters user_signed_in? is not returning true so #posts is nil. Try this to make sure #posts is always defined, it will be populated when your conditional is true.
def home
#user = User.all
#posts = []
if user_signed_in?
#posts = current_user.posts.all.page(params[:page]).per(25)
#post = current_user.posts.build
else
hello
end
end
There is a lot of unconventional code here, I'd recommend wizzing through this tutorial: http://railsforzombies.org/

Related

Adding an item to a list using ajax and rails

So, I have a Ruby on Rails application where the user have a list of ingredients. When the user adds a new item to the list of ingredients I need to refresh the page so the ingredient are shown in the list. I want this to be done dynamically using Ajax, I looked up to the internet for solutions but none worked :/
Here is my HTML code(index.html.erb):
<%= form_for(:ingredientList, :url => {:controller => 'ingredients', :action => 'index'}, :remote => true) do |f| %>
<div class ="modal-text col-md-6">
<div class="model-subtitle">Ingrediente: </div>
<%= f.select :value, options_for_select(#ingredient.map{ |ing| [ing.name, ing.id]}, html_options = { class: 'form-control', required: true })%>
</div>
<div class="modal-text col-md-6">
<div class="model-subtitle">Quantidade: </div>
<%= f.text_field :quantity, class: 'form-control', placeholder: "quantidade", autofocus: true %>
</div>
<%= f.submit :Salvar, class: 'btn btn btn-success center-block'%>
My controller:
def index
#user = User.find_by id: session[:user_id]
#ingredients = Ingredient.joins(:ingredients_users).where("user_id = ?", session[:user_id]).select("*")
#user_id = session[:user_id]
#ingredient = Ingredient.all
respond_to do |format|
format.js
format.html
if params[:ingredientList]
#userIngredient = IngredientsUser.new
#userIngredient.ingredient_id = params[:ingredientList][:value]
#userIngredient.quantity = params[:ingredientList][:quantity]
#userIngredient.user_id = #user.id
if #userIngredient.save
flash[:success] = "Ingredient saved!"
else
#userIngredient.errors.full_messages.each do |error|
flash[:alert] = error
end
end
end
end
end
I don't know if I make something wrong(probably as is not working) or if I forgot to add something. Do any of you know how to solve this?
I say this without knowing what your app/views/ingredients/index.js file looks like, but I think you have it almost correct.
As soon as you call format.js and format.html in your controller, it will go ahead and render the corresponding view (eg: app/views/ingredients/index.js). Any code that comes after format.js and format.html will be executed only AFTER the rendering is complete.
So in your case, what happens is that the rendering is done prior to actually processing the input (initializing the ingredient, trying to save it, ...). Try moving format.js and format.html to the end of the action code:
def index
#user = User.find_by id: session[:user_id]
#ingredients = Ingredient.joins(:ingredients_users).where("user_id = ?", session[:user_id]).select("*")
#user_id = session[:user_id]
#ingredient = Ingredient.all
respond_to do |format|
if params[:ingredientList]
#userIngredient = IngredientsUser.new
#userIngredient.ingredient_id = params[:ingredientList][:value]
#userIngredient.quantity = params[:ingredientList][:quantity]
#userIngredient.user_id = #user.id
if #userIngredient.save
flash[:success] = "Ingredient saved!"
else
#userIngredient.errors.full_messages.each do |error|
flash[:alert] = error
end
end
end
format.js
format.html
end
end
All of that said, you're "violating" the Rails conventions. An index action should primarily be used to get a series of objects. Saving data should be either via the create (if it's a new record) or update action (if it's a change to an existing record).

Ajax escape_javascript method does not delete previous rendered partial in Rails

I'm new in Ruby on Rails so I decided to do railstutorial.org's tutorial. I stuck on chapter 12, listing 12.37-38. I converted all html.erb files into html.haml (in chapter 7). I want Ajax to replace partials after clicking Follow/Unfollow button but right now when I click e.g. Follow button, Unfollow button is appearing above Follow and this Follow button is not disappearing.
Sth like that: http://i.imgur.com/RjwTsUU.png.
After that when I'm clicking on upper button it successfully replaces rendering of Follow/Unfollow partials but the button above(in this case Follow) will be visible until page is refreshed. What I am doing wrong?
destroy.js.erb:
$("#follow_form").html("<%= escape_javascript(render 'users/follow' ) %>");
$("#followers").html('<%= #user.followers.count %>');
create.js.erb:
$("#follow_form").html("<%= escape_javascript(render 'users/unfollow' ) %>");
$("#followers").html('<%= #user.followers.count %>');
show.html.haml:
- provide(:title, #user.name)
.row
%aside.col-md-4
%section.user_info
%h1
= gravatar_for #user
= #user.name
%section.stats
= render 'shared/stats'
.col-md-8
= render 'follow_form' if logged_in?
- if #user.microposts.any?
%h3
Microposts (#{#user.microposts.count})
%ol.microposts
= render #microposts
= will_paginate #microposts
follow_form partial:
- unless current_user?(#user)
%div#follow_form
- if current_user.following?(#user)
= render 'unfollow'
- else
= render 'follow'
unfollow partial:
= form_for(current_user.active_relationships.find_by(followed_id: #user.id), html: { method: :delete}, remote: true) do |f|
= f.submit "Unfollow", class: "btn"
follow partial:
= form_for(current_user.active_relationships.build, remote: true) do |f|
%div
= hidden_field_tag :followed_id, #user.id
= f.submit "Follow", class: "btn btn-primary"
relationships_controller.rb:
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
According to the chapter where this feature is described you aren't quite using the right syntax for the render call. It should be:
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= #user.followers.count %>');
Note the extra brackets after the render call that wraps around 'users/unfollow' - the same is true for your other call:
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= #user.followers.count %>');
Hope that helps.

Rails creating a new object instead of updating it

I'm having trouble with editing an object from the admin panel on my app. (It's a political related app, hence the references to Liberal and Conservative, etc).
When I edit a category it creates a new category (with the same name) instead of updating it. I can't figure out why.
I can see from the log that when I edit the category Rails appears to go to the POST route (POST /admin/categories(.:format) admin/categories#create) instead of the PUT or PATCH route (PATCH /admin/categories/:id(.:format) admin/categories#update)
Apologies if I am using incorrect terminology, I'm not well versed in Rails.
categories_controller.rb:
class Admin::CategoriesController < Admin::DashboardController
before_action :find_category, only: [ :edit, :update, :destroy ]
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
flash[:notice] = 'Category created'
redirect_to admin_categories_path
else
flash[:error] = 'Something was wrong'
render :new
end
end
def edit
end
def update
#category.update(category_params)
if #article.save
flash[:notice] = 'Category saved'
redirect_to admin_categories_path
else
flash[:error] = 'Something was wrong'
render :edit
end
end
def destroy
#category.destroy
redirect_to admin_categories_path
end
private
def find_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:name, :liberal_image, :conservative_image)
end
end
edit.html.slim inside the admin view (using slim):
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
.container
= link_to 'Manage Categories', admin_categories_path
h1 = "Edit Category - #{#category.name}"
= render partial: 'form', locals: { category: #category }
= simple_form_for #category, url: admin_category_path, method: 'delete' do |f|
= f.submit 'Delete category', class: 'btn btn-danger btn-lg'
I'm presuming it's something wrong in one of these files but let me know if there's something else I need to copy up there. Thanks !
_form.html.slim
= simple_form_for #category, url: admin_categories_path, method: 'post' do |f|
div.form-group.required
= f.input :name, label: 'Category Name'
div.form-group.required
= f.input :liberal_image, as: :file, label: 'Liberal Image'
div.target
div.form-group.required
= f.input :conservative_image, as: :file, label: 'Conservative Image'
div.target
div.form-actions
= f.submit 'Save', class: 'btn'
javascript:
$(document).ready(function() {
$('input[type="file"]').on('change', function(event) {
var files = event.target.files;
var image = files[0]
var reader = new FileReader();
reader.onload = function(file) {
var img = new Image();
img.src = file.target.result;
$(event.target).parent().parent().find('.target').html(img);
}
reader.readAsDataURL(image);
console.log(files);
});
});
We'd need to see your form partial to be clear, but from first glance, you have an issue with your update action:
def update
if #category.update(category_params)
redirect_to admin_categories_path, notice: "Category saved"
else
render :edit, error: "Something was wrong"
end
end
You had #article.update - I don't know why, because you haven't even set #article at all.
--
Update
It seems that your form is the cause of the issue:
= simple_form_for #category, url: admin_categories_path, method: 'post' do |f|
Here, you're sending each request to the create action.
You need to use the following:
= simple_form_for [:admin, #category] do |f|
As someone noticed, you are using #article variable that seems to be undeclared. This should cause an ApplicionError, so I think this maybe a simple bug, that your "edit" link actually leads to the "new" action. Does the name field actually contains a category name, or is it empty?
UPDATE: Actually, using #article will not cause an error. But check your links to be sure anyway.

Setting a controller action inside link_to

I need help with allowing users to click "Follow/Unfollow" link and have the action take place on the user show page. Currently I have the action sitting inside a popup and I don't know how I can set it inside a link action so there would be no need for a popup.
I tried rendering the forms on the show page but that does not show as I expected.
show.html.slim:
- if current_user.following?(#user)
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
_follow.html.slim:
= form_for(current_user.relationships.build(followed_id: #user.id)) do |f|
div
= f.hidden_field :followed_id
= f.submit "Follow", class: "btn btn-large btn-primary"
_unfollow.html.slim:
= form_for(current_user.relationships.find_by(followed_id: #user),
html: { method: :delete }) do |f|
= f.submit "Unfollow", class: "btn btn-large"
Relationships controller:
def create
#user = User.find(params[:relationship] [:followed_id] )
current_user.follow!(#user)
redirect_to #user
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
redirect_to #user
end
end
You shouldn't need a form for this, just pass the correct params to a nested route for relationships, then handle the specifics in the user model follow and unfollow methods. Try something like this:
users/show.slim
- if current_user.following?(#user)
= link_to new_user_relationship_path(id: current_user.id, followed_id: #user.id), class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to user_relationship_path(id: current_user.id, followed_id: #user.id), method: delete, class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
routes
resources :users do
resources :relationships
relationships_controller
def create
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.follow #followed_user
redirect_to #user
end
def destroy
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.unfollow #followed_user
redirect_to #user
end
private
def user_params
params.require(:user).permit(:followed_id, )
end

undefined method `picture' for nil:NilClass using carrierwave

I am making a form in my rails application where people have the option of adding images and I am using 'carrierwave' but I am getting an undefined method error on the edit page. Here is the code for the form:
<%= title "Add Item to #{#todo_list.title}" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<%= form.file_field :picture %>
<% end %>
Here I can see the upload button and it is working fine but on the edit page I get the above stated error. Code for my edit page:
<%= title "Editing Todo Item" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<% end %>
<div class="row">
<div class="small-12 columns">
<%= link_to "Delete", todo_list_todo_item_path(#todo_list, #todo_item), method: :delete, data: { confirm: "Are you sure?" }, class: "button radius expand alert" %>
</div>
<%= #todo_item.picture %>
</div>
Why is this showing an undefined method error. I tried creating a method in my todo_item model but its still showing the above error.
Controller for todo_item:
class TodoItemsController < ApplicationController
before_action :require_user
before_action :find_todo_list
before_action :set_back_link, except: [:index]
def index
go_back_link_to todo_lists_path
end
def new
#todo_item = #todo_list.todo_items.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_item_params)
if #todo_item.save
flash[:success] = "Added todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "There was a problem adding that todo list item."
render action: :new
end
end
def edit
#todo_item = #todo_list.todo_items.find(params[:id])
end
def update
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.update_attributes(todo_item_params)
flash[:success] = "Saved todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "That todo item could not be saved."
render action: :edit
end
end
def destroy
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.destroy
flash[:success] = "Todo list item was deleted."
else
flash[:error] = "Todo list item could not be deleted."
end
redirect_to todo_list_todo_items_path
end
def complete
#todo_item = #todo_list.todo_items.find(params[:id])
#todo_item.toggle_completion!
redirect_to todo_list_todo_items_path, notice: "Todo item updated."
end
def url_options
{ todo_list_id: params[:todo_list_id] }.merge(super)
end
private
def set_back_link
go_back_link_to todo_list_todo_items_path(#todo_list)
end
def find_todo_list
#todo_list = current_user.todo_lists.find(params[:todo_list_id])
end
def todo_item_params
params[:todo_item].permit(:content)
end
end
To display your image you should change
<%= #todo_item.picture %>
to
<%= image_tag(#todo_item.picture_url) %>

Resources