Ajax Delete request ReactJS and Ruby on Rails - javascript

I have a component called Items that lives within a parents called ItemsContainer. When a button in Items is clicked an Ajax function is called to delete that Item.
At the moment however I am receiving a 500 error message and am not sure why.
Item Component
class Item extends React.Component{
constructor(props) {
super()
this.state = {
name: '',
price: 0,
code: '',
id: ''
}
}
componentWillMount() {
this.setState({
name: this.props.data.name,
price: this.props.data.price,
code: this.props.data.code,
id: this.props.data.id
})
}
deleteItem() {
let finalUrl = '/items/' + this.state.id;
$.ajax({
type: "DELETE",
url: finalUrl, /* THIS URL IS CALLING CORRECTLY ie. /items/8 */
dataType: "json",
success: function(response) {
console.log("successfully deleted");
},
error: function () {
console.log("error");
}
})
}
render(){
let itemName = this.props.data.name
let itemCode = this.props.data.code
let itemQuantity = 1
let itemPrice = (this.props.data.price * itemQuantity).toFixed(2)
const itemId = this.props.data.id
return(
<tr>
<td>{itemName}</td>
<td>{itemCode}</td>
<td>{itemQuantity}</td>
<td><button className="btn btn-warning" onClick={this.deleteItem.bind(this)}>Remove</button></td>
<td>£{itemPrice}</td>
</tr>
)
}
}
Rails Items Controller
class ItemsController < ApplicationController
def create
#item = Item.new(item_params)
if #item.save
render partial: 'items/item', locals: {item: #item}
else
render json: #item.errors.to_json
end
end
def destroy
if #item.destroy
render partial: 'items/item', locals: {item: #item}
else
render json: #item.errors.to_json
end
end
private
def item_params
params.require(:item).permit(
:name,
:price,
:code,
:id
)
end
end
Creating a new Item is working as expected but I can't work out why I am receiving my 500 error for my delete action. Any help would be much appreciated.

Please check your destroy method in rail controller.
There is no definition for #item hence 500 internal server error :)

Related

I want to send the value of an object to the server

What I want to come true
I am creating an app. View is Nuxt and server is Rails.
I can't register the text as I want.
I am registering book information.
There are multiple pieces of information that I want to register, and the content and status of post_items_attributes are multiple, but only one can be registered. What should I do in this case?
If you set it to 0, you can only register one, so I don't know what to do.
this.$axios.$post(url.POST_API + 'posts', {
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: [{
/////////////////////////////////////////////////
content: list[0].content,
status: list[0].status
////////////////////////////////////////////////
}]
}
})
.then((responseBook) => {
context.commit('book/userBook', responseBook)
context.commit('book/clearBook')
context.commit('todos/clear')
})
},
this.$store.state.todos.list
export const state = () => ({
list: []
})
In list
Code
Nuxt
const list = context.state.todos.list
const selectedBook = context.state.book.selectedBook
// plugin/bookInfo $title,$author,$image
this.$axios.$post(url.POST_API + 'posts', {
// send to Rails
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: [{
/////////////////////////////////////////////////////////////
content: list[0].content,
status: list[0].status
////////////////////////////////////////////////////////////
}]
}
})
.then((responseBook) => {
context.commit('book/userBook', responseBook)
context.commit('book/clearBook')
context.commit('todos/clear')
})
},
Rails
def create
posts = Post.new(post_params)
if posts.save
render json: 'OK', status: 200
else
render json: 'EEEOR', status: 500
end
end
private
def post_params
params.require(:post).permit(:title, :author, :image, post_items_attributes: [:id, :content, :status])
end
You don't give example of list in the question code. But from the context, I conclude that this is an array of JS objects. So you can map it like this:
let post_items_attributes =
list.map(item => {
return {
content: item.content,
status: item.status,
}
})
And then pass it in the post:
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: post_items_attributes
}
Or you can do it inline without this variable

Won't save the attributes of a record

