Introduction
A comments system is a back boon for any blog, article, and social media website so that users can share their opinions.
So, in this article, we will implement comment systems by using ASP.net core MVC, C#.net, Jquery, and Bootstrap. Moreover, we can use this comment system in PHP or other web development technologies because I have used Jquery for front end development.
I have used VS2019 and SQL Server for development.
Please follow the following steps:-
Note: This article is assuming that you have already developed the functionality of saving Users, Articles or blogs and now you are looking for comments system for your website.
If you are a front-end developer then, you can jump to jquery code
I am going used VS2019 and SQL Server and please follow the following steps:-
Create a Database (I have used SQL database, so you can use MYSQL also)
1: Article or Blog Tabel
2: Comments Table
3: Stored Procedures for Comments binding
Create Proc [dbo].[GetArticleComments]--GetArticleComments 11
(
@ArticleId bigint
)
as
select articleComm.Id,articleComm.ParentId,articleComm.CommentText,convert(varchar, articleComm.CommentedDate, 22)as CommentedDate, articleComm.CommentedById
,userProfile.FirstName as UserName
from UserArticleComments as articleComm
inner join
UsersProfile as userProfile on userProfile.UserId=articleComm.CommentedById
and articleComm.ParentId=0 and articleComm.ArticleId=@ArticleId
union all
select articleComm.Id,articleComm.ParentId,articleComm.CommentText,convert(varchar, articleComm.CommentedDate, 22)as CommentedDate, articleComm.CommentedById
,userProfile.FirstName as UserName
from UserArticleComments as articleComm
inner join
UsersProfile as userProfile on userProfile.UserId=articleComm.CommentedById
and articleComm.ParentId<>0 and articleComm.ArticleId=@ArticleId
order by ParentId asc
For backend development, I have used C#.net and entity framework core code first.
1: Create service class and Interface, like following the screenshot
and create functions for saving and fetch comments data from the database
public class ArticleCommentsList
{
public Int64 Id { get; set; }
public Int64 ParentId { get; set; }
public string CommentText { get; set; }
public string CommentedDate { get; set; }
public Int64 CommentedById { get; set; }
public string UserName { get; set; }
}
public async Task AddComment(ArticleCommentViewMoldel model)
{
try
{
UserArticleCommentsDataModel userArticleCommentsData = new UserArticleCommentsDataModel
{
CommentText = model.CommentText,
CommentedBy = await _unitOfWork.UserLoginRepository.GetByID(model.UserId),
Article=await _unitOfWork.UserCommunityArticlesRepository.GetByID(model.ArticleId),
ParentId=model.ParentId,
IsActive=true
};
await _unitOfWork.ArticleCommentsRepository.Insert(userArticleCommentsData);
var _userProfile = await _unitOfWork.UserProfileRepository.FindAllBy(c => c.User.Id == model.UserId);
ArticleCommentsList articleComments = new ArticleCommentsList {
Id= userArticleCommentsData.Id
,CommentedDate=userArticleCommentsData.CommentedDate.ToString()
,CommentText=model.CommentText
,CommentedById=model.UserId
,ParentId=userArticleCommentsData.ParentId
,UserName= _userProfile[0].FirstName
};
return articleComments;
}
catch (System.Exception)
{
throw;
}
}
You can use own code for calling SQL stored procedure
public async Task> GetArticleComments(long ArticleId)
{
IList list = new List();
await _context.LoadStoredProc("GetArticleComments")
.WithSqlParam("ArticleId", ArticleId)
.ExecuteStoredProcAsync((handler) =>
{
list = handler.ReadToList();
// do something with your results.
});
return list;
}
2: Create ArticleCommentsController controller in asp.net core project for calling service functions and paste following code
[HttpPost]
public async Task addNewComment(ArticleCommentViewMoldel model)
{
try
{
model.UserId = _claimAccessor.UserId;
var result =await _articleCommentsService.AddComment(model);
return Json(new { status = true, messsage="Thanks", result = result });
}
catch (Exception)
{
//Handle Error here..
}
return Json(new { error = true });
}
public IActionResult AddArticleCommentPartial()
{
ViewBag.Id = _claimAccessor.UserId;
return PartialView("_addComment");
}
public async Task LoadArticleCommentsAsync(long articleId)
{
return Json(await _articleCommentsService.GetArticleComments(articleId));
}
jquery code
3: Add a "_addComment.cshtml" partial view under "ArticleComments" folder and paste following code
<div class="form-group mb-4" id="ArticleComment">
<div class="row">
<div class="col-md-12">
<textarea class="form-control" style="height:80px" placeholder="Start the discussion..." id="txtArticleComment" message="comment text is a required field." required ></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right article-main-comment">
@if(ViewBag.Id!=0){
<button type="submit" onclick="AddArticleComment();" id="btnPost" disabled class="btn btn-primary">COMMENT</button>
}
else{
<b>What are your thoughts? Log in or Sign up</b>
}
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#txtArticleComment').keyup(function () {
if (this.value.length > 0) {
$("#btnPost").removeAttr("disabled");
}
else {
$("#btnPost").attr("disabled", true);
}
});
});
</script>
4: Add Comment.js file and paste the following code and also, please add toastr js from online
function AddArticleComment() {
if (formValidation('ArticleComment') === false) {
return false;
}
var dataModel = {
ArticleId: $('#ArticleId').val(),
ParentId: 0,
CommentText: $('#txtArticleComment').val()
};
postArticleComment(dataModel);
$('#txtArticleComment').val('');
}
function postArticleCommentReply(parentId)
{
var dataModel = {
ArticleId: $('#ArticleId').val(),
ParentId: parentId,
CommentText: $('#txtArticleReply'+parentId).val()
};
postArticleComment(dataModel);
}
function postArticleComment(dataModel)
{
if (dataModel.CommentText.trim().length === 0) {
toastr.error("comment box is empty, please share you thoughts", 'Warning', { timeOut: 5000 });
return;
}
$.ajax({
type: 'POST',
data: { model: dataModel },
url: '/ArticleComments/addNewComment',
success: function (data) {
console.log(JSON.stringify(data));
if (data.status) {
toastr.success(data.messsage, '', { timeOut: 1000 });
appendArticleComment(data.result);
}
else {
toastr.success(data.messsage, 'Warning', { timeOut: 5000 });
}
},
failure: function (response) {
// alert(response.responseText);
swal("Sorry!", response.responseText, "error");
},
error: function (response) {
swal("Sorry!", response.responseText, "error");
}
});
}
var list = "";
function loadArticleCommentsAjax(articleId) {
list = '';
$('#divComments').html('loding comments..');
$.ajax({
type: 'POST',
data: { articleId: articleId },
url: '/ArticleComments/LoadArticleCommentsAsync',
success: function (data) {
//console.log(JSON.stringify(data));
getNestedChildren(data, 0);
//console.log( getNestedChildren(data, 0));
//console.log(list);
$("#divComments").html(list);
},
failure: function (response) {
// alert(response.responseText);
swal("Sorry!", response.responseText, "error");
},
error: function (response) {
swal("Sorry!", response.responseText, "error");
}
});
}
function getNestedChildren(arr, parent) {
//var _button = '<div><i class="fa fa-comment-alt" ></i > <input type="button" class="replycomment" onclick="addAticleCommentBox(this,@item.Id);" value="Reply"></div>';
list += "<ul>";
for (var i in arr) {
if (arr[i].parentId === parent) {
list += '<li><div class="comment-profile">'+arr[i].userName+' '+arr[i].commentedDate+'</div>' + arr[i].commentText;
list += '<div><i class="fa fa-comment-alt fa-sm" ></i > <input type="button" class="replycomment" onclick="addAticleCommentBox(this,' + arr[i].id + ');" value="Reply"></div>';
//bind child comments
getNestedChildren(arr, arr[i].id);
list += '</li>';
list += '<div id="' + arr[i].id + '"><ul></ul></div>';
}
}
list += "</ul>";
}
function appendArticleComment(result) {
//var _li = ' <li>' + result.commentText + '</li>';
var _li='<li><div class="comment-profile">'+result.userName+' '+result.commentedDate+'</div>' +result.commentText;;
var _replyDiv = '<div><i class="fa fa-comment-alt fa-sm" ></i > <input type="button" class="replycomment" onclick="addAticleCommentBox(this,' + result.id + ');" value="Reply"></div>';
var _nextDiv = '<div id="' + result.id + '"><ul></ul ></div >';
if (result.parentId === 0) {
$('#divComments').children('ul').append( _li + _replyDiv + _nextDiv);
} else {
$('#' + result.parentId).children('ul').append(_li + _replyDiv + _nextDiv);
removeArtcleCommentBox(result.parentId);
}
}
function addAticleCommentBox(_this, _Id) {
//if comment box is already opened then following if will close that box
if ($('#divArticleCommentBox' + _Id).length)
{
removeArtcleCommentBox(_Id);
return;
}
//else add comment box near by reply button
var _strHtml = '<textarea class="form-control" style="height:80px" placeholder="Start the discussion..." id="txtArticleReply' + _Id + '" message="comment text is a required field." required="" spellcheck="true" onkeyup="articleWordCount(this,' + _Id + ')"></textarea>';
var _strButtonOne = '<div class="comment-box-btn"><button type="submit" onclick="removeArtcleCommentBox(' + _Id + ');" id="btnArticleCancel' + _Id + '" class="btn btn-link">CANCEL</button>';
var _strButtonTwo = ' <button type="submit" onclick="postArticleCommentReply(' + _Id + ');" id="btnArticleReply' + _Id + '" disabled="" class="btn btn-primary">REPLY</button></div>';
$(_this).closest('div').append('<div class="comment-box" id="divArticleCommentBox' + _Id + '">' + _strHtml + _strButtonOne + _strButtonTwo + '</div>');
}
function removeArtcleCommentBox(_Id) {
$('#divArticleCommentBox' + _Id).remove();
}
function articleWordCount(_this,_id) {
if (_this.value.length > 0) {
//alert(_this.value.length);
$("#btnArticleReply" + _id).removeAttr("disabled");
}
else {
$("#btnArticleReply" + _id).attr("disabled", true);
}
}
4: Last step, add comment.js file on the article detail page and paste following code
<input type="hidden" id="ArticleId" value="@Model.Id" />
@*<partial name="/Views/ArticleComments/_addComment.cshtml" />*@
<div id="divAddCommentBox">
</div>
<div id="divComments">
</div>
<script>
$('#divAddCommentBox').load('/ArticleComments/AddArticleCommentPartial');
//$('#divComments').load('/ArticleComments/LoadArticleComments?articleId='+@Model.Id +'&parentId=0');
loadArticleCommentsAjax(@Model.Id);
</script>
5: use the following CSS
/*COMMENTS */
#divComments ul {
position: relative;
}
#divComments ul:before {
content: "";
background: #ddd;
width: 2px;
height: 100%;
position: absolute;
left: 0;
}
#divComments > ul:first-child:before {
display:none;
}
#divComments > ul:first-child{
padding:0;
}
#divComments ul li {
display: inline-block;
}
#divComments ul .replycomment {
background: none;
border: none;
}
#divComments ul .replycomment {
background: none;
border: none;
font-size: 12px;
color: #ea9c0c;
font-weight: bold;
}
#divComments ul i.fa-comment-alt {
color: #ea9c0c;
}
#divComments ul li {
display: inline-block;
color: #666;
width:100%;
}
#divComments .comment-box button.btn,
.article-main-comment button.btn{
padding: 2px 5px;
font-size: 11px;
}
#divComments .comment-box {
margin-top: 5px;
margin-bottom:10px;
}
#divComments .comment-box textarea {
margin-bottom: 5px;
}
.comment-box-btn {
text-align: right;
}
#divComments ul:hover:before {
background: #ea9c0c;
}
#divComments ul .replycomment:focus {
border: none;
box-shadow: none;
outline: none;
}
.comment-profile {
font-size: smaller;
color: cornflowerblue;
}
now you run the project and you will get the following output
I hope you like this article, please post your comments.
Thanks!
Thanks, for reading the blog, I hope it helps you. Please share this link on your social media accounts so that others can read our valuable content. Share your queries with our expert team and get Free Expert Advice for Your Business today.
Hire me on Linkedin
My portfolio