What is CKEditor?
CkEditor is a ready-for-use HTML text editor which is designed to simplify web content creation. The best Browser-based rich text editors are usually called WYSIWYG editors. It is very easy to use in any type of project.
In this article, we are going to learn how to implement CKEditor in ASP.NET MVC. In this example, we will cover how to use it, how to post a simple blog or article & how to retrieve the data from the database.
Here, I am going to use just three tables: Technology (used to retrieve the ID of technology), Users (used to retrieve the user's information), and UserPost (used to retrieve the article details).
Step 1. First, I am going to create tables by using the script given below.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Technology](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
NULL,
CONSTRAINT [PK_Technology] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Users](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
NOT NULL,
NOT NULL,
NOT NULL,
NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UserPost](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[UserId] [bigint] NOT NULL,
[TechnologyId] [bigint] NOT NULL,
NOT NULL,
NULL,
[Contents] [nvarchar](max) NOT NULL,
[CreationDate] [datetime] NULL,
CONSTRAINT [PK_UserPost] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
Step 2. Create an empty MVC project by using Visual Studio. Download the latest version of CKEditor from here & add the folder to the Application.
Step 3. I am going to use font-awesome css, bootstrap css, bootstrap JS, and ckeditor.js. Add all the references into a BundleConfig file, as given below.
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/headerJS").Include(
"~/Scripts/External/jquery.min.js",
"~/Scripts/External/bootstrap.min.js",
"~/Scripts/ckeditor/ckeditor.js",
"~/Scripts/ckeditor/samples/js/sample.js"
));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/css/bootstrap.min.css",
"~/Content/css/custom.css",
"~/Content/css/font-awesome.css",
"~/images/favico.ico",
"~/Content/css/bootstrap-datetimepicker.min.css"
));
bundles.Add(new ScriptBundle("~/bundles/footer").Include(
"~/Scripts/External/jquery.min.js",
"~/Scripts/External/bootstrap.min.js",
"~/Scripts/External/bootstrap-datepicker.js",
"~/Scripts/External/bootstrap-datetimepicker.min.js",
"~/Scripts/Custom/Shared/Layout.js"
));
}
Now, add the reference of the above header, CSS, and footer into a _Layout. cstml.
Step 4. Create a view model ContributeViewModel, which contains some properties, as given below.
public class ContributeViewModel
{
public long UserId { get; set; }
public string TechnologyId { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
public string Contents { get; set; }
}
Step 5. Now, create controller Contribute, where action method Contribute() will return a View, which will take user inputs like article title, article description, technology name, and Content by using an editor. For technology names, we are using checkboxes.
On the submit button, we are going to save these data into a database by using a repository pattern. Read my article for reference on CRUD operation using repository pattern unit of work.
@model CoderFunda.ViewModel.Contribute.ContributeViewModel
<div class="mainContent">
<div class="col-sm-12">
<span class="pageText1">Contribute your </span><span class="pageText2">Blog</span>
</div>
<div class="col-sm-12">
<div class="col-sm-12 col-md-6">
<dl class="dl-horizontal dlCustom">
<dt>@Html.LabelFor(x => x.Title) :</dt>
<dd>
@Html.TextBoxFor(x => x.Title, new { @class = "form-control", @placeholder = "Enter Title" })
</dd>
<dt>@Html.LabelFor(x => x.TechnologyId, "Techonlogy") :</dt>
<dd>
<div class="checkbox customCheckbox" id="chkTech">
<label>
<input type="checkbox" name="blankCheckbox" id="blankCheckbox" value="Asp.NET">ASP.NET
</label>
<label>
<input type="checkbox" name="blankCheckbox" id="blankCheckbox" value="C#">C#
</label>
<label>
<input type="checkbox" name="blankCheckbox" id="blankCheckbox" value="MVC">MVC
</label>
<label>
<input type="checkbox" name="blankCheckbox" id="blankCheckbox" value="SQL">SQL
</label>
<label>
<input type="checkbox" name="blankCheckbox" id="blankCheckbox" value="Other">Other
</label>
</div>
<label id="lblContributeTechErrorMsg" style="display:none;color:red"></label>
</dd>
</dl>
</div>
<div class="col-sm-12 col-md-6">
<dl class="dl-horizontal dlCustom">
<dt>@Html.LabelFor(x => x.Description) :</dt>
<dd>@Html.TextAreaFor(x => x.Description, new { @class = "form-control", @placeholder = "Description" })</dd>
</dl>
</div>
<div class="col-sm-12">
<dl class="dl-horizontal">
<dt>@Html.LabelFor(x => x.Contents) :</dt>
<dd>
<div id="editor"></div>
<label id="lblContributeContentErrorMsg" style="display:none;color:red"></label>
</dd>
</dl>
<div class="text-center">
<a href="@Url.Action("Home", "Account")" class="customSubmit">BACK</a>
<input type="button" class="customPurple btnSubmit" id="btnSubmit" value="SUBMIT">
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
Now, write the code on the submit button and click event, which will execute an action method using AJAX call in JavaScript, as given below.
$('.btnSubmit').click(function() {
if (ValidateContribute()) {
var Title = $("#Title").val();
var TechnologyId = "";
$('input[name="blankCheckbox"]:checked').each(function() {
if (this.value != '') {
TechnologyId = this.value;
}
});
var Description = $("#Description").val();
var Content = CKEDITOR.instances['editor'].getData();
var ContributeModel = {
Title: Title,
TechnologyId: TechnologyId,
Description: Description,
Contents: Content
};
$.ajax({
url: 'Contribute/Contribute',
type: 'POST',
data: JSON.stringify(ContributeModel),
contentType: 'application/json;charset=utf-8',
success: function(data) {
if (data.success === true) {
window.location.href = "../Account/Home";
} else if (data.success === false) {
alert("Error occurred..!!");
}
},
error: function() {
alert("Error occurred..!!");
}
});
}
});
Step 6. Create an action method in the contribute controller, which will insert the data into a database. If the data is inserted successfully, it will return the JSON result as true; otherwise, it will return false. In this action method, from the GetLoggedInUserId() method, we get the current user ID of the user who is logged into an application.
[HttpPost]
public ActionResult Contribute(ContributeViewModel ContributeViewModel)
{
try
{
var technologyId = UnitoffWork.TechnologyRepository
.GetSingle(x => x.Technology1 == ContributeViewModel.TechnologyId)
.Id;
int userId = Helper.UserHelper.GetLoggedInUserId();
UserPost userPost = new UserPost
{
UserId = userId,
TechnologyId = technologyId,
Title = ContributeViewModel.Title,
Description = ContributeViewModel.Description,
Contents = ContributeViewModel.Contents,
CreationDate = DateTime.UtcNow
};
UnitoffWork.UserPostRepository.Insert(userPost);
UnitoffWork.Save();
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
catch
{
return Json(new { success = false }, JsonRequestBehavior.AllowGet);
}
}
Step 7. Create a View Model named ArticleViewModel, as given below.
public class ArticleViewModel
{
public ArticleViewModel(UserPost userPost, IEnumerable<PostLike> postLikes, List<UserComment> userComment)
{
Image = userPost.User.UserDetails.Count == 0
? string.Empty
: userPost.User.UserDetails.FirstOrDefault()?.Image ?? string.Empty;
FirstName = userPost.User.FirstName;
LastName = userPost.User.LastName;
Title = userPost.Title;
Description = userPost.Description;
PostId = userPost.Id;
UserId = userPost.UserId;
CreationDate = userPost.CreationDate.Value;
Contents = userPost.Contents;
Technology = userPost.Technology.Technology1;
}
public string Image { get; set; }
public long PostId { get; set; }
public long UserId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Contents { get; set; }
public string Technology { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime CreationDate { get; set; }
}
In an index method of controller, I am going to show a list of all the articles with titles, descriptions, author names, dates, etc. If we click on any record (the user would like to read any article from this list), then it will redirect to another action, which will show an article in detail. For this, I have created an action method, as shown below.
public ActionResult Article(long Id)
{
int userId = Helper.UserHelper.GetLoggedInUserId();
Expression<Func<UserPost, object>> parames1 = v => v.User;
Expression<Func<UserPost, object>> parames2 = v => v.User.UserDetails;
Expression<Func<UserPost, object>> parames3 = v => v.PostLikes;
Expression<Func<UserPost, object>> parames4 = v => v.UserComments;
Expression<Func<UserPost, object>>[] paramesArray = new Expression<Func<UserPost, object>>[]
{
parames1,
parames2,
parames3,
parames4
};
var userPost = UnitoffWork.UserPostRepository
.GetAllSingleTracking(x => x.Id == Id, navigationProperties: paramesArray)
.Select(u => new ArticleViewModel(
u,
u.PostLikes.Where(x => x.UserId == userId && x.PostId == Id),
u.UserComments.ToList()
));
ViewBag.Techonology = userPost.FirstOrDefault().Technology;
return View(userPost);
}
Note. Here, I have used two more tables, PostLikes & UserComments, to get the count of likes & comment details. In the above action method, I used LINQ expression and a parameter array to get the record from the database on the basis of PostId.
Step 8. Now, create a View to show article details for the above action, as given.
@model IEnumerable<CoderFunda.ViewModel.Contribute.ArticleViewModel>
<div class="row pageEffect" id="pageEffect">
<!---left content------->
<form id="frmArticle">
<div class="col-sm-8">
<div class="col-sm-12">
<span class="pageText1">Article </span>
<span class="pageText2">Detail</span>
</div>
@if (Model.Count() > 0)
{
foreach (var item in Model)
{
<div class="col-sm-12">
<div class="clearfix"></div>
<div class="articleOver">
<img src="~/Content/images/src.png" alt="" class="img-responsive">
</div>
<div class="article">
<h4 class="purpleText"><i>@item.Title</i></h4>
<span class="orangeText">
By @item.FirstName @item.LastName
<i class="fa fa-clock-o"></i> @item.CreationDate.ToShortDateString()
@if (!string.IsNullOrEmpty(@item.UserSkill))
{
@item.UserSkill
}
<br />
Technology : @ViewBag.Techonology
</span>
<br>
<b>@item.Description</b>
</div>
<div class="clearfix"></div>
<br>
<input type="hidden" id="hdnArticleContent" value="@item.Contents" />
<div id="dvshowArticleContent"></div>
@if (CoderFunda.Helper.UserHelper.IsLogedIn())
{
<a href="javascript:void(0);" style="text-decoration: none !important;">
@if (@item.IscurrentUserLiked)
{
<i class="fa fa-thumbs-up likeIcon" style="font-size:20px" data-toggle="tooltip" data-placement="top" title="Dislike" id="imgLike-1" onclick="RemoveLikes(@item.PostId,this);" data-original-title="DisLike">@item.LikeCount</i>
<i class="fa fa-comment likeIconNew" style="font-size:20px;color:#4800ff"> @item.CommentCount</i>
}
else
{
<i class="fa fa-thumbs-up likeIconNew" style="font-size:20px" data-toggle="tooltip" data-placement="top" title="Like" id="imgLike-1" onclick="AddLikes(@item.PostId,this);" data-original-title="Like">@item.LikeCount</i>
<i class="fa fa-comment likeIconNew" style="font-size:20px;color:#4800ff"> @item.CommentCount</i>
}
</a>
<div class="clearfix"></div>
}
else
{
<i class="fa fa-thumbs-up likeIcon" style="font-size:20px" data-toggle="tooltip" data-placement="top" title="Dislike" data-original-title="DisLike">@item.LikeCount</i>
}
<hr />
<div class="col-sm-12">
<div class="commentPic col-sm-4">
@if (string.IsNullOrEmpty(item.Image))
{
<img src="@Url.Content("~/Content/images/profile.jpg")" class="img-responsive" alt="" />
}
else
{
<img src="@Url.Content("~/Content/images/" + @item.Image)" class="img-responsive" alt="" />
}
</div>
<div class="row">
<div class="col-xs-8 col-sm-10" style="text-align:left">
<textarea class="form-control" id="taComment" placeholder="Comment here">
@if (CoderFunda.Helper.UserHelper.IsLogedIn())
{
<div class="col-xs-4 commentBtn col-sm-4">
<a href="javascript:void(0)" class="btn btn-primary post btnCommentSubmit" id="btnCommentSubmit" data-post-id="@item.PostId">Post</a>
</div>
}
else
{
<div class="col-xs-4 commentBtn col-sm-4">
<a href="javascript:void(0)" class="btn btn-primary post" disabled="disabled">Post</a>
</div>
}
</textarea>
<div class="clearfix"></div>
<hr>
<div class="col-sm-12">
@if (item.Comment != null)
{
foreach (var comment in item.Comment)
{
<div class="col-sm-4">
@if (string.IsNullOrEmpty(comment.Image))
{
<img src="@Url.Content("~/Content/images/profile.jpg")" class="img-responsive" alt="" style="height:30px; width:30px !important">
}
else
{
<img src="@Url.Content("~/Content/images/" + @comment.image)" class="img-responsive" alt="" style="height:30px; width:30px !important">
}
</div>
<div class="comment col-sm-8">
@comment.Comment
<br>
<small class="pull-right authorText">By @comment.UserName</small>
</div>
<div class="clearfix"></div>
<hr>
}
}
</div>
</div>
</div>
</div>
</div>
}
}
<div class="clearfix"></div>
</div>
<!---right content--------->
</form>
</div>
Step 9. Now, run the Application.
Access the Contribute action method from the URL & you will see the view given below in which the user can enter the details, as given below.
Click the Submit button. Now, open the Index method, on which you will get the article list, as given below.
If a user would like to read any article from this list, click on any article, and the result will be given below.
Summary
In this article, you learned the basics of how to implement CKEditor in ASP.NET MVC.