简体   繁体   中英

jQuery: Comment list disappears on ajax form submission

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">
            <a href="javascript:void(0)" class="btn btn-success post-this-comment">Comment</a>
        </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">&nbsp;</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>

                    <a href="javascript:void(0)" class="reply comment{!! $each_comment->id !!}" title="Reply to above comment">Reply</a>

                    <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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM