Angular + Rails + MongoDB(mongoid) delete function won't work - ruby-on-rails

I have an Angular app with a rails back end and using the mongoid gem as a database. I'm trying to make a delete function, but I can't get it to work.
I get
No route matches [DELETE] "/api/tags"
I can't seem to get the ID. I thought I was doing that in the factory. Otherwise, I don't know how to get it.
My code:
angular:
var myApp = angular.module('tagsapp', ['ngRoute', 'ngResource']);
myApp.factory("Tag", function($resource) {
return $resource("/api/tags/:id", { _id: "#id"},
{
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
})
// Controllers
myApp.controller("TagListCtrl", ['$scope', '$resource', 'Tag', '$location',
function($scope, $resource, Tag, $location) {
$scope.tags = Tag.query();
$scope.saveNewTag = function() {
Tag.create({ tag: $scope.newTag }, function(response){
$scope.tags.push(response);
$scope.newTag = null;
});
}
$scope.deleteTag = function(tagId) {
Tag.destroy({ _id: tagId }, function(response){
var index = $scope.tags.indexOf(tagId);
$scope.tags.splice(index, 1)
$location.path('/')
})
};
}]);
and my rails controller:
class TagsController < ApplicationController
before_action :set_tag, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#tags = Tag.all
end
def show
#tag = Tag.find(params[:id])
end
def new
#tag = Tag.new
end
def edit
#tag = Tag.find(params[:id])
end
def create
# #tag = Tag.new(tag_params)
tag = Tag.create!(tag_params)
render json: tag, status: 201
end
def update
tag.update_attributes(tag_params)
render json: tag, status: 201
end
def destroy
#tag.destroy
respond_with #tag
end
private
def set_tag
#tag = Tag.find(params[:id])
end
def tag_params
params.require(:tag).permit(:name, :color, :order)
end
end
and my view:
<div ng-controller="TagListCtrl" ng-style="{color: myColor}">
<table>
Name: <input ng-model="newTag.name"> <br>
Color: <input ng-model="newTag.color"> <br>
Order: <input ng-model="newTag.order"> <br>
<button ng-click="saveNewTag()">Save</button>
</div>
<tr ng-show="tags.length" ng-repeat="tag in tags">
<td>{{tag.name}}</td>
<td>{{tag.color}}</td>
<td>{{tag.order}}</td>
<td>
Remove
</td>
</tr>
</table>
</div>
rails routes:
Rails.application.routes.draw do
scope "api", defaults: {format: :json} do
resources :tags
end
root to: "tags#index", anchor: false
end
I'm suspecting that my routes are off. So far, adding a new tag works in the rails index.html.erb. I didn't put in any templates for angular to route to.

Related

Creating a Review Rails Backend/React Frontend

I am attempting to allow a user that is logged in to create a review for a game. I am having a couple issues that keep popping up in my console.
HTTP Origin header (http://localhost:3001) didn't match request.base_url (http://localhost:3000)
I attempted to remedy this with putting config.force_ssl = true in my production file from what I read up on, but still hitting this issue.
Im also hitting
NoMethodError (undefined method `id' for nil:NilClass):
which is ref my review_controller in create
app/controllers/reviews_controller.rb:19:in `create'
Below is my ReviewController and my ReviewContainer and ReviewForm
class ReviewsController < ApplicationController
# before_action :authorized, only:[:create]
before_action :authenticate_with_http_digest, only: [:new, :create]
def index
reviews = Review.all
render json: reviews
end
def show
review = Review.find_by(params[:id])
render json: review
end
def create
game = Game.find_or_create_by(name: params[:review][:game_name])
review = Review.new(review_params)
review.game_id = game.id
review.user_id = #user.id
review.save
render json: review
end
def update
review = Review.find(params[:id])
review.update(review_params)
review.save
render json: review
end
def destroy
review = Review.find(params[:id])
review.destroy
render json: {error: "Review Removed"}
end
private
def review_params
params.require(:review).permit(:user_id, :game_id, :user_username, :reviewed_game, :rating, :game_name)
end
end
import React, { Component } from 'react'
import Review from './Review'
import ReviewForm from './ReviewForm'
export default class ReviewsContainer extends Component {
state = {
reviews: [],
}
componentDidMount(){
fetch('http://localhost:3000/reviews')
.then(res => res.json())
.then(reviews => this.setState({ reviews }))
}
addReview = (review) => {
fetch('http://localhost:3000/reviews',{
method: "POST",
headers: {
"Content-Type" : "application/json",
Accept: "application/json",
Authorization: `bearer ${localStorage.token}`
},
body: JSON.stringify({ review: review }
),
})
.then(res => res.json())
.then(( json => {
this.setState(prevState => ({
reviews: [...prevState.reviews, json ]
}))
}))
}
// handleShowForm = () => {
// this.setState({edit: false})
// }
render() {
return (
<div className="review-grid">
<ReviewForm addReview={this.addReview} review={this.handleSubmit} />
<h1 className="review-content">REVIEWS!</h1>
<ul className="review-cards">
{
this.state.reviews.map(review => <Review key={review.id} review={review}/>)
}
</ul>
</div>
)
}
}
import React, { Component } from 'react'
class ReviewForm extends React.Component {
state = {
reviewed_game: '',
rating: '',
user_username: '',
}
handleReviewedGame = (event) => {
this.setState ({
reviewed_game: event.target.value
})
}
handleRating = (event) => {
this.setState ({
rating: event.target.value
})
}
handleUser = (event) => {
this.setState ({
user_username: event.target.value
})
}
handleForm = (e) => {
e.preventDefault()
// console.log(e)
const review = {
reviewed_game: this.state.reviewed_game,
rating: this.state.rating,
}
this.props.addReview(review)
}
render() {
return (
<div className="form-container">
<form onSubmit={(e) => {this.handleForm(e)}}>
<div>
<label>Review</label>
<br></br>
<textarea type="text" placeholder="Drop Your Review" rows={10} cols={50} value={this.state.reviewed_game} onChange={this.handleReviewedGame} className="form"/>
<div>
<label>Stars</label>
<br></br>
<input type="number" max="5" min="0" value={this.state.rating} onChange={this.handleRating} />
</div>
</div>
<button type="submit" className="sub-review">Create Review!</button>
</form>
</div>
)
}
}
export default ReviewForm;
Any advise on how to correct the issue is appreciated! thanks!

Rails ajax ActionController::Parameters permitted: false

I'm trying to do a Ajax call that return all of my reviews when click on a link. When I click on the link I'm calling to a method of User model passing a parameter and I receiving this error <ActionController::Parameters {"controller"=>"users", "action"=>"show_all_reviews"} permitted: false>
My user_controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#my_reviews = #user.my_reviews.where.not(comment: [nil, ""])
#my_reviews = #my_reviews.paginate(:page => params[:page], :per_page => 1)
#friends = #user.get_friends_list
end
def show_all_reviews
#user = User.find(params[:user_id])
#my_reviews = #user.my_reviews.where.not(comment: [nil, ""])
end
private
def user_params
params.require(:user).permit(:description, :phone)
end
end
That's my button that do the Ajax call
<%= link_to 'Mostrar todos los comentarios', '#', remote: true, id: 'show_more_link', data: {user: #user.id} %>
And my jquery function:
$('#show_more_link').on('click', function(event) {
event.preventDefault();
var user_id = $(this).data("user");
console.log(user_id);
$.ajax({
url: "/show_all_reviews",
type: "POST",
data: {
"user_id": user_id,
},
dataType: "json",
success: function(data) {
alert('done');
}
});
});
I add this to routes.rb
get '/show_all_reviews', to: 'users#show_all_reviews', as: :show_all_reviews
You don't need to use $.ajax(). This can be done in simple way: -
Route: -
get '/show_all_reviews/:user_id', to: 'users#show_all_reviews', as: :show_all_reviews
Add path to link_to including remote: true
<%= link_to 'Mostrar todos los comentarios', show_all_reviews_path(user_id: #user.id), remote: true, id: 'show_more_link' %>
You made a mistake. Change type to GET inside your ajax code. As I see in your routes the custom action with GET type.
Or you can also modify approach here. Use namespace.
in your routes.rb add:
namespace :users, path: nil, as: nil do
resources :users, only: [] do
resources :reviews, only: :index
end
end
create new folder under controllers /users. Add new controller there:
controllers/users/reviews_controller.rb
class Users::ReviewsController < ApplicationController
def index
#user = User.find(params[:user_id])
#reviews = #user.my_reviews.where.not(comment: [nil, ""])
render json: #reviews
end
end
inside view file:
<%= link_to 'reviews', user_reviews_path(user), remote: true %>

Searchkick in another controller

I am a newbie in Ruby on rails, please help me solve my problem.
I already use gem searchkick, and in console I can get the result.
this is my code
route.rb
resources :m_customers
resource :patient, except: [:index, :show] do
collection do
get 'autocomplete'
end
end
root :to => 'home#index'
get 'patient/PatientList', to:'patient#PatientList'
get 'patient/PatientList/autocomplete', to:'patient#autocomplete'
get 'patient/PatientHistory/:kode/:histid', to:'patient#PatientHistory', as: 'patient/PatientHistory'
get 'patient/PatientSub/:id', to:'patient#PatientSub', as: 'patient/PatientSub'
get 'patient/PatientObj/:objid', to:'patient#PatientObj', as: 'patient/PatientObj'
get 'patient/PatientAsses/:assid', to:'patient#PatientAsses', as: 'patient/PatientAsses'
get 'patient/PatientPlan/:planid', to:'patient#PatientPlan', as: 'patient/PatientPlan'
m_customer.rb => model
class MCustomer < ActiveRecord::Base
self.primary_key = :Cust_ID
searchkick match: :word_start, searchable: [:Cust_Name, :Cust_ID]
MCustomer.search "tipping poi"
end
patient_controller.rb
class PatientController < ApplicationController
require 'will_paginate/array'
def PatientList
#date_now = Time.now
#patients = TRegistration.paginate(:page => params[:page], :per_page => 7).where(:Reg_status => 'N')
#patient2s = TRegistration.paginate(:page => params[:page], :per_page => 7).where(:Reg_status => 'P')
if params[:query].present?
#MCustomers = MCustomer.search(params[:query])
else
#MCustomers = []
end
end
def autocomplete
render json: MCustomer.search(params[:query], autocomplete:true, limit: 10).map do |customer| {name: customer.Cust_Name, value: customer.Cust_ID}
end
end
end
when I go to the page below, I can find the result from searchkick :
//localhost:3000/patient/PatientList/autocomplete?query=s
but when I inserted serch input, nothing could be shown
PatientList.html.erb
<div class="cust-search">
<%= form_tag patient_PatientList_path, method: :get do %>
<div class="form-group">
<%= text_field_tag :query, params[:query], class: 'form-control twitter-typeahead' %>
<%= submit_tag 'Search', class: 'btn btn-default' %>
</div>
<% end %>
</div>
java script patient.js
var ready;
ready = function() {
var engine = new Bloodhound({
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.name); },
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: { url: '../patient/PatientList/autocomplete?query=%QUERY' }
});
// initialize the bloodhound suggestion engine
var promise = engine.initialize();
promise
.done(function() { console.log('success!'); })
.fail(function() { console.log('err!'); });
// instantiate the typeahead UI
$( '.twitter-typeahead').typeahead(null, {
displayKey: 'Cust_Name',
source: engine.ttAdapter()
});
}
$(document).ready(ready);
$(document).on('page:load', ready);
please help. thanks anyway.
This is what your code should look like if you want a working autocomplete (only the relevant parts)
m_customer.rb (you should really consider naming it just customer)
class MCustomer < ActiveRecord::Base
searchkick autocomplete: ['Cust_Name']
end
you should use the autocomplete option and provide the column you want to autocomplete, e.g: cust_name
patient_controller.rb ( you should really consider naming it customer_controller )
class PatientController < ApplicationController
def autocomplete
render json: MCustomer.search(params[:query], autocomplete:true, limit: 10).map do |customer| { name: customer.Cust_Name }
end
end
end
here you are formatting your json response
patient.js (read this carefully and see the modifications)
var engine = new Bloodhound({
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.name); },
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: { url: '/patient/autocomplete?query=%QUERY' }
});
var promise = engine.initialize();
$( '.twitter-typeahead').typeahead(null, {
name: "customer",
displayKey: "name",
limit: 20,
source: engine.ttAdapter()
});
}
I think this is where you made your mistake, you should provide the right key so the method will read your json as it should and retrieve the names you want.
you should check out this wonderful guide, you can read and just copy-paste with the modifications that suit your code:
https://rubyplus.com/articles/4031-Autocomplete-using-Typeahead-and-Searchkick-in-Rails-5
hope it helps anyone

Rails: Completed 500 Internal Server Error, with AngularJS $http.post call

I'm currently working on this tutorial: AngularJS Tutorial: Learn to Build Modern Web Apps with Angular and Rails
In the tutorial project, users can create Blog Posts and Comments for those Posts. So far I've been able to create Blog Posts (which are saved into database), but when I try to create a Comment for a Post, then I get the following error:
Started POST "/posts/16/comments.json" for 127.0.0.1 at 2015-02-15 08:32:40 +0200
Processing by CommentsController#create as JSON
Parameters: {"body"=>"6", "author"=>"user", "post_id"=>"16", "comment"=>{"body"=>"6"}}
Comments create action entered... 6
Completed 500 Internal Server Error in 9ms
NameError (undefined local variable or method `post' for #<CommentsController:0xb6036e50>):
app/controllers/comments_controller.rb:6:in `create'
Note: line “Comments create action entered... 6
” is logger.info message.
Screenshot
comments_controller.rb
class CommentsController < ApplicationController
def create
logger.info "Comments create action entered... " + params[:body]
comment = post.comments.create(comment_params)
respond_with post, comment
end
def upvote
comment = post.comments.find(params[:id])
comment.increment!(:upvotes)
respond_with post, comment
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
posts_controller.rb
class PostsController < ApplicationController
def index
respond_with Post.all
end
def create
respond_with Post.create(post_params)
end
def show
logger.info "show action entered... " + params[:id]
#respond_with Post.find(params[:id])
#the code below works, the line above resulted in error: 406 (Not Acceptable)
render json: Post.find(params[:id]).to_json
end
def upvote
post = Post.find(params[:id])
post.increment!(:upvotes)
respond_with post
end
private
def post_params
logger.info "post_params entered..."
params.require(:post).permit(:link, :title)
end
end
In the PostsController's show action, I had previously changed line: respond_with Post.find(params[:id]) to: render json: Post.find(params[:id]).to_json because line: respond_with Post.find(params[:id]) produced error: GET http://0.0.0.0:3000/posts/4 406 (Not Acceptable)
I'm not sure, but the above issue might be related to internal error (500) message, why post is not found. Also if I use line: respond_with Post.find(params[:id]) in the PostsController then I still end up with the same problem with the Comment creation.
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
respond_to :json
def angular
render 'layouts/application'
end
end
routes.rb
FlapperNews::Application.routes.draw do
root to: 'application#angular'
resources :posts, only: [:create, :index, :show] do
resources :comments, only: [:show, :create] do
member do
put '/upvote' => 'comments#upvote'
end
end
member do
put '/upvote' => 'posts#upvote'
end
end
end
Below is post.js file that does the Ajax calls in which the o.addComment function's $http.post call tries to create the Comment in the following way: $http.post('/posts/' + id + '/comments.json', comment);
angular.module('flapperNews').factory('posts', ['$http',
function($http){
var o = {
posts: []
};
o.getAll = function() {
return $http.get('/posts.json').success(function(data){
angular.copy(data, o.posts);
});
};
o.create = function(post) {
console.log("o.create");
return $http.post('/posts.json', post).success(function(data){
o.posts.push(data);
});
};
o.upvote = function(post) {
return $http.put('/posts/' + post.id + '/upvote.json')
.success(function(data){
post.upvotes += 1;
});
};
o.get = function(id) {
return $http.get('/posts/' + id).then(function(res){
return res.data;
});
};
o.addComment = function(id, comment) {
console.log("addComment " + id + ", comments " + comment )
return $http.post('/posts/' + id + '/comments.json', comment);
};
o.upvoteComment = function(post, comment) {
console.log("o.upvoteComment " + post.id + ", comments " +comment.id)
return $http.put('/posts/' + post.id + '/comments/'+ comment.id + '/upvote.json')
.success(function(data){
comment.upvotes += 1;
});
};
return o;
}
]);
app.js
angular.module('flapperNews', ['ui.router', 'templates'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
})
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts/_posts.html',
controller: 'PostsCtrl',
resolve: {
post: ['$stateParams', 'posts', function($stateParams, posts) {
console.log( "$stateParams.id " +$stateParams.id)
return posts.get($stateParams.id);
}]
}
})
$urlRouterProvider.otherwise('home')
}]);
My rails version is 4.0.2
Any help would be appreciated, because I've been struggling with the code for a couple of hours :-) Anyway, I'm glad that there is Stackoverflow forum where one can ask some advice :-)
First, this has nothing to do with angular. You don't have the post defined, so add:
post = Post.find params[:post_id]
also, i think your comment belongs_to post, you should set the post as the comment's post before saving the comment, so:
#comment.post = post

How to pass variable into controller action from view?

views/vehicles/_form.html.haml
= link_to "Deactivate", "/vehicles/deactivate"
I want to pass in #vehicle in my link_to above.
How do I do this?
controllers/vehicles_controller.rb
def deactivate
#vehicle = Vehicle.find(params[:id])
#vehicle.active = 0
#vehicle.save
respond_to do |format|
format.html { redirect_to vehicles_url }
format.json { head :no_content }
end
end
To make it easy and in Rails way, you can use Rails resources:
# routes.rb
resources :vehicles do
put 'deactivate', on: :member
end
# view:
= link_to 'Deactivate', deactivate_vehicle_path(#vehicle), method: :put
Best answer already given by Marek Lipka.
There is also a way using ajax
<%= link_to 'Deactivate', javascript::void(0), :class => "deactivate" %>
Put some script:
<script>
$(".deactivate").click(function() {
$.ajax({
type: "post",
url: "/vehicles/deactivate",
data: {id: <%= #vehicle.id %>},
dataType:'script',
beforeSend: function(){
// do whatever you want
},
success: function(response){
// do whatever you want
}
});
});
</script>
This worked for me, I ended up using the Update action in my controller.
= link_to "Deactivate", vehicle_path(#vehicle, :vehicle => {:active => 0}), method: :put, :class=>'btn btn-mini'

Resources