I'm working on an intranet web app using ASP.NET MVC4 and Entity Framework. On one of my views, I have a list of persons which might be huge in the future. So, to make things easier, I wanted to implement an autocomplete field component using jQuery UI and Json.
The thing is that when I'm using my database to provide a source to my jQuery code, it is not working. However, when I create a variable by hard coding data, it works.
My Action :
public ActionResult AutoComplete(string term)
{
BuSIMaterial.Models.BuSIMaterialEntities db = new Models.BuSIMaterialEntities();
//var result = new [] {"A","B","C","D","E","F"}; with this, it works
var result = (from obj in db.Persons where obj.FirstName.ToLower().Contains(term.ToLower()) select obj).ToArray(); // with this, it doesn't work
return Json(result, JsonRequestBehavior.AllowGet);
}
My View :
#{
ViewBag.Title = "Auto";
}
<h2>Auto</h2>
<label for="persons">Persons : </label><input type="text" id="persons" name="persons"/>
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
#Styles.Render("~/Content/themes/base/css")
<script type="text/javascript" language="javascript">
$(document).ready(function () {
$('#persons').autocomplete({
source: '#Url.Action("AutoComplete")'
});
});
</script>
}
I tried to modify my return type (JsonResult instead of ActionResult) but nothing changes. Any idea to solve this problem?
The reason your code doesn't work is because you are attempting to send a domain model to the view which most probably contains circular references in its object graph and is not JSON serializable. To fix the problem do this:
public ActionResult AutoComplete(string term)
{
BuSIMaterial.Models.BuSIMaterialEntities db = new Models.BuSIMaterialEntities();
//var result = new [] {"A","B","C","D","E","F"}; with this, it works
var result = db
.Persons
.Where(p => p.FirstName.ToLower().Contains(term.ToLower()))
.Select(p => p.FirstName) // <!-- That's the important bit you were missing
.ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
Notice how I am projecting the Person entity to a string (taking only its FirstName). In your example you were directly taking the entire Person object which doesn't make sense for the autocomplete plugin as you need to send an array of strings to the view.
Related
I would like to create a view that would contain a different view. I've never used json before. How i can do this and How can I format the json data in the view?
My first function "Details" is to retrieve a object from the database and return view "Details.cshtml". In this view I want generates a partial view ("Stats.cshtml"). And now I want to generate a partial view with the data downloaded in the json format inside the Stats function.
Controller
public IActionResult Details(int? id = 1)
{
var person = _context.Persons.Find(id);
return View(champion);
}
public IActionResult Stats()
{
var json = new WebClient().DownloadString("url");
return Json(s);
}
View - Details.cshtml
#model Person
<div class=row">
<div class="col-sm-5"> #Model.Name </div>
<div class="col-sm-5"> #Html.Partial("Stats") </div>
</div>
View - Stats.cshtml
<h2>Stats</h2>
<div> here I want to put in a json field </div>
When I run "Stats" function from the address localhost/Home/Stats I get the result in json, but when I run "Details" function I get view "Details" and "Stats" without the json value.
to render a partial, you have many options, by your code,
the simplest one is: move your Stats code to Details action
public ActionResult Details()
{
...//prepare your person viewModel
var result = new WebClient().DownloadString("url");
var stats = JsonConvert.DeserializeObject<YourViewModel>(result);
//you have 2 options to return data
yourPersonModel.Stats=stats ; //<== you have to change PersonViewModel
//or ViewBag.Stats=stats;
return View(yourPersonModel);
}
then in Details.cshtml:
#Html.Partial("Stats", ViewBag.Stats or Model.Stats)//by your choice before.
Since Html.Action is removed, but ViewComponent comes in Core, you cannot directly call it right now, but this link will tell you how to make it back: #Html.Action in Asp.Net Core
public ActionResult Stats()
{
var result = new WebClient().DownloadString("url");
var yourViewModel = JsonConvert.DeserializeObject<YourViewModel>(result);
return PartialView(yourViewModel);
}
add the following code in your View - Stats.cshtml:
#model YourViewModel
then in Details.cshtml:
#Html.Action("Stats")
Be aware that Html.Action cannot call async actions, be careful to use it.
the next solution is to use new feature ViewComponent, here is details:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1
the last one will not be what you expected: use AJAX to load this partial page on page Details is loaded to client
I'm trying to use AngularJs to MVC5, but my problem is I don't have any Idea how to get the data from razor #model using AngularJs.
So I decide to convert the #Model to json but it's not working.
_Layout.cshtml
Razor #model
#model IEnumerable<GoActive.Models.ActiveDirectory2>
AngularJs
<script>
(function () {
var app = angular.module('app', []);
var response = #Model.AsRawJson(); // But .AsRawJson(); is not allowed in this one.
})();
</script>
Alternatively you could convert the entire server-side model into a JS object using Json.Encode
var model = #Html.Raw(Json.Encode(Model));
In case if you Model is a collection then you can use a sequential for loop to iterate over your model collection. So the complete code would be something like..
<script type="text/javascript">
var model = #Html.Raw(Json.Encode(Model));
for (var i = 0; i < model.length; i++) {
alert(model[i].myValue);
// Do Something with your model
}
</script>
try this once.
<div data-ng-init="angularModel=#razorModel"></div>
This is the Controller that returns the above-mentioned view:
public PartialViewResult Day()
{
Day model = new Day();
return PartialView("_Day", model);
}
[HttpPost]
public PartialViewResult Day(Day model)
{
return PartialView("_Day", model);
}
I wrote both the Get and the Post method after I read this question. Note that I haven't set the model in the Controller.
Then I have my View:
#model Timing.Models.Day
#{
Timing.Models.Day d = new Timing.Models.Day();
d.UserId = "some value";
}
This piece of code is working fine: when I go to retrieve, or display, d.UserId I get the right value.
Furthermore I have a script in the same view.
<script>
function getUserId() {
//some code that gets a string
return userId;
}
</script>
And also this script is working right.
I looked for a solution and this is the best I've been able to find:
#{
Timing.Models.Day d = new Timing.Models.Day();
d.UserId = Page.ClientScript.RegisterStartupScript(this.GetType(), "Getuser", "getUserId()", true);
}
But it's not working because of something that is null (I wasn't able to understand what was null though).
Is there a way to solve this problem?
For those who ask, I'm using a script because the value I want to set is the one of an html element (this is my related question).
Thanks!
Declare your variable you want in javascript
#{
var useridForjs = model.property // access model and set useridForjs
}
to use in javascript
<script>
function getUserId() {
//some code that gets a string
var myserversidevarvalue = '#useridForjs'; // here myserversidevarvalue will store value..
return userId;
}
</script>
Make it easy on yourself and inject your id into a hidden form field, easily obtainable by javascript. Add this to your view:
Html.HiddenFor(model.userId)
Then access it like this:
function getUserId() {
return $('#userId').val();
}
Can someone help me out I'm new to jQuery, I would like to know how can I pass an object through from a controller action MVC
public ActionResult UserGroupSetting()
{
UserGroupSetting setting = //some code to retrieve the settings
ViewData["Theme"] = setting.Theme;
ViewData["Image"] = setting.LogoImage;
returnView(setting);
}
What I want its to get the Theme and the logo image, in a jQuery function, that I will use to update a class in a stylesheet, The theme object contains a colour in Hex form only for now.
If someone can please help me with the call to a jquery function and how will I expose both the image and the theme objects in jquery.. Thanks
You can return the data in jSon format and let jquery make a call to your action method with getJSON method
public ActionResult UserGroupSetting()
{
var result=new { Theme="YourTheme", Logo="YourLogoURL"};
return Json(result, JsonRequestBehavior.AllowGet);
}
In your page,Call this from your javascript
$(function(){
$.getJSON('YourController/UserGroupSetting', function(data) {
alert(data.Theme);
alert(data.Logo);
});
});
if i were you , i would do this:
you can use ViewBag: in action: ViewBag.Setting = setting;
UserGroupSetting setting = //some code to retrieve the settings
ViewBag.Theme = setting.Theme;
ViewData.Image = setting.LogoImage;
returnView(setting);
then in Razor:
#{
var setting = (UserGroupSetting)ViewBag.Setting;
}
output it to javascript tag:
<script type="text/javascript">
var setting = new setting{
Theme = #setting.Theme,
Image = #setting.Image
}
</script>
This may not be the best solution, but one option I've used in the past is to create a hidden input in your view and access the value of the input in your jQuery code. This would be an example of the code you would have in your view:
<input id="someID" type="hidden" value="#TempData["Theme"]" />
I have a datatable, on that datatable i set a Html.ActionLink. When I click that action link, I want to send an id of the item to a javascript function and have a new datatable appear below with all of its content that belongs to the selected item in the datatable above. So for example if I click a students name in a table, I want all the students Grades and Test to appear below in a separate datatable. I've never worked with javascript much so I'm not sure how I can do this. If someone can please point me in the right direction or give some tips I'd appreciate it.
original first datatable:
#foreach (var item in ((List<Epic>) ViewData["selectedestimate"]))
{
<tr>
<td>
#* #Html.ActionLink(#item.Name, "action", "controller", new {id = item})*#
#item.Name
</td>
Javascript to call:
<script type="text/javascript">
function StoryClick(story) {
$.get("#Url.Action("action", "controller")", function (response) {
$('#stories').accordion({ collapsible: true });
});
}
</script>
ActionController:
public List<EpicDetails> getEpicDetails(int id)
{
return eRepository.getItemsById(id).tolist();
}
Or do I need an ActionResult?
public Actionresult Details(int id)
{
}
I realize that I'm not even close right now, but its just b/c I'm not sure what steps to take to do this.
Eventually I would make a accordion and put the table in the accordion.
In situations like this I like to actually keep the <a> the ActionLink generates, and just add JavaScript to enhance the behavior of the link. So your view wouldn't really change (I did add a class so that we can bind an event handler to it later):
#Html.ActionLink(#item.Name, "action", "controller", new {id = item, #class = "item-link" })
Then write some jQuery (it looks like you already have a dependency on jQuery. If not, I can revise the answer to use vanilla JavaScript) to bind an event handler to links with class item-link:
<script type="text/javascript">
$(document).ready(function () {
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$.get(this.href, function (response) {
// Do whatever you want with the data.
});
});
});
</script>
And, yes, your action method in the controller should return an ActionResult. It's hard for me to say what type of ActionResult you should return without actually knowing what type of data you want to consume on the client, but if you wanted to inject HTML onto the page, you could write something like this:
public ActionResult Details(int id)
{
var itemDetails = /* Get details about the item */;
return PartialView("Details", itemDetails);
}
Then in your JavaScript you would write:
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$.get(this.href, function (response) {
$("element_to_populate").html(response);
});
});
Where element_to_populate would be a selector that points to where you want to inject the HTML.
I would highly recommend using javascript templating (I prefer handlebars.js) on the client side and returning your student data as a JsonResult. This will keep your bandwidth usage to a minimum.
But, because you seem more comfortable with razor, you could use that for all your templates, return plain html from your controller/view, and then use this javascript instead
<script type="text/javascript">
$(function() {
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$("#gradesContainer").load(this.href, function (response) {
//Do whatever you want, but load will already have filled up
//#gradesContainer with the html returned from your grades view
});
});
});
</script>
In your main page, below the student list, you would just need to add
<div id="gradesContainer"></div>
Your other controller would look like this
public ActionResult TestGrades(int id) {
var model = getTestGradesModel(id);
return View(model);
}
If you were returning JSON for client-side javascript templating it would look like
public ActionResult TestGrades(int id) {
var model = getTestGradesModel(id);
return new JsonResult() {Data = model}; //no view here!
}