I've a requirement where I've to return an id from Ajax call to Index method of the `HomeController' and I can pass the id to the controller with Ajax as follows:
Update 1:
ProductRating Class:
public class ProductRating
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double UserRating { get; set; }
public double? RatingCount { get; set; } //Messed up with this earlier and when Luqman told me about the model, it reminded to add this property and the rest is done
public double? Total { get; set; }
}
Ajax Call:
<script>
$(document).ready(function (response) {
if (response) {
$.ajax({
url: '#Url.Action("AddRating")',
data: { id: '#item.ProductId', rating: rate },
type: 'POST',
success: function (data) {
$('.#item.ProductId').html(data);
}
});
}
}).fail(function () {
alert("Failed to get total ratings!");
});
</script>
Controller:
[HttpPost]
public JsonResult AddRating(int id, double rating)
{
var con = new ProductRating
{
RatingCount = db.Ratings.Where(c => c.ProductId == id).Count()
};
if (con.RatingCount >= 300)
{
return Json("Rating limit already exits", JsonRequestBehavior.AllowGet);
}
else
{
Ratings aRatings = new Ratings();
aRatings.ProductId = id;
aRatings.UserRating = rating;
db.Ratings.Add(aRatings);
db.SaveChanges();
return Json(con.RatingCount, JsonRequestBehavior.AllowGet);
}
}
See in the above method, I've set the id into a session variable and would pass this variable value into the Index method as a parameter. The problem is the session variable doesn't work in the first attempt until I refresh the page. But while debugging, I can see the values and every time, it gets erased. I guess, that isn't the correct way to do so. ViewBag and Session don't seem to be helpful. My requirement is to count ratings of individual products respect to their product id and that should be returned with the same Ajax call. In the Index method, I am doing the following:
CountAll = db.Ratings.Where(c => c.ProductId == id).Count() //id - I am trying to pass from Ajax call
I hope, there would be a better way to do this and currently struggling with it.
Note: Thanks #Luqman and #Stephen Muecke. #Stephen Muecke Thanks for the call. I was making two Ajax calls for demo purpose and apology for the delay to update the post.
Here is the working sample right now:
Why you use the session to get store the id. Please use a Model or Tempdata to store that id.
Related
I have a JS file making an AJAX request to a controller. The code below works without the .Include() method but when I add it, the app runs but it doesn't populate my code. I tried running in debug mode but I'm not getting clear answers.
JS file
$(function () {
$.ajax({
type: "GET",
url: "https://localhost:5001/api/SortData/All",
contentType: "application/json; charset=utf-8",
dataType: "json"
}).done(response => {
// For each element in the response, add the submission card
$(response).each(index => {
// Formatting the submission date to be legible
var date = getFormattedDate(response[index].submitDate);
// Adding each submission card to the dashboard
$(".proposals").append(` ...some html...`)}});
API
[Route("api/[controller]/[action]")]
public class SortDataController : ControllerBase
{
[HttpGet, ActionName("All")]
public IQueryable<Proposals> GetAllProposals()
{
return _context.Proposals.Include(p => p.DeveloperName.Name);
}
}
Proposals Model
public class Proposals
{
public Proposals()
{
// Adding values to fields automatically. These fields are not on the form for users to see and update.
SubmitDate = DateTime.Now;
StatusId = 14;
AssignedTo = "johnDoe";
}
// other properties
[NotMapped]
public Users DeveloperName { get; set; }
// a few more properties
}
Users Model
public partial class Users
{
// other properties
public string Name { get; set; }
//some more properties
public IList<Proposals> Proposals { get; set; }
}
The Include is for the Navigation property only, so instead of using Include(p => p.DeveloperName.Name) try to use Include(p => p.DeveloperName) which will also contains Name when you retrieve data.
return _context.Proposals.Include(p => p.DeveloperName);
If you only would like a few specific fields,you need to select them explicitly. Something like this would work:
_context.Proposals.Include(p => p.DeveloperName)
.Select(p => new Proposals
{
StatusId = p.StatusId ,
// etc... The fields you need from Proposals go here
DeveloperName = new Users
{
Name = p.Category.Name
}
}
OK, I figured out Blue's Clues.
Turns out, I had to add
.AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
to the end of services.AddMvc() in my StartUp. Apparently, there was some looping issue that this code prevents.
I also updated my API Controller to...
[Route("api/SortData")]
public class SortDataController : Controller
{
[HttpGet("All")]
public IActionResult GetAllProposals()
{
return Ok(_context.Proposals.Include(p => p.DeveloperName).Include(p => p.Status).ToList());
}
}
From there, I was able to see the JSON response in the XHR and add it accordingly to my JS file.
I'm desperately trying to find out a solution towards solving this feature, but I haven't really got any useful help anywhere so far.
I'm working on a ASP.NET Framework MVC project, where I try to implement a simple rating feature for a Quiz class. But I'm kind of lost when it comes to the AJAX part and the Controller.
I can't figure it out how to implement this so every votes related to a specific Quiz gets registered in the Notification table.
I created a Notification class inside Model to store the results of Votes:
public class Notification
{
public int Id { get; set; }
[Required]
public Quiz Quiz { get; set; }
public int UpVoteCount { get; private set; }
public int DownVoteCount { get; private set; }
public int Score => UpVoteCount - DownVoteCount;
public void UpVote()
{
UpVoteCount++;
}
public void DownVote()
{
DownVoteCount++;
}
}
Then in the folder under Controller/API/QuizsController.cs I have implemented this action method, which I'm unsure of it's implementation. I'm kind of lost in this part! :
[HttpPost]
public IHttpActionResult Vote(int id)
{
var userId = User.Identity.GetUserId();
var quiz = _context.Guizzes.Single(m => m.Id == id && m.AuthorId == userId);
var notification = new Notification
{
Quiz = quiz, UpVote, DownVote, Score
};
_context.SaveChanges(); // save to the database
return Ok();
}
Then in the View I created MyQuiz.cshtml file and in there I have implemented the below html and AJAX code, but again this part certainly lacks some basic constructions to connect the controller to register the votes in the database.:
#model IEnumerable<VikingNotes.Models.Quiz>
#{
ViewBag.Title = "Home Page";
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">
<ul class="quizs verOffset7">
#foreach (var quiz in Model)
{
#*<li> #quiz.Creation - #quiz.Author.UserName </li>*#
<li>
<!-- Star Rating with font awesome-->
<div class="rating-star ">
#for (int i = 0; i < 5; i++)
{
<i class="fas fa-star"></i>
}
</div>
<!-- up-down arrow with font awesome-->
<div class="article">
<a href="voteUp" i class='fas fa-caret-up' style='font-size:40px;color:darkgrey'></a> <br>
<a href="votedown" class='fas fa-caret-down' style='font-size:40px;color:darkgrey'></a>
</div>
</li>
}
</ul>
#section scripts
{
<script>
jQuery(function ($) {
// Hook up our vote handlers
$("a.vote").live('click', voteClick);
function voteClick(event) {
var voteLink, voteType, item, itemId;
// Regardless of the below, we handle the event, so "consume" it
event.stopPropagation();
event.preventDefault();
// Get the anchor element, wrapped in a jQuery instance
voteLink = $(this);
// See if the vote has already been done or is in progress
if (voteLink.hasClass('done') || voteLink.hasClass('inprogress')) {
// Ignore the click, possibly tell the user why
return;
}
// Get the vote type
voteType = voteLink.hasClass('up') ? 'up' : 'down';
// Get the item we're voting on
item = voteLink.closest('.article');
// Get its ID
itemId = item.attr('data-itemid');
// If we didn't get an ID...
if (!itemId) {
// ...report error
return;
}
// Mark "in progress" and initiate the vote; action continues
// in our callbacks below
voteLink.addClass('inprogress');
$.ajax({
url: 'savevote',
data: { itemId: itemId, voteType: voteType },
type: 'POST',
success: votePostSuccess,
error: votePostError
});
// Called when the POST is successful
function votePostSuccess(response) {
// The POST worked
voteLink.removeClass('inprogress');
// Did things work on the server?
if (response === "ok") { // Or whatever
// Yes, the vote was successfully recorded
voteLink.addClass('done');
}
else {
// Report an error to the user, the server couldn't record the vote
}
}
// Called when the POST fails for some reason (HTTP errors)
function votePostError(xhr, statusText, err) {
// Not in progress anymore
voteLink.removeClass('inprogress');
// Report error to user
}
}
});
</script>
}
The Quiz Model looks like this:
public class Quiz
{
public int Id { get; set; }
public ApplicationUser User { get; set; }
[Required]
public string UserId{ get; set; }
[Required, MaxLength(200)]
public string Title { get; set; }
[Required]
[StringLength(Int32.MaxValue)]
public string Description { get; set; }
public DateTime Creation { get; set; }
public Genre Genre { get; set; }
public IEnumerable Genres { get; set; }
[Required]
public int GenreId { get; set; }
}
I also tried another approach:
I tried to adopt this tutorial [https://www.jqueryscript.net/other/Awesome-Rating-System.html][1]
with my project, but I couldn't get any results. I applied the instructions on the link and I downloaded the libraries and added to the project.
Please, some helpful souls!
(I've to add that it's my first project with mvc, razor and AJAX)
If what you want to do is simply send the star rating to the controller. You can do the following.
#for (int i = 1; i <= 5; i++)
{
<a onclick"javascript: voteClick(#i, //ID of the quiz)">
<i class="fas fa-star"></i>
</a>
}
That should send the vote rating and the id of the quiz to the function. From there you can simply use an ajax post to post it to the controller method.
EDIT: Note that you will need to change the parameters of your javascript function to:
function voteClick(vote, id)
{
//Function stuff
}
I have a grid with Web Api server side sorting, which works. I have a requirement to add filtering. When the grid's datasource sends the filtering arguments to the Web Api controller, the Filter object is always 'empty', not null. Here is my setup
Grid datasource:
var myTeamGridDataSource = new kendo.data.DataSource({
serverPaging: true,
serverSorting: true,
serverFiltering: true,
schema: {
data: "data",
total: "count"
},
pageSize: 10,
transport: {
read: {
url: baseMyTeamUrl, // web api
dataType: "json",
type: "POST",
}
},
sort: {
field: "Name",
dir: "asc"
}
});
The Action:
public HttpResponseMessage KendoGridMyTeam(GridInputModel inputModel)
{
...
}
GridInputModel.cs
public class GridInputModel
{
public int Page { get; set; }
public int PageSize { get; set; }
public int Skip { get; set; }
public int Take { get; set; }
public List<GridInputSortModel> Sort { get; set; }
public List<GridInputFilterModel> Filter { get; set; }
}
GridInputFilterModel.cs
public class GridInputFilterModel
{
public GridInputFilterModel()
{
filters = new List<GridInputFilterModel>();
}
public string field { get; set; }
public string Operator { get; set; }
public object value { get; set; }
public string logic { get; set; }
public List<GridInputFilterModel> filters { get; set; }
}
The Request Body
take=10&skip=0&page=1&pageSize=10&sort[0][field]=Name&sort[0][dir]=asc
&filter[filters][0][field]=Name
&filter[filters][0][operator]=eq
&filter[filters][0][value]=cling
&filter[logic]=and
The GridInputModel "GridInputModel inputModel" is instantiated, and the sort object is preset and that feature works just fine. But the filter, when set in the client and sent to the server is empty. After a bunch of searching, I cannot find a modern example of server side grid filtering. You may suggest that I use the Kendo.Mvc library with:
[DataSourceRequest] DataSourceRequest request
This ALSO creates a Filter object in the Request but the filters are also empty. Any suggestions? One option, that I would hesitate to use, is to use parameterMap and send the filter along on the querystring. If I have to, fine, but somebody has to have this working.
Whats happening is that your application serverside isn't deserializing the filter objects properly. You can view this if you set the parameter to an object and view the actual JSON that is sent. Your filter will be present in the string. Use Parameter map and format the filter array before its sent and you might one to overload the DataSourceClass that kendo uses. I had the same issue and had to create my own class and format the filters before sending. The issue that messing up your filters is the operator property in the filter array.
In your javascript try this:
var grid = $("#YourGridId").data("kendoGrid");
var $filter = new Array();
$filter.push({ field: "Name", operator: "eq", value: "cling" });
grid.dataSource.filter($filter);
and in your controller method you need this signature:
public ActionResult ReadData([DataSourceRequest]DataSourceRequest request){
...
}
I am using MVC instead of WebApi, but I was getting the same symptom of a null filter passed to my controller method. I, too, went down the rabbit hole of creating my own model to handle the request parameter sent to my controller method, trying to parse Kendo's filter structure, etc. I finally got it to work by following this example on Falafel's blog. Specifically, you need to make the changes listed below to your code.
In the end, the technique shown below makes it very easy to implement server processing for Kendo's DataSource (once you know all the required pieces to make it work).
Grid DataSource: You have to match all the (case-sensitive) fields coming back from the server. I left out errors, and it stopped working. You may have to change the type to get it working with WebApi.
type: "aspnetmvc-ajax",
schema: {
data: "Data",
total: "Total",
errors: "Errors",
model: {
id: "YourKeyFieldName"
}
},
The Action: Yes, you need to use Kendo's DataSourceRequest object with its attribute .
using Kendo.MVC.UI;
using Kendo.MVC.Extensions;
[HttpPost]
public ActionResult KendoGridMyTeam([DataSourceRequest] DataSourceRequest request)
{
...
return Json(yourDataVariable.ToDataSource(request));
}
I'm following Scott Allen's MVC4 course on PluralSight (I'm using MVC5 and WebAPI2 but they should be the same) and I am trying to pass an object via HTTP PUT. The model binder should bind it, but I am getting NULL for the parameter.
public HttpResponseMessage PutObjective(int id, [FromBody] Objective objective)
{
if (ModelState.IsValid && id == objective.ObjectiveID)
{
//todo: update - look up id, replace text
return Request.CreateResponse(HttpStatusCode.OK, objective);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
and in my front-end javascript I am doing the following (I'm creating an object for testing, so ignore 'objective' passed in):
var updateObjective = function (objective) {
var myobj = { "ObjectiveID": "3", "ObjectiveDescription": "test" };
return $.ajax(objectiveApiUrl + "/" + objective.ObjectiveID, {
type: "PUT",
data: myobj
});
}
My class looks like this:
public class Objective
{
public int ObjectiveID { get; private set; }
public string ObjectiveDescription { get; set; }
public Objective (int Id, string Desc)
{
this.ObjectiveID = Id;
this.ObjectiveDescription = Desc;
}
}
Any thoughts on why 'objective' in the backend is always 'null' ?
I've done what Scott Allen is doing, even tried adding in [FromBody] but no luck. $.ajax should have the correct content type by default I understand, so no need to set it.
I had Fiddler2 but I'm unsure as to what I am looking at to be honest. I can see my object as JSON being sent to the backend.
Well, if you're familiar with Model Binding you'll have seen the issue in my Objective class:
public int ObjectiveID { get; private set; }
with a private set, no instance can be created of the Objective class. To make it work, the 'private' access specifier needs to be removed.
What needs to happen really is that Objective becomes ObjectiveViewModel, and we convert what comes back to an Objective domain object (which may have more properties than we need for this screen). This can have a private set.
I have a view page, users.cshtml. And I have a JsonResult action method, jsongetusers() that returns the list of users in json format.
On users.cshtml page load, I want to get the list of users, build a table and display it. What is the best way to implement this in ASP.Net MVC using Razor? I am pretty new to MVC3 and Razor. My initial thought was to loop through the json result and build a table using javascript/jquery and append it to the DOM. But I am guessing there must be a better way to do this?
Thanks.
As Mystere Man suggested, getting just a view first and then again making an ajax call again to get the json result is unnecessary in this case. that is 2 calls to the server. I think you can directly return an HTML table of Users in the first call.
We will do this in this way. We will have a strongly typed view which will return the markup of list of users to the browser and this data is being supplied by an action method which we will invoke from our browser using an http request.
Have a ViewModel for the User
public class UserViewModel
{
public int UserID { set;get;}
public string FirstName { set;get;}
//add remaining properties as per your requirement
}
and in your controller have a method to get a list of Users
public class UserController : Controller
{
[HttpGet]
public ActionResult List()
{
List<UserViewModel> objList=UserService.GetUsers(); // this method should returns list of Users
return View("users",objList)
}
}
Assuming that UserService.GetUsers() method will return a List of UserViewModel object which represents the list of usres in your datasource (Tables)
and in your users.cshtml ( which is under Views/User folder),
#model List<UserViewModel>
<table>
#foreach(UserViewModel objUser in Model)
{
<tr>
<td>#objUser.UserId.ToString()</td>
<td>#objUser.FirstName</td>
</tr>
}
</table>
All Set now you can access the url like yourdomain/User/List and it will give you a list of users in an HTML table.
If you really need to go this way, then this is what you can do. There are probably better ways of doing it, but this is all that I have at the moment. I did no database calls, I just used dummy data. Please modify the code to fit in with your scenario. I used jQuery to populate the HTML table.
In my controller I have the an action method called GetEmployees that returns a JSON result with all the employees. This is where you would call your repository to return the users from a database:
public ActionResult GetEmployees()
{
List<User> userList = new List<User>();
User user1 = new User
{
Id = 1,
FirstName = "First name 1",
LastName = "Last name 1"
};
User user2 = new User
{
Id = 2,
FirstName = "First name 2",
LastName = "Last name 2"
};
userList.Add(user1);
userList.Add(user2);
return Json(userList, JsonRequestBehavior.AllowGet);
}
The User class looks like this:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
In your view you could have the following:
<div id="users">
<table></table>
</div>
<script>
$(document).ready(function () {
var url = '/Home/GetEmployees';
$.getJSON(url, function (data) {
$.each(data, function (key, val) {
var user = '<tr><td>' + val.FirstName + '</td></tr>';
$('#users table').append(user);
});
});
});
</script>
Regarding the code above: var url = '/Home/GetEmployees'; Home is the controller and GetEmployees is the action method that you defined above.
I hope this helps.
UPDATE:
This is how I would have done it..
I always create a view model class for a view. In this case I would have called it something like UserListViewModel:
public class UserListViewModel
{
IEnumerable<User> Users { get; set; }
}
In my controller I would populate this Users list from a call to the database returning all the users:
public ActionResult List()
{
UserListViewModel viewModel = new UserListViewModel
{
Users = userRepository.GetAllUsers()
};
return View(viewModel);
}
And in my view I would have the following:
<table>
#foreach(User user in Model.Users)
{
<tr>
<td>First Name:</td>
<td>user.FirstName</td>
</tr>
}
</table>
Add a View:
Right-Click View Folder
Click Add -> View
Click Create a strongly-typed view
Select your User class
Select List as the Scaffold template
Add a controller and action method to call the view:
public ActionResult Index()
{
var users = DataContext.GetUsers();
return View(users);
}
The normal way of doing it is:
You get the users from the database in controller.
You send a collection of users to the View
In the view to loop the list of users building the list.
You don't need a JsonResult or jQuery for this.
You can do this easily with the KoGrid plugin for KnockoutJS.
<script type="text/javascript">
$(function () {
window.viewModel = {
myObsArray: ko.observableArray([
{ id: 1, firstName: 'John', lastName: 'Doe', createdOn: '1/1/2012', birthday: '1/1/1977', salary: 40000 },
{ id: 1, firstName: 'Jane', lastName: 'Harper', createdOn: '1/2/2012', birthday: '2/1/1976', salary: 45000 },
{ id: 1, firstName: 'Jim', lastName: 'Carrey', createdOn: '1/3/2012', birthday: '3/1/1985', salary: 60000 },
{ id: 1, firstName: 'Joe', lastName: 'DiMaggio', createdOn: '1/4/2012', birthday: '4/1/1991', salary: 70000 }
])
};
ko.applyBindings(viewModel);
});
</script>
<div data-bind="koGrid: { data: myObsArray }">
Sample