Telerik-MVC Grid Display value of property versus edit value - model-view-controller

Hopefully this will have a simple answer.
Using MVC3, I am passing a simple list of POCO objects as a model to my view:
public partial class PeopleAddress
{
public int Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
I use the PeopleId as the FK property to the Person entity, and the Person navigational property to navigate to the object. Here is my controller for the view:
public ViewResult Index()
{
var peopleaddresses = db.PeopleAddresses.Include("Person");
return View(peopleaddresses.ToList());
}
Pretty trivial. I add the columns to the grid and normal edit modes, etc in the view, but for the PersonId property.
QUESTION ABOUT COLUMNS: How can I get the select (normal) mode to display the model.Person.Name, but keep the edit mode on editing model.PersonId? For model binding purposes, I need the HTTP post to send PersonId.
Help!

Simple
If all you need is the Person.Id when you hit edit (you're editing outside the grid or something), then it IS that simple. Your column would be:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>");
And you can get the Id for the person:
onEdit(e) {
var personId = e.dataItem['Person'].Id;
}
Full
However, if you're trying to edit in the grid using a combobox, your column should look like:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>").EditorTemplateName("PersonSelector");
your editor template:
#(Html.Telerik().ComboBox()
.Name("YourNameGoesHere")
.DataBinding(binding => binding.Ajax().Select("SelectPeopleForComboBox","Shared")))
Your client script:
onEdit(e){
$comboBox = $(e.cell).find('#Person');
if($comboBox.length > 0) {
var comboBox = $ddl.data('tComboBox');
comboBox.fill(function(){
if (e.dataItem['Person'] != null){
ddl.value(e.dataItem['Person'].Id)
}
});
}
}

Related

LINQ - query table with related data

I have a "Product" table that has related "Tegs"
public class Product
{
public Product()
{
Tegs = new List<Teg>();
}
[Key]
public int Id { get; set; }
public string ProductName { get; set; }
public virtual ICollection<Teg> Tegs { get; set; }
public virtual ICollection<Product> RelatedProducts { get; set; }
}
This is the Tegs:
public class Teg
{
public Teg()
{
}
public int Id { get; set; }
public string TegName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
Given a product ID, using LINQ I would like to retrieve all products that have at least one teg as the selected product.
I am using this code:
Product product = db.Product.Include(u => u.Tegs).Where(u => u.Id == id).Single();
List<int> tegid = product.Tegs.Select(c => c.Id).ToList();
IEnumerable<Product> relatedProducts = db.Product.Include(u => u.Tegs).Where(p => p.Tegs.Any(t => tegid.Contains(t.Id)));
Question 1:
How can I exclude from the relatedProducts the product with the ID?
Question 2:
How can nest my List inside the Linq query for the relatedProducts ?
On your first question, if I understand correctly, you want to exclude the product with the given productId from relatedProducts result list. In this case you just need to add one more condition to your Where() clause like this:
IEnumerable<Product> relatedProducts = db.Product
.Include(u => u.Tegs)
.Where(p => p.Tegs.Any(t => tegid.Contains(t.Id)) && p.Id != id);
And about your second question, I'm not quite sure what's the point here. Maybe you've meant to combine the second and third query in a single roundtrip to db?

#foreach model is null :System.NullReferenceException: Object reference not set to an instance of an object

I have this error on read my view on #foreach.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Model is null.
this is my userprofile model.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Secretarias> Secretarias { get; set; }
public DbSet<Secretarias_Direcciones> Secretarias_Direcciones { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
public int UserId { get; set; }
[Required]
public string Nombre { get; set; }
[Required]
public int SecretariaId { get; set; }
[Required]
public int DireccionId { get; set; }
[Required]
public string UserName { get; set; }
}
this is my control
public ActionResult ListaUsuarios()
{
UsersContext db = new UsersContext();
var model = from usrs in db.UserProfiles
select new { usrs.UserId, usrs.Nombre, usrs.UserName, usrs.SecretariaId, usrs.DireccionId };
ViewBag.Model = model.ToList();
return View();
}
this is my view
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Nombre)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.SecretariaId)
</td>
<td>
#Html.DisplayFor(modelItem => item.DireccionId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.UserId }) |
#Html.ActionLink("Details", "Details", new { id=item.UserId}) |
#Html.ActionLink("Delete", "Delete", new { id=item.UserId })
</td>
</tr>
}
Here are the Exception Details:
System.NullReferenceException: Object reference not set to an instance
of an object. Model is null.
You're getting the NullReferenceException because you haven't passed a strongly-typed model to your view. So in your foreach loop, Model is null. The normal way of doing that is like so:
return View(model);
The real problem is that you're mixing different concepts here. You're trying to access the properties of a model in a strongly-typed way, and yet you're defining a ViewBag.Model property, which is dynamic and has no relation whatsoever to the Model property within a view.
If you want to access your model in a strongly-typed way, and that's certainly the way I'd suggest you do it, you need to make several changes. I'd suggest you first define a view model to represent the data for a UserProfile:
public class UserProfileViewModel
{
public int UserId { get; set; }
public string Nombre { get; set; }
public int SecretariaId { get; set; }
public int DireccionId { get; set; }
public string UserName { get; set; }
}
Now, change your action to create a list of these:
public ActionResult ListaUsuarios()
{
using (UsersContext db = new UsersContext())
{
var model = from usrs in db.UserProfiles
select new UserProfileViewModel
{
UserId = usrs.UserId,
Nombre = usrs.Nombre,
UserName = usrs.UserName,
SecretariaId = usrs.SecretariaId,
DireccionId = usrs.DireccionId
};
return View(model.ToList());
}
}
Notice that I've made several changes here. Firstly, I've added the using statement to ensure UsersContext is disposed properly. Next, I've changed the query to instantiate UserProfileViewModels, instead of anonymous types. Lastly, I'm directly passing model as a parameter to View(), making sure to call ToList() on it, in order to evaluate the query before UsersContext gets disposed.
Finally, you just need to make one change to your view:
#model List<UserProfileViewModel>
#foreach (var item in Model)
{
// rest of code goes here
}
The #model directive specifies the exact model type that the view should expect to receive, thus making it strongly-typed.
Obviously - check the data types of each of the Ids that you are using to perform your joins in your database. Also ensure the database data types were mapped to the correct .Net data types.
If the data types of your ID fields appear to be ok, I would suggest narrowing down which join is causing the error. Modify you LINQ expression to have a single join and ensure there is no error. Add the other joins in one at a time until the error appears.
For your model, please check that it is being populated.
Depending on whatever is happening, it could be that your model is just null
and possibly its type might be something that it is not expected.
-- in the controller a foreach loop on the model context reference will let you see the values

