I want to create several instances of my model:
public int RestrictionID{get;set}
public string portefeuille{get;set;}
public int Min{get;set;}
public int Max{get;set;}
So I created a ViewModel that is:
public int ViewModelID{get;set;}
public ICollection<Restriction> Restrictions{get;set;}
But when using entity framework, and trying to create an instance of ViewModel(that is several instance of Restriction) nothing appears (no input for the user), same as if the compiler don't know how to represent the input of Restrictions. So what I want is exactly the same input(properties) to be filled severeal times say 4 times.
How can I achieve this? I need some help..
Thank you!
Does the ViewModelID serve any purpose? If not, you can just type your View to an IEnumerable of Restriction objects, like so:
#model IEnumerable<Restriction>
Then, in your view, you can enumerate the collection as you would any other:
#foreach (var restriction in Model)
{
#Html.DisplayFor(_ => restriction.portefeuille)
etc....
}
However worth noting that if you want to be able to edit and post back multiple instances of your model you will need to use an indexed loop like so:
#for (int i=0; i < Model.Count; i++)
{
#Html.EditorFor(_ => Model[i].portefeuille)
etc...
}
This allows the model binder to correctly bind the values.
If you do need the ViewModelId, then by all means use the ViewModel (it's good practice to use them anyway), and the same techniques still apply.
Your question is a little hard to understand, but I think your problem is merely that you have not initialized your Restrictions. For example:
model.Restrictions = new List<Restriction>
{
new Restriction(),
new Restriction(),
new Restriction(),
new Restriction()
}
Would give you four sets of Restriction fields, once you rendered this collection in your view. You might also what to do something like the following instead:
for (var i = 0; i < 4; i++)
{
model.Restrictions.Add(new Restriction);
}
That's a little less verbose, particularly if you were to have more than 4 items. Either way, the idea is that you need an actual Restriction instance in your collection for each set of fields you want rendered on the page.
If you need to add more dynamically, that's an entirely different issue that will require the use of JavaScript.
Related
I would like to create a more complex EditorTemplate to select a customer from a list.
I'm aware of the DropDownListFor, but I would like to show cards with customer
pictures and some data not just a regular select list.
What I would like to do:
create an EditorTemplate for customer selecting, for instance... In any POCO Class
public class X{
[Key] int Id {get;set;}
[UIHint("CustomerSelector")] int Custumer_Id {get;set;}
}
And the "CustomerSelector" Editor template be able to query all clients and show them into a rich list.
What is the problem:
It's not a good idea to add querying logic from inside a view. This is against MVC pattern.
It's not very modular to query the customer list in every controller and pass it as argument to the EditorTemplate.
How can I create this EditorTemplate without mess up with the MVC pattern nor duplicate code in every controller?
Unfortunately, there is no truly good way to handle something like this. Your are correct that it's improper for database access to happen within a view. Not only does this violate MVC, but it would also require creating an additional instance of your context in the view, when you should really have just one per request.
The alternative, as you've mentioned, would be to do the query in the controller and then pass that into your view. Honestly, this is probably your best of bad options here.
Another choice I see is to use a child action. A child action allows you to confine the logic of querying the users and passing to a view in just one place. The downside is that that you would have to handle the field naming manually, because the rendering of the child actions view will be outside the scope of the form you're building. In other words, you'd have to do something like:
#Html.Action("CustomerSelect", new { fieldName = "Customer_Id" })
That's not really ideal, either, as now you've got a string that you've got to keep track of, and you'll have to be careful about actually providing the right name. For example, if this was a collection of items, then you'd actually need to pass something like "MyCollection[" + i.ToString() + "].Customer_Id". You can see that this starts to get messy quick. For this reason alone, I'd pretty much nix this as a possible solution.
One final option is to use an HtmlHelper extension. This has the same problem as an editor template would in that you're going to have to new up an instance of your context, but it's at least better in the respect that it's not being done inside a Razor view.
I have a collection of items, name it type "A", that I want to view and edit some of its attributes in a View. I would like it to save simultaneously, however, this does not seem to work as it does not seem like it is passing anything back to the Post method.
My Controller:
[HttpPost]
public ActionResult inline(IEnumerable<A> listA)
{
for (int i = 0; i <= listA.Count(); i++ )
{
A theObj = listA.ElementAt(i);
db.SaveChanges();
} //Somehow this is returning to be Null
}
My View:
#model IEnumerable<A>
#using (Html.BeginForm())
{
#Html.EditorForModel("Multiple")
}
So far this prints out all the Id of the entries (with no formatting/line breaks, of course) for some reason.
And I am not sure how to create an editor template "Multiple", this is what I have so far:
#model Models.A
#Html.EditorFor(x => x.Id)
I'm not sure where to put it / create it, so I just made it as another view in the same folder.
Any pointers how I can make this to work, so that I can edit multiple entries of the same object in the same view and pass it back to the controller and save it? I'm a newbie to MVC, so if this seems like a really simple question, I apologize in advance. Thanks!
Like most things in programming, there's multiple ways to achieve this. If you want to go the editor template route, though, it's pretty straight-forward.
As #JamieD77 pointed out, editor templates go into Views\Shared\EditorTemplates. The most important part of that path is the EditorTemplates directory convention, though. Just as with any other view in MVC, you can override/fallback depending on where you place your view. For example, Areas\Foo\Views\Shared\EditorTemplates, will work as well, but then it's only available to the Foo area. Or, you can override it for a particular controller by placing it in Views\Foo\EditorTemplates.
Then, the name of the view should correspond with the type it's intended to be used with. In your case, the view should be named A.cshtml. You can technically specify the view name manually by either passing it to EditorFor or using something like UIHint, but it's easier and more foolproof to just rely on convention here.
Inside this view, you should create the look and feel you want to have a for a single instance of A, with all editable properties represented. Then, in your main view, you simply call Html.EditorFor on the collection property, which in your case here, is the whole model:
#Html.EditorFor(model => model)
Razor will realize it has a collection and render the editor template for each item in the collection. Importantly, because it has this context, it will also be able to generate the appropriate indexes on the field names.
If you did something like the following instead:
#foreach (var item in Model)
{
#Html.EditorFor(m => item)
}
Your field names would not be indexed, and the modelbinder would not know what to do with the posted data. If you wanted to use a loop, you would have to use indexing inside the loop:
#for (var i = 0; i < Model.Count(); i++)
{
#Html.EditorFor(m => Model[i])
}
That then gives Razor the proper context to generate appropriate field names. However, importantly, that approach requires utilizing a List<T> structure, rather than something like IEnumerable<T> or ICollection<T>.
The title may sound a little strange, so I'll try to explain my problem:
Lets say I have a class that holds some Information:
class InfoHolder
{
public int MyInfo1 {get; set;}
public int MyInfo2 { get; set; }
}
Then I have another class, that does something with an info:
class InfoGUIRepresenter
{
// Display an int in some kind of GUI
// Allow the user to change the int via the GUI
}
Now I would need two objects of the representer class, to expose my Info-class to the user: One representer for each of the two infos. To achieve that it would be nice to pass each of the properties as some kind of "parameter" to my representer classes.
But of course that's not possible in C#. Another solution would be to pass the names of the properties and then use reflection to access them - not very nice!
Is there any solution to this? Maybe some kind of architecture that addresses this kind problem?
Thanks!
One option is to pass two delegates - one for the setter and one for the getter:
var holder = new InfoHolder();
var representer = new InfoGUIRepresenter(() => holder.MyInfo1,
value => holder.MyInfo1 = value);
Then your InfoGUIRepresenter constructor would take a Func<int> and an Action<int>. (Or you could make the representer generic, for different types of property.)
You mention display "in some kind of GUI" which implies you are using a GUI framework. Surely that framework supports a well defined model for this sort of situation. Eg MVVM if you are using WPF (your info objects are models, your UI is a view and you can do binding from view to model via view model properties bound to UI element properties). I.e are you not trying to reinventing the wheel here?
It appears that AutoMapper's methods BeforeMap and AfterMap have a critical bug, which if one is attempting to iterate over a collection of the source object to populate a property of the destination object, those mapping methods execute more than once. See: Extra iterations in a foreach in an AutoMapper map
What I'm trying to do is a bit complicated, so please bear with me.
I have a EF4 many-to-many graph (Games-to-Platforms) I'm trying to build based on incoming form data. In order to build the graph, I take the raw integer ids that come from the form, and then grab the correct Platforms from my repository in order to add them to the Game's collection. You can see my attempt at doing this within BeforeMap in the link I provided above.
The problem is that I'm not sure how to proceed. I need to be able to grab a hold of the destination (Game) object in order to successfully Add the Platforms to the Game. Is something like this possible in ForMember? From what I've read, it doesn't look like a custom resolver would work for me, and I'm not sure how I'd implement a custom type converter given all the moving parts (two entities, repository).
Any ideas or suggestions?
I simply decided to make my own static mapper. Not an ideal, or even great solution, but it works. It can definitely be made more abstract, but I figure it's a band-aid until AutoMapper is fixed. My solution:
public static class GameMapper
{
public static Game Map(IGameRepository repo, AdminGameEditModel formData, Game newGame)
{
newGame.GameID = formData.GameID;
newGame.GameTitle = formData.GameTitle;
newGame.GenreID = formData.GenreID;
newGame.LastModified = DateTime.Now;
newGame.ReviewScore = (short)formData.ReviewScore;
newGame.ReviewText = formData.ReviewText;
newGame.Cons = String.Join("|", formData.Cons);
newGame.Pros = String.Join("|", formData.Pros);
newGame.Slug = formData.Slug;
if (newGame.Platforms != null && newGame.Platforms.Count > 0)
{
var oldPlats = newGame.Platforms.ToArray();
foreach (var oldPlat in oldPlats)
{
newGame.Platforms.Remove(oldPlat);
}
}
foreach (var platId in formData.PlatformIDs)
{
var plat = repo.GetPlatform(platId);
newGame.Platforms.Add(plat);
}
return newGame;
}
}
Unfortunately, I can't make the third parameter an out parameter due to my need to overwrite existing entity data during updating. Again, it's definitely not a pretty, or even good solution, but it does the job. I'm sure the OO gods will smite me at a later date.
Suppose you have a list of People A and a list of People B in a page. And these two are seperate classes in L2S, representing two different tables. Therefore, you cannot pass a single model as follows:
...
#model PeopleA
...
#foreach(var peopleA in Model.People) ...
#foreach(var peopleB in //what?)
Accordingly, I guess, I have three options to follow.
The first one is to devide the page into partial views so that I can pass a model through RenderAction helper. Since I will use these partial views only once this option does not seem attracting to me.
The second option would be to use ViewBags which I don't want to since I prefer strongly typed models.
The last one, finally, which I was about to use but wanted to ask before doing so, is to create a model as the following:
ModelMyPage.cs
public List<PeopleA> peopleA { get; set; }
public List<PeopleB> peopleB { get; set; }
MyController.cs
...
ModelMyPage m = new ModelMyPage();
m.peopleA = // query
m.peopleB = // another query
return(m);
And you got the idea. Is this the valid way to accomplish my task or is there a better c# way to do what I want?
Creating a ViewModel specific to the page, as your option 3 is the way I would do it.
I believe this is also the recommended approach.
No, there is not any better idea. In asp.net MVC, M stands for ViewModels, not the Business, Domain models. It is recommended to create ViewModels for your views and it's not reccomended to use Business Models. You should design your ViewModels to fit the need of controller interactions with Domain, and from controller to view interactions
Your first and third options seem both OK.
ad 1) "only using them once" is not a good argument-against. Use Partial views to organize views.
ad 2) Use the Viewbag to add small items like a lookup list.
ad 3) ViewModels are (becoming) common in MVC. This is probably the best approach.
I would do it the third way. Additionally, if you are going to render identical html for each person in both arrays, I would concat them before foreach:
var person in Model.PeopleA.Concat(Model.PeopleB)
I usually create a Model for the page, and name it as such, eg AccountDetailsPageModel. Then other models can be properies of this for complex pages.