I'm trying to get the upgrade the example shown in this blog post
https://medium.com/#mitch_23203/the-exact-same-app-in-hyperstack-7f281cef46ca
to use hyperstack models like the followup blog post, but it just doesn't work.
Here is my code after I changed it:
class TodoIndex < HyperComponent
def create_new_todo_item
Todo.create(title: #todo)
#title = nil
end
render(DIV, class: 'ToDo') do
IMG(class: 'Logo', src: 'assets/logo.png', alt: 'Hyperstack Logo')
H1(class: 'ToDo-Header') { 'Hyperstack To Do' }
DIV(class: 'ToDo-Container') do
DIV(class: 'ToDo-Content') do
Todo.each do |item|
TodoItem(key: item, item: item.title)
.on(:delete_item) { item.destroy }
end
end
DIV do
INPUT(type: :text, value: #title)
.on(:change) { |e| mutate #title = e.target.value }
.on(:enter) { create_new_todo_item }
BUTTON(class: 'ToDo-Add') { '+' }
.on(:click) { create_new_todo_item }
end
end
end
end
Everything seems to work fine, and new Todo's are saved but the attributes are nil. Like the title doesn't get saved.
It should be:
def create_new_todo_item
Todo.create(title: #title) # not #todo
#title = nil
end
You set the title of Todo to its state value (#todo)...

Display Rails error messages inside a Bootstrap modal when using Selectize and Ajax

I'm trying to build an app that allows users to share quotes by artists about other artists. For instance, a quote by Bob Dylan about John Lennon. As such, my Artist model is set up in a way that allows an artist to be both the Speaker and Topic on a Quote, and each Quote belongs_to each Artist as the Speaker or Topic.
I'm having trouble getting a Rails error message to display inside a Bootstrap modal when using Selectize to trigger the modal. I got the modal working by following this demo.
The modal is used to create a new Artist from the quotes/new form, but I can't get the error messages for the Artist model to display in the Bootstrap modal or on the quotes/new page. When I try to create something that triggers an error message (such as validates_uniqueness) in the modal, it just closes the modal and doesn't display the error message. Everything else is working as expected.
What am I missing to connect the Ajax request to the view?
Here's the relevant section of my form:
<%= f.label :speaker, 'Who said it?' %>
<%= f.collection_select :speaker_id, #speakers, :id, :name,
{prompt: 'Select an artist'}, {class: 'form-control selectize-speaker'} %>
Full source for quotes/form.html.erb
Here's the relevant code in my controller:
class ArtistsController < ApplicationController
def create
#artist = current_user.artists.build(artist_params)
authorize #artist
respond_to do |format|
if #artist.save
if request.referer.include?("artists")
flash[:success] = "Artist was successfully created."
format.html { redirect_to #artist }
else
format.json { render json: #artist }
end
else
format.html { render :new }
format.json { render json: #artist.errors.full_messages }
end
end
end
end
Full source for artists_controller.rb
Relevant javascript code:
$(document).on('turbolinks:load', function() {
var selectizeCallback = null;
// Selectize Speaker
$('.speaker-modal').on('hide.bs.modal', function(e) {
if (selectizeCallback != null) {
selectizeCallback();
selecitzeCallback = null;
}
$('#new_speaker').trigger('reset');
});
$('#new_speaker').on('submit', function(e) {
e.preventDefault();
$.ajax({
method: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
success: function(response) {
selectizeCallback({value: response.id, text: response.name});
selectizeCallback = null;
$('.speaker-modal').modal('toggle');
}
});
});
$('.selectize-speaker').selectize({
create: function(input, callback) {
selectizeCallback = callback;
$('.speaker-modal').modal();
$('#speaker_name').val(input);
}
}); // end selectize speaker
}); // end document on
Full source for quotes.js.
And my error message partial, shared/_error_messages.html.erb:
<% if object.errors.any? %>
<div id='error_explanation'>
<div class='alert alert-danger'>
<button type='button' class='close' data-dismiss='alert'>×</button>
<p><strong>The form contains
<%= pluralize(object.errors.count, 'error') %>.</strong></p>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
Additional source files:
models/quote.rb
models/artist.rb
controllers/quotes_controller.rb
Both a successful and unsuccessful save are returning a 200 response, which means that your success callback will be called:
success: function(response) {
selectizeCallback({value: response.id, text: response.name});
selectizeCallback = null;
$('.speaker-modal').modal('toggle');
}
This always toggles the modal, therefore closing it.
If you ensure that the response is a 4xx on validation error, then you can define an error callback which populates your errors list and does not close the modal.
So instead of:
format.json { render json: #artist.errors.full_messages }
Use something like:
format.json { render json: #artist.errors.full_messages, status: :bad_request }
Then, pass an error callback to your AJAX call:
error: function(response) {
// somehow populate your errors list
// display the errors list
}
This won't work right now, though, because the errors container won't exist: you only render it under this condition:
object.errors.any?
Which, on initial load, will always evaluate false. What you can instead do is always render the errors container, default it to some hidden class if there aren't any errors, and in your error callback, remove the hidden class after it's populated.

Rendering JS.ERB results in raw code

When an AJAX request executes, show.js.erb renders the partial _article.haml.
What I want to be able to do in show.js.erb is to write:
<%= j render 'article' %>
Since it has a .js extension I am required to wrap this in JavaScript (the example above does not render the partial), so:
'<%= j render 'article' %>' OR ('<%= j render 'article' %>');
This would render the partial but with
raw code--including HTML and JS escaping.
('things will go back to \"normal.\"<\/p>\n\n');
What's the right way to do this?
welcome#index:
.ajax_load.article-content{ data: { 'remote-url' => article_path(#article) } }
articles.js:
$(document).ready(function() {
$('.ajax_load').each(function(index, element) {
var url = $(element).data('remote-url')
if (url) {
$.get(url, function(responseText) {
$(element).html(responseText);
})
} else {
console.log("missing url for ajax!")
}
})
})
This answer belongs to #MrYoshiji.
Ajax:
$(document).ready(function() {
$('.ajax_load').each(function(index, element) {
var url = $(element).data('remote-url')
if (url) {
$.get(url, function(responseText) {
$(element).html(responseText);
}, 'html' )
} else {
console.log("missing url for ajax!")
}
})
})
articles_conroller renders _article partial directly:
def show
#respond to JS
respond_to do |format|
format.js { render :partial => "article" }
end
end
welcome#index:
.ajax_load.article-content{ data: { 'remote-url' => article_path(#article) } }
You have to decide where you want the partial to render. In most cases, you'd grab the container on the page with jQuery and insert the partial there like:
$('#article-container').html('<%= j render 'article' %>');

Backbone.js is not rendering views properly

I am new at ruby on rails and backbone as well. I have an existing code in backbone and ruby which is working fine which creates some boards and one board have many lists and so on. I will provide code for that one also if needed. Now I am going to add some feature of categories like user can create and delete categories and each categories have many boards. Following data is displayed on browser from Database which is correct. I am not getting it properly why proper views are not displaying on browser.
Following is the code. Kindly suggest me some code changes so that it will work. It will be really appreciated. Thanks in advance.
/app/assets/javascripts/collections/categories.js
Kanban.Collections.Categories = Backbone.Collection.extend({
model: Kanban.Models.Category,
url: "/categories",
});
app/assets/javascripts/models/category.js
Kanban.Models.Category = Backbone.RelationalModel.extend({
urlRoot: "/categories",
relations: [{
type: Backbone.HasMany,
key: "boards",
relatedModel: "Kanban.Models.Board",
collectionType: "Kanban.Collections.Boards",
reverseRelation: {
key: "category"
}
}]
});
app/assets/javascripts/views/categories/categories_index.js
Kanban.Views.CategoriesIndex = Backbone.View.extend({
template: JST['categories/index'],
tagName: "section",
className: "categories-index",
events: {
"submit form.create_category": "createCategory"
},
initialize: function () {
var that = this;
that.collection.on("all", that.render, that);
},
render: function () {
var that = this;
var renderedContent = that.template({
categories: that.collection
});
that.$el.html(renderedContent);
console.log("categories ka render");
return that;
},
createCategory: function (event) {
event.defaultPrevented();
var that = this;
// get form attrs, reset form
var $form = $(event.target);
var attrs = $form.serializeJSON();
$form[0].reset();
var category = new Kanban.Models.Category();
// fail if no category name
if (!attrs.category.name) {
var $createContainer = $("div.create_category");
var $nameInput = that.$el.find("input.category_name");
$nameInput.hide();
$createContainer.effect("shake", {
distance: 9,
times: 2,
complete: function () {
$nameInput.show();
$nameInput.focus();
}
}, 350);
return false;
}
// save list
category.save(attrs.category, {
success: function (data) {
// category.get("users").add(Kanban.currentUser);
that.collection.add(category);
// keep focus on list input
that.$el.find("input.category_name").focus();
}
});
}
});
app/assets/javascripts/routers/main_routers.js
Kanban.Routers.Main = Backbone.Router.extend({
initialize: function (options) {
this.$rootEl = options.$rootEl;
},
routes: {
"": "index",
"/login": "login",
"categories/:id": "showCategoryBoards",
"boards/:id": "showBoardLists"
//"boards/:id": "deleteBoard"
},
index: function () {
var that = this;
var categoriesIndex = new Kanban.Views.CategoriesIndex({
collection: Kanban.categories
});
that.$rootEl.html(categoriesIndex.render().$el);
},
//index: function () {
//var that = this;
//var boardsIndex = new Kanban.Views.BoardsIndex({
//collection: Kanban.boards
//});
//that.$rootEl.html(boardsIndex.render().$el);
//},
showCategoryBoards: function (id) {
var that = this;
var category = Kanban.categories.get(id);
var categoryShow = new Kanban.Views.BoardIndex({
model: category
});
that.$rootEl.html(categoryShow.render().$el);
},
showBoardLists: function (id) {
var that = this;
var board = Kanban.boards.get(id);
var boardShow = new Kanban.Views.BoardShow({
model: board
});
that.$rootEl.html(boardShow.render().$el);
}
});
app/assets/javascripts/kanban.js
window.Kanban = {
Models: {},
Collections: {},
Views: {},
Routers: {},
initialize: function() {
var that = this;
that.$rootEl = $("#content");
Kanban.currentUser = new Kanban.Models.CurrentUser();
Kanban.currentUser.fetch({
success: function (response) {
// console.log("got user");
Kanban.category = new Kanban.Models.Category();
Kanban.category.fetch({
success: function (response) {
Kanban.boards = new Kanban.Collections.Boards();
Kanban.boards.fetch({
success: function (response) {
// console.log("got boards");
new Kanban.Routers.Main({ $rootEl: that.$rootEl });
Backbone.history.start();
}
});
},
error: function (response) {
// console.log("please log in");
}
});
}
});
}
};
$(document).ready(function(){
Kanban.initialize();
});
//BOARD DELETION METHOD... !
$(document).on("click", ".delete-icon", function() {
var board_id = $(this).parent().attr('id');
$.ajax({
url: "/api/boards/"+board_id,
type: 'DELETE',
success: function(result) {
$("#"+board_id).remove();
}
});
});
//LIST DELETION METHOD... !
$(document).on("click", ".list-delete-icon", function() {
var listId = $(this).parent().attr('id').replace(/list_/, '');
// alert(listId);
//var id = $("div").attr('id').replace(/button/, '');
$.ajax({
url: "/api/lists/"+listId,
type: 'DELETE',
success: function(result) {
alert("success!!!");
$("#list_"+listId).remove();
}
});
});
//card DELETION METHOD... !
app/assets/templates/categories/index.jst.ejs
<header class="categories-index">
<span class=""></span>
<h2>My Categories</h2>
</header>
<div>
<ul class="nav pull-left navbar-nav">
<% categories.each(function (category) { %>
<li id="<%= category.id %>" class="boxes">
<a href="/#categories/<%= category.id %>">
<%= category.escape("title") %>
</a>
<div class="delete-icon">
<span class="glyphicon glyphicon-trash">
</div>
</li>
<% }); %>
<li class="boxes">
<p>Create New Category</p>
<form class="create_category" id="myform">
<input type="text"
id="customInput"
class="category_name"
name="category[title]"
placeholder="Category Name ..." />
<input type="text"
class="category_description"
name="category[description]"
placeholder="Category Description ..." />
<input type="submit" value="Create Category" />
</form>
</li>
</ul>
</div>
/app/controllers/categories_controller
class CategoriesController < ApplicationController
# before_action :set_category, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#categories = Category.all
# respond_with(#categories)
# #categories = current_user.categories.includes(:boards)
end
def show
respond_with(#category)
end
def new
#category = Category.new
respond_with(#category)
end
def edit
end
def create
category = Category.new(params[:category])
if category.save
# board.members << current_user
render json: category, status: :ok
else
render json: category.errors, status: :unprocessable_entity
end
end
def update
#category.update(category_params)
respond_with(#category)
end
def destroy
#category.destroy
respond_with(#category)
end
private
def set_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:title, :description)
end
end
app/models
class Category < ActiveRecord::Base
attr_accessible :title, :description
has_many :boards, dependent: :destroy
end
app/views/categories/index.rabl
collection #categories
attributes :id, :title, :description
config/routes.rb
Kanban::Application.routes.draw do
devise_for :users
resources :users
# root to: "categories#index"
root to: "root#index"
resource :root, only: [:index]
resources :categories
# resource :session, only: [:new, :create, :destroy]
#get "login" => "sessions#new"
# get "logout" => "sessions#destroy"
# resources :users, only: [:show]
namespace :api do
resources :users, only: [:show] do
collection do
get :current
end
end
resources :boards, only: [:index, :show, :create, :update, :destroy]
resources :lists , except: [:edit] do
collection do
post :sort
end
end
resources :cards, except: [:edit] do
collection do
post :sort
end
end
resources :card_comments, only: [:index, :create, :destroy]
end
end
you're trying to initialize a "collection" instead of a "model" in your CategoriesIndex file:
initialize: function () {
var that = this;
that.collection.on("all", that.render, that);
which is being called from your Route file calling constructor with collection.
index: function () {
var that = this;
var categoriesIndex = new Kanban.Views.CategoriesIndex({
collection: Kanban.categories,
});
that.$rootEl.html(categoriesIndex.render().$el);
},
so, see your kanban.js file
it should be:
Kanban.categories = new Kanban.Collections.Categories();
Kanban.categories.fetch({

Categories