asp.net MVC autocomplete with Html.TextBoxFor - asp.net-mvc-4

I am modifying a preexisting application. I am trying to add the jquery autocomplete functionality. I have it working and calling the controller but the problem is the name attribute in the input field is "someClass.someMethod" so because I can't put this in the controller parameter like this, but still want to satisfy asp.net's Model Binding rules, what can I do?
Controller:
public JsonResult GetPs(string pN, PSModel pS)
{
List<string> pNs = null;
pNs= pS.PEntryBL.BusinessLayerPS.PS
.Where(x => x.Text.StartsWith(pN)).Select(y => y.Text).ToList();
return Json(pNs, JsonRequestBehavior.AllowGet);
}
View:
$(function () {
$("#autoCompletePNBox").autocomplete({
source: '#Url.Action("GetPs", "PS", new {pS = #Model})'
});
});
In Form:
#Html.Label("Scan PN: ", new { #class = "DFont"})
#Html.TextBoxFor(r => r.PEntryBL.PS, new { #class = "pageFont", id = "autoCompletePNBox" })

Using This Post
I was able to get it working by grabbing the value of the input field and passing it on each function call (or each time a user enters a character).
Now when the user selects the item from the autocomplete list, and selects submit then the frameworks form model binding behind the scenes will still work as originally implemented.
Here is the jquery code change:
<script type="text/javascript">
$(function () {
var src = '#Url.Action("GetPs", "PS", new {pS = #Model})'
$("#autoCompletePNBox").autocomplete({
source: function (request, response) {
$.ajax({
url: src,
dataType: "json",
data: {
pN: $("#autoCompletePNBox").val()
},
success: function (data) {
response(data);
}
});
}
});
});
I couldn't figure this out right away as my Jquery skills are not very strong. Hopefully this helps someone.

Related

MVC Failed to load resource: the server responded with a status of 500 (Internal Server Error)

