jQuery: Comment list disappears on ajax form submission - php

This may be a bit confusing but I will do my best to explain what I'm trying to achieve.
I have a comment form on each post page, users can submit comments via an ajax form and the submitted comment will be displayed below the form without page reload.
All of this works, until I instruct the comments to show based on their post_id and the current post id I'm viewing.
When I do that, each post will start showing its comments only but when I try to submit a comment, the comment_list.blade.php loads an empty list of comments instead of adding the new comment to the already existing ones. I have to refresh the page manually to be able to see the comments.
I have 4 views for the comments:
leave_a_comment.blade.php (has 2 includes to comment_fields and
comment_list)
comment_fields.blade.php (the comment form)
comment_list.blade.php (the comment list)
cancel_reply.blade.php (to
close a reply window)
EDIT: I think I found the problem. in comment.js the comment_done_handler() function is not returning any data.
So if I comment out $('.comment-list').html(data.comment_list); the comment list doesn't disappear on new submission. But of course the newly added comment isn't visible until I reload the page.
EDIT 2: actually it's data.comment_list that is being returned empty in comment.js comment_done_handler(data) method.
comment.js
$(document).on('click', 'a.post-this-comment', function(){
var form_data = {
'per_page': $('.comments_per_page').val(),
'commenter_parent': $('#commenter_parent').val(),
'commenter_post': $('#commenter_post').val(),
'commenter_comment': $('#commenter_comment').val(),
'postid': $('#postid').val(),
};
var arr = [
'commenter_parent',
'commenter_post',
'commenter_comment',
'postid'
];
for (var i in arr, i < arr.length, i++) {
var elem = arr[i];
form_data[elem] = $('#' + elem).val();
}
// console.log(form_data); // something like => Object {per_page: "some_value", commenter_parent: "some_value", commenter_user_id: "some_value", commenter_comment: "some_value"}
var request = $.ajax({
type: 'POST',
url: 'post_this_comment',
data: form_data,
dataType: 'json'
});
request.done(comment_done_handler);
request.fail(comment_fail_handler);
});
function comment_done_handler(data){
console.log(data); // data is sent from server
$('.comment-content').append($('.reply-content .comment-fields'));
$('.comment-list').html(data.comment_list); // put new list
$('#captcha-image').attr('src', data.captcha); // put new captchas
clear_input_fields();
remove_error_messages(data);
hide_comment_fields();
}
Comment Model
public static function root_comments($postId) {
return self::child_comments(0, 'desc')->where('post_id', $postId);
}
public static function child_comments($parent_id, $order='asc'){
return self::where('parent_id', $parent_id)->orderBy('created_at', $order)->get();
}
public function posts() {
return $this->belongsTo('App\Post');
}
public function user() {
return $this->belongsTo('App\User');
}
CommentController
protected function comment_list($per_page, Request $request, Post $post) {
$postId = $post->id;
$root_comments = Comment::root_comments($postId);
$root_with_replies = $this->include_replies_for($root_comments);
$paginated_comments = $this->paginate($root_with_replies, $per_page, $request);
return $paginated_comments;
}
public function index(Request $request, Post $post){
$view_data = self::view_data($request, $post);
return view('eastgate.comment.leave_a_comment', $view_data);
}
public static function view_data(Request $request, Post $post) {
$instance = new Self;
$per_page = session('per_page')?session('per_page'):config('constants.per_page'); // default per page on opening the comment page
$result['per_page'] = $per_page;
$result['comments'] = $instance->comment_list($per_page, $request, $post);
$result['total_comments'] = $instance->total_comments();
$result['captcha_builder'] = $instance->captcha_builder();
return $result;
}
public function post_this_comment(Request $request, Post $post) {
$comment = new Comment;
$comment->user_id = Auth::id();;
$comment->comment = Input::get('commenter_comment');
$comment->post_id = Input::get('commenter_post');
$comment->parent_id = Input::get('commenter_parent');
if($comment->parent_id > 0){
$my_parent = Comment::find($comment->parent_id);
$comment->parents = $my_parent->parents.'.'.$comment->parent_id;
}else{
$comment->parents = '0';
}
$comment->save();
$per_page = Input::get('per_page');
$comment_list = view('eastgate.comment.comment_list')
->with('comments', $this->comment_list($per_page, $request, $post))
->with('total_comments', $this->total_comments())
->with('per_page', $per_page)
->render();
$response = array(
'status' => 'success',
'msg' => 'Comment Saved!',
'comment_list' => $comment_list,
'captcha' => $this->captcha_builder()->inline()
);
return Response::json($response);
}
comment_fields.blade.php
<div class="comment-fields">
<div class="row commenter-comment">
<div class="form-group col-md-12">
<textarea id="commenter_comment" name="commenter_comment" class="form-control comment-field" title="User's comment" placeholder="Comment Text"></textarea>
</div>
</div>
<div class="row commenter-name-email">
<input type="hidden" id="commenter_parent" name="commenter_parent" class="commenter-parent" value="0">
<input type="hidden" id="commenter_post" name="commenter_post" class="commenter-post" value="{{ $post->id }}">
</div>
<div class="row commenter-captcha">
<div class="col-md-3">
Comment
</div>
</div>
</div>
comment_list.blade.php
<div class="comment-list">
<div class="row">
<div class="col-xs-12">
<h2>{!! $total_comments !!} comment(s) </h2>
#foreach($comments as $each_comment)
<?php
$name_for_display = $each_comment->user->name;
$date_for_display = $each_comment->created_at->diffForHumans();
$parent_name_for_display = '';
if($each_comment->parent_id > 0){
$parent_comment = $each_comment->parent();
$parent_name_for_display = $parent_comment != null && $parent_comment->name
? $parent_comment->name : 'Anonymous';
$parent_name_for_display = '<span class="glyphicon glyphicon-share-alt" title="Reply to"> </span>'.$parent_name_for_display;
}
$parents_count = substr_count($each_comment->parents, '.');
$offset_length = $parents_count;
$comment_length = 12 - $offset_length;
?>
<div class="col-xs-offset-{!! $offset_length !!} col-xs-{!! $comment_length !!}">
<input type="hidden" id="postid" name="postid" class="post-id" value="{{ $each_comment->post_id }}">
<ul class="list-inline">
<li class="comment-by">{!! $name_for_display !!}</li>
#if($parents_count > 0)
<li class="reply-to">{!! $parent_name_for_display !!}</li>
#endif
<li class="separator"></li>
<li class="comment-on">{!! $date_for_display !!}</li>
</ul>
<p>{!! $each_comment->comment !!}</p>
Reply
<div class="reply-content reply{!! $each_comment->id !!}"></div>
<hr>
</div>
#endforeach
</div>
</div>
<div class="row">
<div class="col-xs-12">
{!! $comments->render() !!}
</div>
</div>
<div class="row">
<div class="col-xs-12">
Show <input type="text" name="comments_per_page" class="comments_per_page" value="{!! $per_page !!}" size="2" title="Number of comments per page"> comments per page
</div>
</div>
</div>
Please note that if I remove where('post_id', $postId) from Comment Model, it will start working and reloads the correct comment_list with the newly added comment.
I hope that makes sense, and shows the problem I'm facing.