Form field values aren't binding to model

I've created a simple MVC application that takes information from a form and passes it to a controller
View:
#model MvcApplication1.Models.BetChargeModel
#using (Html.BeginForm())
{
<div>
#Html.TextBoxFor(m=>m.numerators[0]) / #Html.TextBoxFor(m=>m.denominators[0])
</div>
<div>
#Html.TextBoxFor(m => m.numerators[1]) / #Html.TextBoxFor(m => m.denominators[1])
</div>
<div>
<input type="submit" value="Calculate" />
</div>
}
Controller:
public ActionResult Index()
{
BetChargeModel model = new BetChargeModel();
model.numerators = new List<double>();
model.denominators = new List<double>();
model.denominators.Add(1);
model.denominators.Add(1);
model.numerators.Add(0);
model.numerators.Add(0);
return View(model);
}
[HttpPost]
public ActionResult Index(BetChargeModel model)
{
double odds1 = model.numerators[0] / model.denominators[0];
double odds = model.numerators[1] / model.denominators[1];
//other code
}
Model:
public class BetChargeModel
{
public List<double> numerators { get; set; }
public List<double> denominators { get; set; }
public double result { get; set; }
}
When I run this and try and post back information from the View the Model is coming back empty (full of null fields and zeros). Why is the data in my Textbox's not binding to the model?
(Edit: I've changed the model properties and reference to Numerators, Denominators and Result but haven't updated those here for sake of brevity)
Based on the names numerators and denominators it looks like you have implemented the lists as fields on the model.
You should use properties instead for the model binding to work properly (which I assume that #Raphael has done).
A working model would look like this:
public class BetChargeModel
{
public List<double> numerators { get; set; }
public List<double> denominators { get; set; }
}
... and to follow to naming conventions, make sure to rename numerators to Numerators and denominators to Denominators.
However, if this does not resolve your model binding issue, please elaborate and post your model implementation :)
-- UPDATE
As you have reported that the issue still persists I will post the code I have implemented based on your own provided samples - then you can cross check to make sure everything looks right on your machine - the code shown in the following is tested and works:
Model:
public class BetChargeModel
{
public List<double> numerators { get; set; }
public List<double> denominators { get; set; }
}
View:
#model Project.Models.BetChargeModel
#using (Html.BeginForm())
{
<div>
#Html.TextBoxFor(m=>m.numerators[0]) / #Html.TextBoxFor(m=>m.denominators[0])
</div>
<div>
#Html.TextBoxFor(m => m.numerators[1]) / #Html.TextBoxFor(m => m.denominators[1])
</div>
<div>
<input type="submit" value="Calculate" />
</div>
}
Controller:
public ActionResult Index()
{
var model = new BetChargeModel
{
numerators = new List<double> {0, 0},
denominators = new List<double> {1, 1}
};
return View(model);
}
[HttpPost]
public ActionResult Index(BetChargeModel model)
{
var odds1 = model.numerators[0] / model.denominators[0];
var odds = model.numerators[1] / model.denominators[1];
return null;
}