I have problem with my first MVC project. I'm trying to change values of DropDownList of surnames when select DropDownList of doctor types. I think my action is working. But I cannot use result in view.
Here my codes:
$(function () {
$('select#mCB').change(function () {
var docId = $(this).val();
$.ajax({
dataType: 'json',
data: 'spec=' + docId,
method: 'GET',
url: 'LoadDoctors',
success: function (data) {
$.each(data, function (key, Docs) {
$('select#shCB').append('<option value="0">Select One</option>');
$.each(Docs, function (index, docc) {
$('select#shCB').append(
'<option value="' + docc.Id + '">' + docc.Name + '</option>');
});
});
},
error: function (docID) {
alert(' Error !');
}
});
});
});
Actions:
public static List<Docs> GetDoctorBySpec(string spec)
{
List<Docs> list = new List<Docs>();
string query = "select ID, Familiyasi, Speciality from Doktorlar where Speciality=#spec";
SqlConnection Connection = new SqlConnection(DataBase.ConnectionString);
Connection.Open();
SqlCommand cmd = new SqlCommand(query, Connection);
cmd.Parameters.Add("#spec", spec);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
list.Add(new Docs
{
Id = dr.GetString(0),
Name = dr.GetString(1)
});
}
return list;
}
enter code here
enter code here
[HttpGet]
public ActionResult LoadDoctors(string spec)
{
List<Docs> list = DoctorsService.GetDoctorBySpec(spec);
if (list == null)
{
return Json(null);
}
return Json(list);
}
And here my DropDownLists:
<div class="editor-label">
#Html.LabelFor(model => model.DoktorTuri)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.DoktorTuri, new SelectList(ViewBag.Vrachlar), new { #id = "mCB", #class = "vrachlar" })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Shifokori)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Shifokori, Enumerable.Empty<SelectListItem>(), new { #id = "shCB", #class = "vrachlar" })
</div>
Where is my mistake? Thanks for answers
A 500 (Internal Server Error) almost always means that your throwing an exception on the server. Best guess is in your case it's because your method
DoctorsService.GetDoctorBySpec(spec);
does not accept null as a parameter and the value of spec is null because your never pass it value to the controller. As stann1 has noted your ajax option needs to be
data: {spec: docId},
In addition, you do not specify the JsonRequestBehavior.AllowGet parameter which means the method will fail.
All of this can be easily determined by debugging your code, both on the server and by using your browser tools (in particular the Network tab to see what is being sent and received along with error messages)
However this is only one of many problems with your code.
Firstly, unless Docs contains only 2 properties (the values you need for the option's value attribute and display text), your unnecessarily wasting bandwidth and degrading performance by sending a whole lot of data to the client which is never used. Instead, send a collection of anonymous objects
[HttpGet]
public ActionResult LoadDoctors(string spec)
{
List<Docs> list = DoctorsService.GetDoctorBySpec(spec);
if (list == null)
{
return Json(null, JsonRequestBehavior.AllowGet);
}
var data = list.Select(d => new
{
Value = d.Id,
Text = d.Name
});
return Json(data, JsonRequestBehavior.AllowGet);
}
Next, your script will only ever generate multiple <option value="0">Select One</option> elements (one for each item in the collection) because data in $.each(data, function (key, Docs) { is your collection, and Docs is the item in the collection. Your second $.each() function will never produce anything because Docs is not a collection.
You script should be (note I have use the short version $.getJSON() rather than the longer $.ajax() and also used the default id attributes generated by the html helpers - its not clear why you would want to change the id's using new { id = "mCB" }?)
var url = '#Url.Action("LoadDoctors")'; // never hard code your url's
var shifokori = $('#Shifokori'); // cache it
$('#DoktorTuri').change(function () {
$.getJSON(url, { spec: $(this).val() }, function(data) {
if (!data) { // only necessary depending on the version of jquery
// oops
}
// clear existing options and append empty option with NULL value (not zero)
// so validation will work
shifokori.empty().append($('<option></option>').val('').text('Select One'));
$.each(data, function (index, doc) {
shifokori.append($('<option></option>').val(doc.Value).text(doc.Text));
});
}).fail(function (result) {
// oops
});
});
The data param of the call needs to be a Javascript object literal:
$.ajax({
dataType: 'json',
data: {spec: docId},
method: 'GET',
....
});
Also, try to debug your controller and use a rest extension (or Fiddler) to test the payload, you would catch such error easily yourself ;)

Updating MVC Model List using Knockout.js

I am working on an app which connects to XSockets via WCF and am able to get the data on the client side. I want to display this data using Grid.Mvc and have seen samples of using knockout.js, but I am not sure how to push this into my IEnumerable model so that I can see the View updated.
I have tried using the following code
#{
var initialData = new JavaScriptSerializer().Serialize(Model); }
$(function() {
ws = new XSockets.WebSocket("ws://127.0.0.1:4502/Book");
var vm = ko.mapping.fromJSON('#Html.Raw(initialData)');
ko.applyBindings(vm);
//Just write to the console on open
ws.bind(XSockets.Events.open, function (client) {
console.log('OPEN', client);
ws.bind('SendBook', function (books) {
jQuery.ajax({
type: "POST",
url: "#Url.Action("BooksRead", "Home")",
data: JSON.stringify(books),
dataType: "json",
contentType: "application/json",
success: function (result) {
//This doesnt work
/vm.push({Name:'Treasure Island',Author:'Robert Louis Stevenson'});
//vm.pushAll(result)
},
error: function (result){},
async: false
});
});
});
I am always receiving a null value for the parameter in the the BooksRead JsonResult method.
The model is a simple one
public class BookModel
{
public string Name {get; set;}
public string Author {get; set;}
}
I am returning a BookModel IEnumerable as my Model from the home controller on load and would want to insert new books into it as I receive them in the socket bind. This is because I am using it to generate the grid.
#Html.Grid(Model).Columns(c =>
{
c.Add(b => b.Name).Titled("Title");
c.Add(b => b.Author);
})
I would appreciate any pointers and guidance as to how I can go about achieving this.Many thanks
UPDATE
I am now able to get values in the controller action method after removing the dataType & contentType parameters from the ajax call. The controller method is as follows
public JsonResult BooksRead(string books)
{
BookModel data = JsonConvert.DeserializeObject<BookModel>(books);
List<BookModel> bookList = (List<BookModel>) TempData["books"];
if (bookList != null && data != null)
{
bookList.Add(data);
var bookString = JsonConvert.SerializeObject(bookList);
return Json(bookString);
}
return Json("");
}
I have added a vm.push call in the success handler and am passing the result value to it, but it still doesnt seem to add the new book in the Model. It seems I am doing it the wrong way as I am new to knockout js, jquery & ajax and trying to learn as I go along so please pardon my ignorance
UPDATE 2
I have made a few more changes.Like Uffe said, I have removed the Ajax call. I am adapting the StockViewModel from the StockTicker example to my BookViewModel and have added a parameter to the ctor to take in my IEnumerable model. This works & the item is added. The AddOrUpdate is working fine too & the objects are added to the collection but how can I get my model to be updated in the grid.
#{
var initialData = #Html.Raw(JsonConvert.SerializeObject(Model));
}
$(function() {
vm = new BookViewModel(#Html.Raw(initialData));
ko.applyBindings(vm);
ws = new XSockets.WebSocket("ws://127.0.0.1:4502/Book");
//Just write to the console on open
ws.bind(XSockets.Events.open, function(client) {
console.log('OPEN', client);
ws.bind('SendBook', function (msg) {
vm.AddOrUpdate(msg.book);
});
ws.bind(XSockets.Events.onError, function (err) {
console.log('ERROR', err);
});
});
});
The ViewModel is as follows
var BookViewModel = function(data) {
//this.Books = ko.observableArray(data);
this.Books = ko.observableArray(ko.utils.arrayMap(data, function(book) {
return new BookItem(book);
}));
this.AddOrUpdate = function(book) {
this.Books.push(new BookItem(book));
};
};

ASP.NET MVC4 View Model Parameter Null when Posted

I want to use a view model as input to an Edit form. Call it -
public class StudentEditViewModel
{
}
My Edit action method creates the object and passes it to the Edit view, which has #model set to StudentEditViewModel. This works fine - all data is displayed as expected.
BUT, when I post the changes (using ) things fall apart. Here is the skeleton of my action handler:
[HttpPost]
public ActionResult Edit(StudentEditViewModel student)
{
}
The problem is that "student" is null. All the online examples appear to do it this way, but I'm obviously missing something. Can anyone help me out?
I'll be happy to provide more details if necessary. Thanks in advance.
Your StudentEditViewModel needs properties (E.g public string name { get; set;}) because the StudentEditViewModel should have a property basis for it to have a value and in your view, use the basic LINQ syntax
using(Html.BeginForm())
{
#html.TextBoxFor(u => u.name)
<input type="submit"/>
}
Try adding also with a non-Data annotation ActionResult and check out the breakpoints. This was my mistake before when I tried to program. Hope I can help you.
Are you using Explicitly typed View Model? In this case you can do it by jquery as follows:
$("#btnSave").click(function (event) {
event.preventDefault();
if ($("#form1").valid()) {
var formInput = $('#form1').serializeObject();
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'your controller action url be here',
data: JSON.stringify(formInput)
})
.done(function (data, textStatus, jqXHR) {
showMsg('page-message', 'success', 'Success!', 'The item was saved.');
$('#PriorityDateId').val(data);
window.location.href = 'after save where you want to redirect(give url)';
})
.fail(function (jqXHR, textStatus, errorThrown) {
showResponse('page-message', 'error', 'Error!', jqXHR.responseText);
});
}
});
If need more info, let me know..
Your model should contain some properties like so:
model:
public class StudentEditViewModel
{
//Sample Properties
public string Name{get;set;}
public int Age {get;set;}
}
And your view should look something like this.
#model Nameofyourproject.Model.StudentEditViewModel
#using(Html.BeginForm())
{
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name) // this where you will enter a value for the Name property
#Html.LabelFor(m => m.Age)
#Html.TextBoxFor(m => m.Age) // this where you will enter a value for the Age property
<input type="submit"/> //this will submit the values to your action method.
}
you will be able to get this values now in your action method
[HttpPost]
public ActionResult Edit(StudentEditViewModel student)
{
var name = student.Name;
var age = student.Age;
}

