Like button in MVC using 'Ajax.Beginform()' not able to display total likes on page - ajax

I am beginner in MVC and Ajax development and want a like button in my web, which should work like this: if the user clicks on it total likes will be incremented by 1 and if the user clicks it again (dislike) then it will decremented by 1. What I have done so far is this:
Here's the Model:
public class Like
{
public int Id { get; set; }
public virtual Video Video { get; set; }
public int VideoID { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Here is the Controller:
Post Method
[HttpPost]
public ActionResult Like(int Id, Like like)
{
if (ModelState.IsValid && User.Identity.IsAuthenticated == true)
{
like.Video = storeDB.Videos.Find(Id);
like.UserId = User.Identity.GetUserId();
var userlike = storeDB.Likes.Where(l => l.UserId == like.UserId && l.VideoID == Id);
if (userlike.Count() == 0)
{
storeDB.Likes.Add(like);
storeDB.SaveChanges();
}
else if (userlike.Count() == 1)
{
var likeDel = storeDB.Likes.FirstOrDefault(l => l.UserId == like.UserId && l.VideoID == Id);
storeDB.Likes.Remove(likeDel);
storeDB.SaveChanges();
}
List<Like> videoLikes = storeDB.Likes.Where(v => v.VideoID == Id).ToList();
int nooflikes = videoLikes.Count();
ViewBag.noLikes = nooflikes;
return Json(ViewBag.noLikes, JsonRequestBehavior.AllowGet);
}
else
{
ViewBag.Message = "Login to like this video";
return PartialView("Like", ViewBag.noLikes);
}
This is the Get method of Like:
public ActionResult Like(int id)
{
List<Like> videoLikes = storeDB.Likes.Where(v => v.VideoID == id).ToList();
int nooflikes = videoLikes.Count();
ViewBag.noLikes = nooflikes;
return Json(ViewBag.noLikes, JsonRequestBehavior.AllowGet);
}
and I have created a Partial View for this:
#if (ViewBag.Message != null)
{
<script>
$(document).ready(function () {
alert('#ViewBag.Message');
});
</script>
}
//to display total likes
<input type="text" id="likes" name="likes" value='#ViewBag.noLikes' readonly="readonly" style="border:none; background-color:transparent; width:20px" /><span style="color:black">likes</span>
and this is the main view in which I am using Ajax.BeginForm()
#using (Ajax.BeginForm("Like", "VOD", new { Id = Model.Id },
new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "likecount"
}))
{
<button type="submit" id="like" value="Like" class=" btn btn-primary btn-xs"> Like <span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> </button>
}
<span id="likecount">
#Html.Partial("Like", new Models.Like())
</span>
Now the issue is that when the page is first loaded it is not displaying total likes, and when I click on like button it returns 1 and when I click like button again it returns 0 but when I refresh the page or when I post back to another page total likes again disappear.
Can anybody help me?

Instead of doing so much code you can do above functionality in simple way.
You will required following to do so,
2 Action Methods,
1. Increment Like Count.
2. Decrease Like Count.
2 Buttons on web page
1. Like Button (Initially visible)
2. DisLike Button (Initially Invisible)
you can create a ajax request on both buttons something like bellow,
$("#btnLike").click(function () {
$.ajax({
type: "POST",
url: "Controller/LikeAction",
data: { /* Your Data */ };
success: function (result) {
// Hide Like Button & Show Dislike Button
},
error: function (error) {
alert("error = " + error);
}
});
});
Same way you can create ajax request for dislike button also. You just need to hide and show buttons.

Related

Get a boolean value from database to checkbox with option to update in .NET MVC using AJAX

