I' still newby to this so I'll try to explain what I'm doing. Basically what I want is to load a dropdownlist depending on the value of a previous one, and I want it to load the data and appear when the other one is changed. This is the code I've written in my controller:
public ActionResult GetClassesSearch(bool ajax, string phylumID, string kingdom){
IList<TaxClass> lists = null;
int _phylumID = int.Parse(phylumID);
int _kingdom = int.Parse(kingdom);
lists = _taxon.getClassByPhylumSearch(_phylumID, _kingdom);
return Json(lists.count);
}
and this is how I call the method from the javascript function:
function loadClasses(_phylum) {
var phylum = _phylum.value;
$.getJSON("/Suspension/GetClassesSearch/",
{ ajax: true,
phylumID: phylum,
kingdom: kingdom
},
function(data) {
alert(data);
alert('no fallo')
document.getElementById("pClass").style.display = "block";
document.getElementById("sClass").options[0] = new Option("-select-", "0", true, true);
//for (i = 0; i < data.length; i++) {
// $('#sClass').addOption(data[i].classID, data[i].className);
//}
});
}
The HTML associated is:
<p id="pPhylum">
<%= Html.Label("Phylum: ") %>
<%= Html.DropDownList("sPhylum",
(SelectList)ViewData["PhyRecID"],
"--Select One--",
new { onchange = "loadClasses(this);" }
)%>
</p>
<p id="pClass">
<%= Html.Label("Class: ") %> <select id="sClass"></select>
</p>
The thing is that just like this it works, I pass the function the number of classes within a selected phylum, and it displays the pclass element, the problem gets when I try to populate the slist with data (which should contain the objects retrieved from the database), because when there is data returned by the database changing return Json(lists) instead of return Json(lists.count) I keep getting the same error:
A circular reference was detected while serializing an object of type 'SubSonic.Schema.DatabaseColumn'.
I've been going round and round debugging and making tests but I can't make it work, and it is suppossed to be a simple thing, but I'm missing something. I have commented the for loop because I'm not quite sure if that's the way you access the data, because I've not been able to make it work when it finds records. Can anyone help me?
Thanks in advance,
Victor
Finally I solved this problem. The problem was that JSON for some reason wasn't capable of displaying objects that are referenced to each other, that have problems with foreign keys and stuff. I created a new class just with the fields I needed from the database, and I populated it with a foreach statement. Then I passed that filled IList to the return Json and it worked...
Thank you all for your help,
Victor
Related
I used the following tutorial:
http://msdn.microsoft.com/en-us/library/gg508808%28VS.98%29.aspx
And everything seemed fine, but in my case, string Username always comes back null. After tonnes of research, I found everyone discovered BIND Prefixes. That would be great in many circumstances, but not this one. I should note all properties and names line up, however in my for loop, the EditorFor creates a [i].Username field and this doesn't map to any model property.
QUESTION: I think I want to map [i].Username to Username where i is any number from 0-infinity, so when it GETS, the value is passed to the Action properly. How do I do this? If this is wrong, what do I do validate this for a specific row in a table?
#for (var i = 0; i < Model.Count; i++)
{
BLAH BLAH BLAH CODE FOR BUILDING TABLE ROWS
<td>
#Html.EditorFor(modelItem => Model[i].Username)
</td>
}
Since I could technically have HUNDREDS if not THOUSANDS of records, I would rather not had a binding PREFIX for all 1000. Am I fundamentally missing something here? I am new to ASP.NET MVC and am used to WebForms so I feel like sometimes I am mixing concepts and mashing up something that is entirely wrong.
EDIT:
I fixed it by doing the following, but not sure if this is the best idea. I set the parameter equal to the FieldName without [i] prefix, but still retrieve the element with the [i] prefix. Javascript isn't my Forte so please let me know if it is horrible.
adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
var value = {
url: options.params.url,
type: options.params.type || "GET",
data: {}
},
prefix = getModelPrefix(options.element.name);
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
var paramName = fieldName.substr(fieldName.lastIndexOf(".") + 1);
var actualFieldName = appendModelPrefix(fieldName, prefix)
value.data[paramName] = function () {
return $(options.form).find(":input").filter("[name='" + escapeAttributeValue(actualFieldName) + "']").val();
};
});
setValidationValues(options, "remote", value);
});
You have not posted your code for the model or controller, but assuming you have a RemoteAttribute applied to property Username, for example
public class MyModel
{
[Remote("IsValidUserName", "Person")]
public string Username { get; set; }
}
with a method in PersonController
public JsonResult IsValidUserName(string Username)
{
....
}
and the view
#model List<Person>
...
#for (var i = 0; i < Model.Count; i++)
{
#Html.EditorFor(m => m[i].Username)
}
This will generate html such as
<input name="[0].UserName" ... />
<input name="[1].UserName" ... />
Unfortunately the remote method in jquery-validate posts back the name and value of the element so that the ajax call looks like
$.ajax({
url: '/Person/IsValidUserName',
data: { [0].UserName: 'someone#somewhere.com' },
...
which will not bind.
I have reported this as an issue at Codeplex with a possible solution. In the meantime you can modify the remote method in jquery-validate.js file as follows
remote: function(value, element, param) {
....
var data = {};
// data[element.name] = value;
data[element.name.substr(element.name.lastIndexOf(".") + 1)] = value; // add this
This will strip the prefix so that the posted data is
data: { UserName: 'someone#somewhere.com' },
and will correctly bind to the method.
Assuming the code is formatted in the following way:
View:
#for(var i = 0; i<Model.Count; i++) {
<div class="row">
#Html.EditorFor(modelItem => Model[i].Username)
</div>
}
<style>
.valid{
background: lime;
}
</style>
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
Model:
public class MyModel {
[Remote("IsValidUserName", "Validation", HttpMethod = "POST")]
public string Username { get; set; }
}
It is possible to use the automatic modelbinding to bind to the remote validation. If you were to use a list or array for this, the binding would fail while a Dictionary can catch this error.
Be aware however that the Key in the dictionary will be consistent with the id in the view (e.g. [5].Username will map to {Key: 5, Value: MyModel{Username:...}}) and won't be a default 0, hence the use of a Linq query.
Controller:
[HttpPost]
public JsonResult IsValidUserName(Dictionary<int,MyModel> Users) {
return Json(Users.First().Value.Username.Contains("User"));
}
Problem Description
I wanted to ask about how to use a list Exbando Objects in knockout.js,am using Rob Conrey's Massive and all returned results are dynamic, that's fine with me it suits my needs but when it comes to sending results to knockout i just don't know what to do with it.
Goal
Access object properties like obj.Name, obj.Brand etc...
Sample Code
View:
<div data-bind="foreach: Products">
<p>Product name: <strong data-bind="text: Name"></strong></p>
</div>
Controller:
public JsonResult GetProducts()
{
Products products = new Products();
var Model = products.GetAllProducts();
return Json(Model, JsonRequestBehavior.AllowGet);
}
The result of calling GetProducts is:
[[{"Key":"Id","Value":1},{"Key":"Name","Value":"Badass Boots"},{"Key":"Brand","Value":"Nike"},{"Key":"Description","Value":"Super cool boots that can make you fly (Not Really!)."}, etc...]]
Script File:
function ProductListViewModel() {
// Data
var self = this;
self.Products = ko.observableArray([]);
$.getJSON("/Home/GetProducts", function (data) {
self.Products(data);
});
}
JavaScript error on running the application:
Uncaught ReferenceError: Unable to parse bindings. Bindings value: text: Name Message: Name is not defined
Screen Shot 1:
Screen Shot 2:
An ExpandoObject generally speaking is for all intents and purposes, a dictionary. When serialized as JSON here, it is treated as a dictionary and becomes a collection of key/value pairs (not all serializers behave this way, but the one you're using does). It is not an object you can access members by name, you'll have to convert it into a representation where you can (or serialize it as one in the first place).
Doing the conversion is not the worst thing in the world:
function Product(item) {
var self = this;
// assuming item is an array of key/value pairs
ko.utils.arrayForEach(item, function(pair) {
// doesn't necessarily have to be mapped as an observable
self[pair.Key] = ko.observable(pair.Value);
});
}
With this, you can then map the results to your array of products:
$.getJSON("/Home/GetProducts", function (data) {
self.Products(ko.utils.arrayMap(data, function(item) {
return new Product(item);
}));
});
It looks like the problem is because you are trying to set the value of your ko.observableArray to a json array. Not sure that this will work. Typically this is how I would do it:
function ProductListViewModel() {
// Data
var self = this;
self.Products = ko.observableArray([]);
$.getJSON("/Home/GetProducts", function (data) {
ko.utils.arrayForEach(data, function(item) {
self.Products.push({
Name : item.Name
});
});
});
}
Use knockout's arrayForEach function to iterate through your json array, and push each object into your observableArray.
As in your JSON I see the sequence of Key and Value, so you have to specify the filed name which knockout has to query for to get the relative value and put it on the screen.
So change <strong data-bind="text: Name"> to <strong data-bind="text: Key"> and this should work for you.
I have Gender attribute as tiny int in Db for employee. When user create new employee i want him to choose male/female (which is working properly) by clicking on radio button. Everything is working fine (create and edit) but i want to display in form (for index, details and delete) not 1 or 2, but male/female. there should be some if statement in view but i'm not sure where to put it or how to write correct one ...
any idea?
Thanks!
this is part of code from model:
This one is from details.cshtml:
If you want to avoid having extra properties on your model or adding stuff to your viewbag you can write it inline using razor syntax like below..
<div class="display-field">
#if (model.GENDER == 0){ #Html.Raw("Male") }
#else if (model.GENDER == 1){ #Html.Raw("Female") }
</div>
That's off the top of my head so you might need to check the exact syntax but i think that's close. It will also just dump "Male" or "Female" inside the div, you might want to put it in a label or p tag at least.
This however isn't the approach I would use in a production app, throughout the code i would use a gender enum to give meaning to your bit value and extend enum to include a description that you can parse for presentation purposes.
I think from what u have said u can write it inline using the razor syntax like below in ur details.cshtml
<div class="display-field">
#if(model.GENDER ==0)
{
<label>Male</label>
}
else if(model.Gender==1)
{
<label>Female</label>
}
</div>
I think this must be enough for displaying the Gender in details page.plz comment if u need any help
Use a condition ? true : false selector
int gender = 1; // assumed male
String genderDesc = (gender == 1) ? "Male" : "Female";
You Could Make this a Drop Down field? In your controller do:
ViewBag.Gender = new[]
{
new SelectListItem { Text = "Male", Value = "1" },
new SelectListItem { Text = "Female", Value = "2" }
}.WithEmpty();
The WithEmpty() will give you a blank option or without it will select the top one.
#Html.DropDownListFor(m=> m.Gender, (Ienumarable<SelectListItem>)ViewBag.Gender);
This way the user will see Male and Female but the value will be bound to your model using the value which is 1 or 2.
I'd advise that you create a ViewModel class to represent the Employee entity in a View-friendly format decoupled from your database model and present it in a strongly typed view so you can return it from the controller.
Hint: Have the Gender property represented as a string in the ViewModel and do the conversion from byte to the string representation in your controller
You may put the code in your controller as e.g:
public ActionResult EmployeeDetails(int id)
{
//retrieve the entity from the DB
//set other employee properties here.
//I'm assuming you have set males to 2 and females to 1
...
employeeViewObject.Gender = employeeObjectFromDB.Gender.Value==2?"Male":"Female";
return View(employeeObjectFromDB);
}
Your strongly typed view will not have trouble displaying the gender while saving you the dirt of mixing code and mark-up as:
<p>model.Gender</p>
or
Html.DisplayFor(model=>model.Gender)
I have the following snippet in my razor view.
#Html.ActionLink("US", "List", "Campaigns", new { country = "US", vertical = "Insurance" }, null)
<input id="verticals" />
How do I substitute vertical = "Insurance" with the value of <input id="verticals" />?
This depends on the source of the value that is in the input with id="verticals".
if the value is coming from the server, then you'd use that.
If you would like to apply value that is, for example typed in the input (in case the input type="text"), then you'd have to use some JQuery to update the link with the typed varaible.
Example in this case:
$('#verticals').change(function(){
$('#IdOfTheLinkTag').attr('vertical',$(this).val());
});
The code is not tested, but you get the idea.
I don't think this is possible. If you want that do it with JS and build the link yourself.
Click me!
<input type="text" id="verticals">
<script>
var vert = $('#verticals').val;
$.post() //bla bla
</script>
this is just some pseudo code but you get the idea.
i hope :)
*Note:
If verticals is an hidden field from the controller / model it easier of course
#Html.ActionLink("US", "List", "Campaigns", new { country = "US", vertical = Model.HiddenProperty}, null)
I am using the Telerik MVC Grid with Ajax binding, and am having a problem passing an object to the controller to be used to filter data. I am able to pass simple data (string, int), but not a more complex object.
For instance, I can to this no problem:
.DataBinding(dataBinding => dataBinding.Ajax().Select("_CasesAjaxBinding", "Home", new {orderId = "12345"} ))
And then in my controller, handle the orderId like this:
public ActionResult _CasesAjaxBinding(string orderId)
The problem I am having is when I try to pass a more complex object (in this case the #Model) to the controller, like this (The #Model is type CaseFilterModel):
.DataBinding(dataBinding => dataBinding.Ajax().Select("_CasesAjaxBinding", "Home", new {filterSpec = #Model} ))
And then trying to handle the object, like this:
public ActionResult _CasesAjaxBinding(CaseFilterModel filterSpec)
The filterSpec parameter is always null.
Thanks in advance for any ideas!
As far as I can find on the Telerik forums this cannot be done this way. There was a similar question on there, which described exactly the same problem. When passing the model it was always null in the controller's action method.
However there is a workaround if you want to pass multiple parameters to the select method in order to filter the data, but it requires some client side coding.
I'll include a summary of that work around here, so that the answer is complete. A solemn link doesn't say much.
Assume we have a grid which displays orders items (articles) from all the orders. First make sure to hook up the client side onDataBinding event:
<%= Html.Telerik().Grid<Order>()
.Name("Grid")
.ClientEvents(events => events.OnDataBinding("onDataBinding"))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_AjaxBinding", "Grid"))
%>
In the client side event handler you need to compose your select URL. Here I'll pass two parameters, an order id (int) and a description of an article (string).
<script type="text/javascript">
function onDataBinding(e) {
var orderId = 100;
var searchText = "test";
var params = { OrderId: orderId, ArticleDescription: searchText };
var paramsStr = $.param(params);
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Grid") %>"
+ "?" + paramsStr;
var grid = $('#Grid').data('tGrid');
grid.ajax.selectUrl = selectUrl;
}
</script>
Then in your controller you can declare the select method as follows:
[GridAction]
public ActionResult _AjaxFilterBinding(AjaxFilterBindingModel model)
{
// Retrieve data here and filter it based upon the data present in the model.
var data = ...;
return View(new GridModel<Order> { Data = data });
}
The model looks like:
public class AjaxFilterBindingModel
{
public int OrderId { get; set; }
public string ArticleDescription { get; set; }
}
Passing a collection via the URL (GET) is also possible. Let's assume you want a collection of order IDs instead of just one.
The model would look like this:
public class AjaxFilterBindingModel
{
public IEnumerable<int> OrderIds { get; set; }
public string ArticleDescription { get; set; }
}
And the JavaScript would look like this:
function onDataBinding(e) {
jQuery.ajaxSettings.traditional = true;
var intArray = [1, 2, 3, 4, 5];
var params = { OrderIds: intArray, ArticleDescription: "Test" };
var paramsStr = $.param(params);
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Home") %>" + "?"
+ paramsStr;
var grid = $('#Grid').data('tGrid');
grid.ajax.selectUrl = selectUrl;
}
Remark: Don't forget to set "jQuery.ajaxSettings.traditional = true;" or the parameters will be serialized incorrectly and the ASP.NET MVC model binder will not be able to bind the integer array.
And to be complete here is the Telerik forum thread I mentioned:
http://www.telerik.com/community/forums/aspnet-mvc/grid/getting-the-model-object-in-a-custom-binding-grid-ajax-controller.aspx
And the suggested work around:
http://www.telerik.com/community/forums/aspnet-mvc/grid/code-sample-sending-additional-filters-with-ajax-binding.aspx
Also as a note, this solution is not in Razor syntax... took me forever to spot it but the line
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Home") %>" + "?" + paramsStr;
should be changed to
var selectUrl = "#Url.Action("_AjaxFilterBinding", "Home")" + "?" + paramsStr;
For Razor... I copied the code but couldn't figure out why my _AjaxFilterBinding was not being called. Just thought I'd point it out in case anyone else has this problem.
Thanks for the solution, it NOW works great :)