mvc4 partial view used twice on the same view

I have a partial view for cascading drop downs i.e. Country and State. I am using the following razor view statement to render this partial view
#{Html.RenderAction("PopulateCountriesDropdown", "Helper");}
and it works just fine. Following is the complete code for the partial view that makes an asynchronous call to an action method of the controller
<pre>#model OnlineExamSystem.Models.CountryAndStateViewModel
#Html.LabelFor(m => m.c.CountryName)
#Html.DropDownListFor(m => m.c.CountryId, new SelectList(Model.cntlist, "CountryId", "CountryName"),"--Select Country--", new { #class="aha" })
#Html.Label("State")
Note: since i am unable to write HTML i.e. I have a simple html select for displaying 2nd drop down to show states with class="ddlstates"
<script type="text/javascript">
$(document).ready(function () {
$(".aha").change(function () {
var Url = '/Helper/PopulateStateDropdown';
var catId = $(this).val();
//alert(catId);
var select = $('.ddlstate');
if (catId != '') {
$.getJSON("/Helper/PopulateStateDropdown", { id: catId },
function (ddl) {
select.empty();
select.append($("<option></option>", { value: 0, text: '--Select State--' }));
$.each(ddl, function (index, itemData) {
select.append($("<option></option>", { value: itemData.Value, text: itemData.Text }));
});
});
}
else {
select.empty();
select.append($("<option></option>", { value: 0, text: '--Select State--' }));
}
});
});
as i said this works just fine. but here is the problem i.e. when I try to render the same partial view again on the same page (view) as following
#{Html.RenderAction("PopulateCountriesDropdown", "Helper");}
the rendering is ok, but changing the country in the 2nd partial view does not select the states properly. Also I have noticed that following action method is called twice for the first partial view
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult PopulateStateDropdown(string id)
{
var ls = State.GetStateByCountryId(Int32.Parse(id)).AsEnumerable();
var ddl = ls.Select(m => new SelectListItem() { Text = m.StateName, Value = m.StateId.ToString() });
return Json(ddl, JsonRequestBehavior.AllowGet);
}
and interestingly the the above method is not at all called from the 2nd partial view.
(doucment).ready() binds event to the dome elements when the page is loaded first time, it finds the elements in DOM and bind events to them, in your case as its a partial view, html is rendered dynamically on page after page is loaded so event is not binded.
Use live function for event as you html is dynamically added on the view:
Do like this:
$(".aha").live('change',function () {
});