I need to do the following table with .NET MVC and AJAX:
The idea is a list with users with the option to lock each of them (i am using the standard identity model):
Email | Username | LockoutEnabled
user1#domain.com | User1 | [x]
user2#domain.com | User2 | [0]
user3#domain.com | User3 | [0]
For the moment I successfully update the current status in the view from the db to the checkbox using this approach
<td>
#Html.EditorFor(modelItem => user.LockoutEnabled)
</td>
What i want to do next is:
1. Update the db on the current checkbox status without reloading the page
2. Show a notification on successful DB update from the view (no reload)
This is the controller returning the status
[Authorize(Roles = "Admin")]
public ActionResult LockUser(string id, bool status)
{
PFMDbContext db = new PFMDbContext();
db.Users.FirstOrDefault(user => user.Id == id).LockoutEnabled = status;
db.SaveChanges();
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
This is a simple job using Ajax.
Create a ViewModel that will be passed to the view.
public class ApplicationUserViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public bool IsLocked { get; set; }
}
The controller methods.
[HttpGet]
public ActionResult LockUsers()
{
var db = new PFMDbContext();
var model = db.Users
.Select(p => new ApplicationUserViewModel
{
Id = p.Id,
Name = p.Name, // Or whatever properties you have
IsLocked = p.LockoutEnabled
}).ToList();
}
[HttpPost]
public ActionResult LockUser(string id, bool isLocked)
{
var db = new PFMDbContext();
try
{
var user = db.Users.FirstOrDefault(user => user.Id == id);
user.LockoutEnabled = isLocked;
db.SaveChanges();
return Json(true);
}
catch
{
return Json(false);
}
}
In your view
#model List<ApplicationUserViewModel>
<!-- This is just an example. Use your own template. -->
#foreach (var user in Model)
{
<div>
<p>#user.Name</p>
<input id="#user.Id" type="checkbox" data-userid="#user.Id" class="js-islocked" value="#user.IsLocked" />
</div>
}
Then using javascript you can bind the select event.
$(document).ready(function () {
$(this).on('change', 'js-islocked', function () {
var userId = $(this).data('userid');
var isLocked = $(this).prop('checked');
// Use ajax to save the modification
$.ajax({
type: 'POST',
url: '/Users/LockUser',
data:{ id: userId, isLocked: isLocked },
contentType: 'application/json',
success:function(data) {
// Update a div or show a message
}
})
});
});

Custom Pagination With Last and Previous