DataTypeName in DisplayTemplate is always null

I am trying to access the DataTypeName from the ModelMetadata in a custom DisplayTemplate, but it is always null. My goal is to make a decimal.cshtml template that looks for the DataTypeName and if it's equal to Currency format it as currency. Otherwise display the decimal like normal.
Here's an example of what I have now:
The Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
In a view that is strongly typed to MyModel I have a call like this: #Html.DisplayFor(m => m.foo)
The Display template (~/Views/Shared/DisplayTemplates/decimal.cshtml)
#model decimal?
#{
var type = ViewData.ModelMetadata.DataTypeName; // This is always null
}
I know that it's using my DisplayTemplate like it's supposed to, but I would expect the DataTypeName to not be null in this case since I have the DataType attribute on the model. Is there any way to access the DataType in a display template like this?
Strange, it should work. I am unable to reproduce. Here's what I tried in a new application:
Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyModel
{
foo = 12.5m
});
}
}
View (~/Views/Home/Index.cshtml):
#model MyModel
#Html.DisplayFor(x => x.foo)
Display template (~/Views/Home/DisplayTemplates/decimal.cshtml):
#model decimal?
#ViewData.ModelMetadata.DataTypeName
When I run the application it prints Currency as expected.

How to make AutoMapper call a method after mapping a ViewModel

Is it possbile to make AutoMapper call a method after mapping source and destination?
My ViewModel looks like this:
public class ShowCategoriesViewModel
{
public int category_id { get; set; }
public string category_name { get; set; }
public List<MvcApplication3.Models.Category> SubCategories { get; set; }
public void Sort()
{
SubCategories.Sort(new CompareCategory());
}
}
And my Controller looks like this:
public ActionResult Index()
{
var category = db.Category.Where(y => y.parrent_id == null).ToList();
Mapper.CreateMap<Category, ShowCategoriesViewModel>().
ForMember(dest => dest.SubCategories, opt => opt.MapFrom(origin => origin.Category1));
List<ShowCategoriesViewModel> scvm = Mapper.Map<List<Category>, List<ShowCategoriesViewModel>>(category);
foreach (ShowCategoriesViewModel model in scvm)
{
model.Sort();
}
return View(scvm);
}
I would like to have AutoMapper call the Sort() method, instead of doing a foreach loop. Is this possible?
I think you can use .AfterMap here
Mapper.CreateMap<Category, ShowCategoriesViewModel>()
.ForMember(dest => dest.SubCategories, opt => opt.MapFrom(origin => origin.Category1))
.AfterMap((c,s) => s.Sort());

Resources