Ajax.ActionLink parameter from DropDownList

I have the following view part:
<div class="editor-label">
#Html.LabelFor(model => model.Type)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Type, ElangWeb.Helpers.ModelHelpers.GetExerciseTypes())
</div>
I want to have a link which will generate some partialview based on my model's Type property which is an Enum (I return different partial views based on the type),
I've added the following link:
#Ajax.ActionLink("AddExerciseItem",
"AddExerciseItem",
"Exercise",
new { type=#Model.Type},
new AjaxOptions() { HttpMethod="GET", InsertionMode = InsertionMode.InsertBefore, UpdateTargetId="ExerciseItems"})
My controller action is defined as follows:
public ActionResult AddExerciseItem(ExerciseType type)
{
return PartialView("ExerciseItemOption", new ExerciseItemOption());
}
I however does not work because I have the exeption "Object reference not set to an instance of an object" for my Model. How to resolve this issue?
You could use a normal link:
#Html.ActionLink(
"AddExerciseItem",
"AddExerciseItem",
"Exercise",
null,
new { id = "add" }
)
that you could unobtrusively AJAXify:
// When the DOM is ready
$(function() {
// Subscribe to the click event of the anchor
$('#add').click(function() {
// When the anchor is clicked get the currently
// selected type from the dropdown list.
var type = $('#Type').val();
// and send an AJAX request to the controller action that
// this link is pointing to:
$.ajax({
url: this.href,
type: 'GET',
// and include the type as query string parameter
data: { type: type },
// and make sure that you disable the cache because some
// browsers might cache GET requests
cache: false,
success: function(result) {
// When the AJAX request succeeds prepend the resulting
// markup to the DOM the same way you were doing in your
// AJAX.ActionLink
$('#ExerciseItems').prepend(result);
}
});
return false;
});
});
Now your AddExerciseItem controller action could take the type parameter:
public ActionResult AddExerciseItem(string type)
{
...
}