I am working on an ecommerce site where I have to use custom pagination with Ajax and it's almost done. Here is an image that how it looks:
Now the issue - The red highlighted 5 is the last page and I am trying to show as follows:
I am not sure how to handle this but I've tried the following: (Almost all works except the last section)
#for (int i = 1; i <= Model.TotalPages; i++)
{
if (i == Model.CurrentPage)
{
<li class="#(i == Model.CurrentPage ? "active" : "")">#i</li>
}
else
{
<li><a class="page-number" href="javascript:void();">#i</a></li>
}
}
#if (Model.CurrentPage < Model.TotalPages)
{
<li>
<a class="last-page" href="javascript:void();">#Model.TotalPages</a> #*Here I am willing to show Last, but currently it shows the last page number 5*#
</li>
}
I am wondering how to show the name Last instead of the number I mean last page number. Again using partial view to show the details and pagination as follows:
#model OnlinePharmacy.Helpers.PagedData<OnlinePharmacy.Models.Product>
#{
ViewBag.Title = "Products";
List<OnlinePharmacy.Models.Product> products = ViewBag.Product;
}
<script src="~/Scripts/jquery-1.5.1.min.js"></script>
<script type="text/javascript">
$().ready(function () {
$(".page-number").live("click", function () { //This section deals the pagination
var page = parseInt($(this).html());
var url = '/Product/Products/';
url += 'page/' + page;
$.ajax({
url: '#Url.Action("Index")',
data: { "page": page },
success: function (data) {
$("#product-list").html(data);
}
});
window.history.pushState(
null,
'Products', // new page title
url // new url
)
});
});
$().ready(function () { //This section deals the pagination for the last page
$(".last-page").live("click", function () {
var page = parseInt($(this).html());
var url = '/Product/Products/';
url += 'page/' + page;
$.ajax({
url: '#Url.Action("Index")',
data: { "page": page },
success: function (data) {
$("#product-list").html(data);
}
});
window.history.pushState(
null,
'Products',
url
)
});
});
</script>
<div id="product-list">
#Html.Partial("Index")
</div>
I am sharing the models here for the products and pagination:
public class Product
{
public int ProductId { get; set; }
public int CategoryId { get; set; }
public string ProductName { get; set; }
[AllowHtml]
public string cktext { get; set; }
public double Price { get; set; }
public double Stock { get; set; }
public int Status { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
public class PagedData<T> where T : class
{
public IEnumerable<T> Data { get; set; }
public int TotalPages { get; set; }
public int CurrentPage { get; set; }
}
With the following action, I am getting the page no. using Ajax (Please see the Ajax section above):
public ActionResult Index(int page)
{
List<Product> products = aProductManager.GetAllProducts();
ViewBag.Product = products.OrderBy(p => p.ProductId).Skip(PageSize * (page - 1)).Take(PageSize).ToList();
var dataFromDb = new PagedData<Product>();
dataFromDb.Data = products.OrderBy(p => p.ProductId).Skip(PageSize * (page - 1)).Take(PageSize).ToList();
dataFromDb.TotalPages = Convert.ToInt32(Math.Ceiling((double)products.Count() / PageSize));
dataFromDb.CurrentPage = page;
return PartialView(dataFromDb);
}
Updated: I require to do the following (Requirement little bit changed) - Frankly speaking, I am not aware how to do the below but would appreciate valuable suggestion. Thanks.
If you need to change your text you should change last section like this:
#if (Model.CurrentPage < Model.TotalPages)
{
<li>
<a class="last-page" href="javascript:void();">Last</a>
</li>
}
To make your solution work fine i suggest you to move actual page number to data attribute of your <a> tag like this:
#if (Model.CurrentPage < Model.TotalPages)
{
<li>
<a data-page-number="#Model.TotalPages" class="last-page" href="javascript:void();">Last</a>
</li>
}
And then change your js line:
$(".last-page").live("click", function () {
var page = parseInt($(this).html());
//your other code
});
To this:
$(".last-page").live("click", function () {
var page = parseInt($(this).data('page-number'));
//your other code
});
Ofcource it will be better to change your whole js solution to data attributes.

Controller return incorrect data with ajax ActionLink in mvc

several days ago with searching i put an ajax button inside my page, but i didn't know the issue till now, the thing is happen, is that the result i receive is not the result from ajax redirection, but is result from first processing (though i was wonder why it do it twist)...
And what i wanna do is perform filtering through link button instead of button.
so i have two action, one is for my ajax button, and second for my index:
1.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ChooseCategoryAction(int? id)
{
Monitor.Enter(monitorObj);
// Save code here...
Session.Add("FilterCategory", id);
return RedirectToAction("Index");
}
2.
public override ActionResult Index()
{
.
.
.
int? id = (Session["FilterCategory"] != null) ? int.Parse(Session["FilterCategory"].ToString()) : (int?)null;
List<LinkEntity> filterList = null;
int catId = id ?? -1;
if (catId != -1)
{
filterList = new List<LinkEntity>();
foreach (LinkEntity linkEntity in linkList.Where(
where =>
(catId == 0 && #where.Category == null) ||
(#where.Category != null && #where.Category.Id == catId)).Select(
select =>
new LinkEntity(#select.Id, #select.Name, #select.Link, #select.Description,
#select.Category)))
{
filterList.Add(linkEntity);
}
}
else
{
filterList = linkList;
}
return View(filterList);
}
My View is like this:
<div class="split-left">
#if (ViewBag.CategoriesTags != null)
{
foreach (var cat in ViewBag.CategoriesTags)
{
<div class="link-item-category">
#using (Html.BeginForm())
{
#Ajax.ActionLink((string)cat.Name, "ChooseCategoryAction","Home", new { id = cat.Id }, new AjaxOptions { HttpMethod = "POST" }, new{#class = "category-link"})
}
</div>
}
}
</div>
When i click the link it should goes to Ajax method, then inbox, filter my data and return a view, but it first goes to inbox, then to Ajax, again to inbox, next it goes value seem to be true, but result which return is incorrect
i also before reaching to filtering step, tried following:
#Html.Hidden(((int)cat.Id).ToString())
#Html.ActionLink((string)cat.Name, "ChooseCategoryAction", "Home", null, new { #class = "category-link", onclick = "return false;" })
with following script:
<script type="text/javascript">
$(document).ready(function () {
$('.category-link').click(function () {
$(this).closest('form')[0].submit();
});
});
</script>
But it don't return to controller or don't refresh page

How to send Model value as parameter while submitting #Ajax.Beginform()

I am using #Ajax.Beginform in my View which is tightly bound to the ViewModel.
I've #Html.ListBoxFor inside my form. I add and delete items from the listbox using jQuery. Now what I am want to achieve is that onpress of the submit button, it should send full data present in the listbox regardless of which are selected. Currently it sends the list to controller if I select all the item in the listbox and press submit button. But I don't want to do that. Any idea as to how to achieve this?
Can it be sent as a form parameter.
#using (Ajax.BeginForm("SaveTextComponent", "LoanFilterEditor", new{ param1 = Model.listBoxItem}, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SUCCESS" }))
I try to accept the parameter in the controller like this
public ActionResult SaveTextComponent(TextComponentViewModel model, List<SelectListItem> list)
{
}
But list is null.. Please help.
Maybe you can use this javascript to select all items at your listbox and then, send it to controller:
function listbox_selectall(listID, isSelect) {
var listbox = document.getElementById(listID);
for(var count=0; count < listbox.options.length; count++) {
listbox.options[count].selected = isSelect;
}
}
And after that, you can call the function on your form in this way:
<script>
function submit() {
listbox_selectall('righthand_side_listbox_id', true);
return true;
}
</script>
<form onsubmit="return submit()">
...
</form>
Credits to Viral Patel blog
You can follow my example:
Model:
pulic class TextComponentViewModel {
public int[] SelectedListItem { get; set; }
public List<Item> ListItem { get; set; }
}
public class Item {
public int Id { get; set; }
public String Name { get; set; }
}
View:
#model TextComponentViewModel
#using (Ajax.BeginForm("SaveTextComponent", "LoanFilterEditor", null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SUCCESS" }, new {name = "mainForm", id = "mainForm"}))
{
#Html.ListBoxFor(model => model.SelectedListItem , new MultiSelectList(Model.ListItem, "ID", "Name"))
for(var i = 0; i < Model.ListItem.Count();i++ )
{
#Html.HiddenFor(m => m.ListItem[i].Id)
#Html.HiddenFor(m => m.ListItem[i].Name)
}
<input type = "submit" id="submitButton" />
}
Controller:
public ActionResult SaveTextComponent(TextComponentViewModel model)
{
}
Script:
$("#submitButton").click(function(){
$("#mainForm").submit();
});

Ajax paged list pass object to controller

I am working on some advanced searching functionality and I have run into a roadblock. I would like to use AJAX for my search results and use the PagedList additons to handle paging. I have an AdvancedSearchResults action which takes a search filters class and the current page. The issue is how can I pass the search Filters class into this action correctly. I am sure something I am doing just isnt organized correctly as I would think this would be fairly common. I would really appreciate any help. Thanks in advance.
Controller Action:
public ActionResult AdvancedSearchResults(AdvancedSearchFilters searchFilters, int ? page)
{
//DO STUFF
return PartialView("_SearchResults", results);
}
AdvancedSearchResults Filters:
public class AdvancedSearchFilters
{
public string SearchText { get; set; }
public List<string> SelectedTableTypes { get; set; }
public List<Guid> SelectedGenreIds { get; set; }
public List<Guid> SelectedPlatformIds { get; set; }
public int YearMax { get; set; }
public int YearMin { get; set; }
public int RatingMin { get; set; }
public int RatingMax { get; set; }
}
Search Results Partial View:
#model List<SearchResultItem>
#using PagedList
#using PagedList.Mvc
<!-- 15 Per Page -->
<h2>Search Results</h2>
#if (ViewBag.OnePageOfSearchItems.Count > 0)
{
<div class="pagination">
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfSearchItems, page => Url.Action("AdvancedSearchResults", "Search", new { searchFilters = ViewBag.AdvancedSearchFilters, page = page }),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(PagedListRenderOptions.OnlyShowFivePagesAtATime, "#main-results"))
</div>
foreach (var searchResultItem in Model)
{
<a href="#Url.Action(searchResultItem.ResultType, searchResultItem.ResultType, new { id = searchResultItem.ResultId })" class="result">
#if (searchResultItem.ProfileImageLocation != null)
{
<img src="#searchResultItem.ProfileImageLocation" alt="#searchResultItem.ResultName" />
}
<div class="result-info">
<h3>#searchResultItem.ResultName</h3>
<p>#searchResultItem.DisplayText</p>
</div>
</a>
}
<div class="pagination">
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfSearchItems, page => Url.Action("AdvancedSearchResults", "Search", new { searchFilters = ViewBag.AdvancedSearchFilters , page = page }),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(PagedListRenderOptions.OnlyShowFivePagesAtATime, "#main-results"))
</div>
}
else
{
<span>No Matches.</span>
}
Try passing the data to action method in the form of json. Try the below code which pass the json object to action method.
//// construct your json object.
var jsonObj = '{"SelectedTableTypes" : [{"1","2"}]}';
var postData = $.parseJSON(jsonObj);
$.ajax(
{
url: // your URL with Action name AdvancedSearchResults,
data: postData,
cache: false,
dataType: "json",
success: function (result) {
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});

Resources