I didn't attempt to execute the code, but this is suspicious:
public static function root_comments($postId) {
return self::child_comments(0, 'desc')->where('post_id', $postId);
}
public static function child_comments($parent_id, $order='asc'){
return self::where('parent_id', $parent_id)->orderBy('created_at', $order)->get();
}
root_comments(referenced in the controller's comment_list action) is chaining child_comments. Except child_comments does not return a query builder object, it returns a collection. You need to remove the get() call from child_comments, and instead only use get() when you're completely done with building your query.
Also, check out query scopes which is a much nicer way of accomplishing what you are trying to do.
Edit 1 - Example (using scopes):
I did not run this code, so there my be syntax errors. This is to better explain the concept.
//First, you need to create scopes on the model
public function scopeByParent($query, $parentId, $order = 'asc') {
return $query->where('parent_id', $parentId)->orderBy('created_at', $order);
}
public function scopeForPost($query, $postId) {
return $query->where('post_id', $postId);
}
//Then, change your existing methods...
public static function root_comments($postId) {
return self::byParent(0, 'desc')->forPost($postId)->get();
}
public static function child_comments($parent_id, $order = 'asc') {
return self::byParent($parent_id, $order)->get();
}
Now these are both returning collections as you'd expect. And you can reuse these scopes elsewhere when you need to retrieve the comment records.
Edit 2:
The above is part of the problem. The second part of the problem is that you were never retrieving the post that the comment was posted for. I made this change locally and it started to work:
public function post_this_comment(Request $request, Post $post) {
//...
$per_page = Input::get('per_page');
//After line 148. The $post that is created by the IoC container is just a reference to the class. You still must load the post from the DB to get the proper data from it.
$post = $post->find($request->commenter_post);
$comment_list = view('eastgate.comment.comment_list')
//...
}

EDIT:
Try this:
public static function root_comments($postId) {
return self::child_comments(0, 'desc')->where('post_id', $postId)->get();
}
-- Before edit ---
Your comment_done_handler doesn't fetch the newly created comment list.
You should make another ajax request inside, or maybe as a separate function
function comment_done_handler(data){
var data = $.ajax({
// ......
// params sent for this request to comment_list controller method
data: {
per_page: 10,
request: request,
post: post_id
},
type: 'GET',
url: 'comment_list'
// ......
}).done(function() {
// ....
}).fail(function(){
// ....
});
console.log(data); // data is retrieved from server
$('.comment-content').append($('.reply-content .comment-fields'));
$('.comment-list').html(data.comment_list); // put new list
$('#captcha-image').attr('src', data.captcha); // put new captchas
clear_input_fields();
remove_error_messages(data);
hide_comment_fields();
}

Related

I got new function for API Controller and it's not working at this

I coded update status for category. I got a new function in an API
Controller, so when I click submit it is not working.
This is solved when I move to category.update, but I can't because that function is used for something else.
web.php
Route::patch('category/{$category}', 'Admin\CategoryController#change')
->name('category.change');
Route::resource('category', 'Admin\CategoryController')
->middleware('loggedin');
This is the new function for API Controller:
public function change($category, Request $request)
{
$cate = Category::find($category);
if ($cate->category_status == 0) {
$cate->category_status = 1;
$cate->save();
} else {
$cate->category_status = 0;
$cate->save();
}
return back()->with('success', 'Success!');
}
list.blade.php
<form autocomplete="off" action="{{ route('category.change', [$cate->category_id]) }}" method="POST" enctype="multipart/form-data">
#method('PATCH')
#csrf
<button class="fa fa-eye" type="submit"></button>
</form>
First, in route, delete $
Route::patch('category/{category}', 'Admin\CategoryController#change')
->name('category.change');
Second - use route with named param
route('category.change', ['category' => $cate->category_id])
Third - in controller action Request must be first
public function change(Request $request, $category){}

Laravel dynamic dropdown base on hasMany relationships

I'm trying to create a dynamic two dynamic dropdown menus. These are the service and category selection from my database. I need to make the second dropdown menu which is the category that is dependent on the service. When I select [service_code] it will give a different bunch of categories base on the selected service.
Here is the relationship between the two models.
Service.php
public function categories()
{
return $this->hasMany('App\Models\Categories', 'service_id', 'id');
}
Categories.php
public function service()
{
return $this->belongsTo('App\Models\Service', 'service_id');
}
Here is the code in my controller
AnalysisRequestController.php
public function create()
{
$client = Client::all()->sortBy('client_name', SORT_NATURAL | SORT_FLAG_CASE)->pluck('client_name', 'id');
$services = Service::with('categories')->get()->sortBy('code', SORT_NATURAL | SORT_FLAG_CASE)->pluck('description', 'id');
$categories = Categories::with('service')->get()->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE)->pluck('name', 'id');
return view('encoder-dashboard.analysis-request.create', compact('client', 'services', 'categories'));
}
Here is the code in my view
fields.blade.php
<!-- Service Id Field -->
<div class="form-group col-sm-6">
{!! Form::label('service_id', 'Service:') !!}
{!! Form::select('service_id', $services, null, ['class' => 'form-control','required'])!!}
</div>
<!-- Categories Id Field -->
<div class="form-group col-sm-6">
{!! Form::label('category_id', 'Category:') !!}
{!! Form::select('category_id', $categories, null, ['class' => 'form-control','required'])!!}
</div>
Here is my script section for the request
<script>
$(function() {
$('select[name=service_id]').change(function() {
var url = '{{ url('service') }}' + $(this).val() + '/categories/';
$.get(url, function(data) {
var select = $('form select[name= category_id]');
select.empty();
$.each(data,function(key, value) {
select.append('<option value=' + value.id + '>' + value.name + '</option>');
});
});
});
});
</script>
Here is the defined route
Route::get('service/{service}/categories', 'ServiceController#getCategories');
And lastly here is the function in the controller
ServiceController.php
public function getCategories(Service $service)
{
return $service->categories->select('id', 'name')->get();
}
when I open the console in my browser I got this error.
GET http://127.0.0.1:8000/service/3/categories/ 404 (Not Found)
I tried to follow the answer in this link but still not working..
Appreciate if someone could help.
Thanks in advance.
The route parameter is an ID, not an object. You have to get your model instance yourself.
So getCategories() should look like this:
public function getCategories($idService)
{
$service = Service::findOrFail($idService);
return $service->categories->get(['id','name']);;
}
Edit: To avoid getting an error 500 if the id in the url is not numeric (example: http://127.0.0.1:8000/service/someText/categories/, add a simple check at the beginning of your method:
if(!is_numeric($idService)) abort(404);

Laravel - Pagination doesn't work well

I have a view with a table and pagination for this table but when i use de pagination the view is duplicated.
my controller function:
public function index()
{
$items = Items::where('active', '=', 1)->paginate(5);
if (Request::ajax())
{
return View::make('app/items.index')->with('items', $items)->render();
}
else
{
return view('app/items.index', array('items' => $items));
}
}
my jquery:
function pagination()
{
$(document).on("click", ".pagination a", function(e)
{
e.preventDefault();
alert($(this).attr('href'));
$.ajax({
url: $(this).attr('href'),
})
.done(function(data)
{
console.log(data);
$('#listSearchItems').html(data);
});
});
}
my view:
#section('content')
<div class="header"></div>
<div id="listSearchItem" class="content">
<table>
</table>
#if($items->render())
<nav class="pagination">
<a href="{{$items->previousPageUrl()}}">
<i class="material-icons">arrow_back</i>
</a>
<a href="{{$items->nextPageUrl() }}">
<i class="material-icons">arrow_forward</i>
</a>
</nav>
#endif</div>#stop
And when i click at pagination button, the view code is all duplicated within the , the table information is updated but also adds all existing html of view, duplicating code.
Anyone can help me? I already search a lot about this problem, i don't know if is the how to return the information in the controller.
Thank's
there have some problems with your code.
first why you need ajax to render your datas?
if you need use ajax render datas, your controller should be like this:
public function index()
{
$items = Items::where('active', '=', 1)->paginate(5);
if (Request::ajax())
{
$datas = $items->items();
$results = '';
foreach ($datas as $item) {
$results .="<tr><td>{$item->whateverInYourTable}</td><td>{$item->whateverInYourTable}</td></tr>"
}
return $results;
}
else
{
return view('app/items.index', array('items' => $items));
}
}
if you don't need ajax controller should be like this:
public function index()
{
$items = Items::where('active', '=', 1)->paginate(5);
return view('app.items.index', array('items' => $items));
}
view like this:
<nav class="pagination">
{!! $items->render() !!}
</nav>

Laravel 5: Call to a member function sum() on null and other persistence issues

I have a threaded-comments list each with an upvote/downvote button using jquery.upvote.js.
The voting action is done through ajax and it works, making an upvote or a downvote gets registered in the database with the correct values. Removing the vote will delete the record from the database. So technically it works as intended.
However, I have 2 problems:
The votes that the user has made do not persist after page reload which is important otherwise users won't know what they voted on.
When I add this chunk of code {{ $each_comment->commentvotes->sum('value') }} to the view to grab the sum of the votes on a given comment, I get the following error:
Call to a member function sum() on null
Routes
Route::resource('votes', 'VotesController');
Route::resource('commentvotes', 'CommentVotesController');
I'd like to point out that I've used the same method successfully on posts' votes with Vote model and VoteController.
CommentVote model
class CommentVote extends Model
{
protected $table = 'commentvotes';
protected $fillable = [
'value',
'comment_id',
'user_id'
];
public function user() {
return $this->belongsTo('App\User');
}
public function posts() {
return $this->belongsTo('App\Comment');
}
}
CommentVotesController
class CommentVotesController extends Controller
{
public function __construct() {
$this->middleware('auth', ['only' => ['create', 'edit'] ]);
}
public function store(Requests\CommentVoteRequest $request)
{
$commentId = $request->input('commentId');
$userId = $request->user()->id;
$value = $request->input('value');
// Check to see if there is an existing vote
$vote = CommentVote::whereCommentId($commentId)->whereUserId($userId)->first();
if (!$vote)
{
// First time the user is voting
CommentVote::create(['comment_id' => $commentId, 'user_id' => $userId, 'value' => $value]);
} else {
$vote->value == $value ? $vote->delete() : $vote->update(['value' => $value]);
}
// AJAX JSON RESPONSE
return response()->json(['status' => 'success',
'msg' => 'Vote has been added.']);
}
}
Javascript
$(document).ready(function() {
$('.topic').upvote();
$('.comment').upvote();
$('.vote').on('click', function (e) {
e.preventDefault();
var $button = $(this);
var postId = $button.data('post-id');
var value = $button.data('value');
$.post('http://localhost/r2/public/votes', {postId:postId, value:value}, function(data) {
if (data.status == 'success')
{
// Do something if you want..
}
}, 'json');
});
$('.commentvote').on('click', function (e) {
e.preventDefault();
var $button = $(this);
var commentId = $button.data('comment-id');
var value = $button.data('value');
$.post('http://localhost/r2/public/commentvotes', {commentId:commentId, value:value}, function(data) {
if (data.status == 'success')
{
// Do something if you want..
}
}, 'json');
});
});
Relevant part of the view comment_list.blade.php
#foreach($comments as $each_comment)
<div class="col-md-1">
<div class="upvote comment" data-comment="{{ $each_comment->id }}">
<a class="upvote commentvote {{ $each_comment->commentvotes && $each_comment->commentvotes->contains('user_id', Auth::id()) ? ($each_comment->commentvotes->where('user_id', Auth::id())->first()->value > 0 ? 'upvote-on' : null) : null}}" data-value="1" data-comment-id="{{ $each_comment->id }}"></a>
<!-- Notice how we set the sum of the votes for this post here -->
<span class="count">{{ $each_comment->votes->sum('value') }}</span>
<a class="downvote commentvote {{ $each_comment->commentvotes && $each_comment->commentvotes->contains('user_id', Auth::id()) ? ($each_comment->commentvotes->where('user_id', Auth::id())->first()->value < 0 ? 'downvote-on' : null) : null}}" data-value="-1" data-comment-id="{{ $each_comment->id }}"></a>
</div>
</div>
#endforeach
You have to define the relation inside the Comments model. Otherwise the CommentVotes will not be accessible to Comment entities.

Laravel 4, Showing field of one to one relationship data with jQuery method

I'm trying to show my CatatanObat model data in form with jQuery but, it doesn't appear at all
this is my url:
var url_data_catatanobat = '{{ route('data_catatanobat') }}';
this is my route:
Route::get('cat_obat/{id?}', array('as' => 'data_catatanobat', 'uses' => 'CatatanObatController#getData'));
this is function related in controller:
public function getData($no_ipd)
{
$cat_obat = PasienIri::getData($no_ipd);
return View::make('pages.catatan_obat', compact('cat_obat'));
}
this is function related in PasienIri Model:
public function getData($no_ipd)
{
return PasienIri::where('no_ipd', '=', $no_ipd)->first();
}
table 'pasieniri' and 'catatanobat' related in one to one relationship
in PasienIri Model:
public function catatanobat()
{
return $this->hasOne('CatatanObat', 'no_ipd');
}
in CatatanObat Model:
public function pasieniri()
{
return $this->belongsTo('PasienIri', 'no_ipd');
}
this is jQuery function related to this:
first:
$(document).ready(function(){
showAll();
});
});
second:
function showAll()
{
var no_ipd = $('#no_ipd').val();
dataCatatanObat(no_ipd);
}
third:
function dataCatatanObat(n)
{
link("#link-catatanobat");$("#catatanobat").load(url_data_catatanobat + '/' + n,function()
{
properti()
});
}
Field's data shown after I clicked a button in my form. text related to button in my form layout given id = no_ipd. And the button using id=search_link that from my jQuery function...
this is my button:
<div class="col-sm-1"><a class="btn btn-success icon-search icon-white form-control" id="search_link"></a></div>
and this is html code to show them in my layout
<div class="wrap">
<div class="grey demo-container">
<ul class="accordion" id="accordion-1">
<li id="link-catatanobat">Catatan Obat<ul>
<div class="konten" id="catatanobat">
</div>
</ul>
</li>
</ul>
</div>
i don't write pages.catatanobat here cause it's too long, but I hope you can imagine about how I load every field of the table in my form
for example:
<div class="col-sm-12"><input type="text" id="diagnosa" value="{{$pasieniri->catatanobat->diagnosa}}" class ='form-control' disabled></div>
{{$pasieniri->catatanobat->diagnosa}} <= that's my way to load the data
FYI, when I load "no_ipd" directly from CatatanObat Model, It works. I could show every field perfectly.
but now I want to show every field of CatatanObat by referencing from PasienIri. Cause PasienIri is the master table, where every "no_ipd" saved.
oh another information, when I try to show field's data by using normal controller without jQuery function, it appeared perfectly...
please.. help me...

Categories