ASP.net MVC - Binding a form value to a different name - c#

I have a View that has the ability to both create and edit an item that I have separated into partial views:
MainEditView.cshtml
_CreateChildDialog.cshtml
_EditChildDialog.cshtml
I have separate ViewModels for both the Create and Child items:
public class CreateChildViewModel
{
public string ItemText { get; set; }
}
public class EditChildViewModel
{
public string ItemText { get; set; }
}
Since the partial views for the Edit and Create dialog boxes will both be rendered on the same page, I will have a conflict for form id's and names...since they are both called ItemText.
Is it possible to customize the binding of these elements without writing a custom model binder?
I would like to do something like:
public class EditChildViewModel
{
[BindFrom("EditItemText")]
public string ItemText { get; set; }
}
Or does it just make more sense to rename the ViewModel properties to:
public class EditChildViewModel
{
public string EditItemText { get; set; }
}
public class CreateChildViewModel
{
public string CreateItemText { get; set; }
}
EDIT
Based on converstation with Darin I want to make this a little more clear.
My Parent has an Edit action.
When you edit the Parent, you would never create a new child or edit a child when you are calling the ParentController.Edit action.
I have a separate controller for the Child object that has a Create and Edit method:
public class ChildController
{
public ActionResult Edit() {}
public ActionResult Create() {}
}
I am using jQuery calls to asynchronously post to this controller when you edit or create a child. Basically I use a jquery dialog to create/edit a child that will get saved immediately when I click Ok on the dialog. This would happen even before clicking save for the Edit action of the parent.

I would use editor templates. Normally you would pack those two view models into a main view model which will be used by the main view:
public class MyViewModel
{
public CreateChildViewModel Create { get; set; }
public EditChildViewModel Edit { get; set; }
}
and then:
#model MyViewModel
#Html.EditorFor(x => x.Create)
#Html.EditorFor(x => x.Edit)
and I would replace the two partials by their corresponding editor templates (~/Views/Shared/EditorTemplates/CreateChildViewModel.cshtml and ~/Views/Shared/EditorTemplates/EditChildViewModel.cshtml). The editor templates will take of generating proper names and ids of the corresponding input elements.
Personally I tend to prefer editor/display templates instead of partials as they handle better naming of input elements.

Related

Simple way to bind abstract class model, ASP.Net Core 5

In short, I have a model class which inside of it has some properties as well as some properties which types are abstract classes.
The model looks something like this:
public class ModelClass
{
public string SomeProp { get; set; }
public AbstractOne FirstAbstract { get; set; }
public AbstractTwo SecondAbstract { get; set; }
}
And the abstract classes:
public abstract class AbstractOne
{
public virtual Type TwoType { get; set; }
public string SomeProp { get; set; }
public string SomeOtherProp { get; set; }
}
public abstract class AbstractTwo
{
// Its empty
}
So in the actual code, I have a class that inherits each one of those abstract classes, and in the controller I initially give the model as:
return View(new ModelClass()
{
SomeProp = "Somevalue",
AbstractOne = valueUpInTheCode
AbstractTwo = (AbstractTwo)Activator.CreateInstance(valueUpInTheCode.TwoType)
}
Which works fine, the View gets the model, and I can use the html helpers to generate form fields.
I have a form which has a Html.HiddenFor for SomeProp and AbstractOne, DisplayFor for AbstractOne and EditorFor AbstractTwo which is specific to the class that inherits the AbstractTwo, so the EditorFor line looks something like this:
#Html.EditorFor(model => model.SecondAbstract, Model.SecondAbstract.GetType().Name)
Which, on the UI seems fine at least, all of the default values or values passed when giving creating model for the view appear and seem to be there.
The problem is when that form is submitted, I have a function that should handle it, for now its empty, but I'm still debugging so I just need some place to put a breakpoint:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Submit(ModelClass model)
{
if (ModelState.IsValid == false) return View("Index", model);
return View("Index", model);
}
The problem is, that model ends up having all of its properties set how they were entered, but the FirstAbstract and SecondAbstract don't seem to properly bind and end up both being null.
So far I've tried some stuff I found on here which seemed to be either outdated or not working for my use case, the closest I got to something working was with this blog on how to make a custom Model Binder:
https://www.palmmedia.de/Blog/2018/5/13/aspnet-core-model-binding-of-abstract-classes
It didn't end up working in the end, but gave me an idea of how something like this should be fixed... And I've got no clue how to do that, or where to start even.

MVC - how to send to the partial view ViewModel containing a list

I am the beginner in MVC and I have a web application, where in my controller I declare a list of objects (feedback from visitors) and then send it to the view, which displays it. It looks like this. Declaration:
public class TrekFeedbackItem
{
public string trekid { get; set; }
public string comment { get; set; }
public string author { get; set; }
public TrekFeedbackItem(string trekid, string comment, string author)
{ this.trekid = trekid;
this.comment = comment;
this.author = author;
}
}
And usage:
List<TrekFeedbackItem> feedbackList = new List<TrekFeedbackItem>
{
//constructor called, data entered into the list
}
return View(trekname, feedbackList);
However, now I need to pass also another list, lets call it relatedblogsList. As a first step, I decided to encapsulate my feedbackList into the ViewModel (and once it works, add another list of different objects.)
public class TrekViewModel
{
public List<TrekFeedbackItem> feedback { get; set; }
}
and fill the data like this:
TrekViewModel trek = new TrekViewModel();
trek.feedback = new List<TrekFeedbackItem>
{
//insert data here
};
return View(view, trek);
The problem is - how to send this model to the partial view and how to access it?
Thank a lot
You can pass data into the partial view like below
from the controller return this view:
return PartialView("_partial_viewname", trek);
then in the beginning of the partial view:
#model Models.TrekViewModel
after that you can use Model.feedback inside the partial view.
Set return type of your action controller to "PartialView" rather than "View".
return PartialView("_yourPartialViewName", yourObject);
In case, if application does not work as expected, build it and re-run it.

ASP.net MVC Is it possible to modify a class object in a view?

I am a newbie and creating a website where you can create your own custom quizes. Ive made a database that stores a class object mytests that consists of a name, and a list of questions parameter.
public class MyTests
{
public int ID { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<MyQuestions> AllTestQuestions;
}
//using this object for questions
public class MyQuestions
{
public string QuestionDescription { get; set; }
public string MultipleChoiceCorrect { get; set; }
public string MultipleChoiceB { get; set; }
public string MultipleChoiceC { get; set; }
public string MultipleChoiceD { get; set; }
public string Answerexplanation { get; set; }
}
I'm using the default database code generated by visual studio. I have no problem adding this test object(mytest) to the database, but what I want to do is that on the edit.cshtml view I want to be able to add elements to the question list before returning the object to the database saved.
The problem is I don't know how to edit the model object from the view, or if this is even possible. I could maybe get it to work through a redirect? but I thought that adding the elements directly from the view would be easier. Is it possible to modify the model.object inside a view from the view (putting security concerns aside)?
For example model.title = something;
or
model.list.add()
Is anything like this possible?
If this question is not clear please let me know and I will try to clarify in the comments.
Yes, it is possible to edit the model from within the view.
From within your .cshtml file specify the view model using the #model declaration, then edit the model like so:
#model Namespace.For.MyTests
#Model.name = "Hello World";
<p>#Model.name</p>
Whilst this would work, it's not really what the view is for so I wouldn't recommend it.
The view is about presenting your data, not mutating it - that should be done in the controller, or domain layer. As soon as the user leaves the page then your changes will be lost due to the stateless nature of the web (.NET MVC passes data to the view from the controller, then ends the request).
This should be done at the controller level. You could do it on a view but it's not what the view is for.
Your issue is that if the page is refreshed you will lose you content, so if you do anticipate on the page refreshing you will need a way in which to temporarily hold the information before it being saved.
On a side note, I'd also consider renaming your classes "MyTests" to "MyTest" (singular) and "MyQuestions" to "MyQuestion"... it's just good practice because then you'd have a List of singleton "MyQuestion" in a "MyTest". EntityFramework Codefirst will pluralise the names when the database is created/update.

Different viewmodel in partial view and rest of page?

I'm trying to figure out how i can use fully different data on my page compared to what i use in a partial view.
I have a search bar at the top of my website, that top is a partial view that accepts a searchviewmodel and the bottom could be well, anything (search result, a blog, completely unrelated stuff).
Obviously i don't want to "mix" viewmodels, each of them should be unaware of the other (i'm not going to create a class to host both the partial & main viewmodel for each different page).
What is an appropriate way to handle this scenario?
Scenario is
User fills search on the top
=> if model is invalid, redisplay current page with errors (current page could for
example be today's wether, requiring WeatherViewModel for main content and
SearchViewModel for partial view)
=> else display result page (still need SearchViewModel for top banner
and then SearchResultsViewModel for the page)
I'm not finding any help online so far from googling (all i found was scenarios where it made sense to create a class to host both sub classes as they were functionally related, which isn't the case here).
My SearchViewModel looks like this:
public class SearchViewModel
{
[Required]
public string StartCity { get; set; }
[Required]
public string EndCity { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public SearchType Type { get; set; }
[Required]
public SearchTimeType TimeType { get; set; }
}
It corresponds only to the top search and can be present on ANY page regardless of what viewmodel the REST OF THE PAGE uses.
You can insert the Search Partial View into the Template outside the #RenderBody().
Your partial View might have a a form to an action that returns the ShowResults View.

Using an Editor Template for a List

I have something like this
public class ResumeVm
{
public ResumeVm()
{
EducationVms = new List<EducationVm>();
}
public Guid UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<EducationVm> EducationVms { get; set; }
}
public class WorkExperienceVm
{
public string Title { get; set; }
}
I now want to make an editor template for each of the EducationVms, I made a Template for 1 Education Vm and tried to use
#Html.EditorForModel("WorkExperienceVm")
but it does not know how to pass in the EducationVms
If I do
#Html.EditorForModel("WorkExperienceVm", #Model.EducationVms )
It gets made as it expects only 1 Vm to be sent in.
// View (WorkExperienceVm)
#model ViewModels.WorkExperienceVm
#Model.Title
The EditorForModel overload that you're using is incorrect. EditorForModel produces an editor template for the current model (i.e. the ResumeVm) and the string you're passing in is the additional view data object, not the name of the view.
I'm assuming that "WorkExperienceVm" is the name of the view. Try using EditorFor:
#for(int i = 0; i < Model.EducationVms.Count; i++)
{
Html.EditorFor(m => m.EducationVms[i], "WorkExperienceVm")
}
An alternative is to create a template that's actually called EducationVm.cshtml and type it to EducationVm, then you can just do the following and the framework will figure out that you want the template called for each item in the collection:
#Html.EditorFor(m => m.EducationVms)
Unfortunately this approach can't be achieved using UIHints or passing in the view name manually into the helper, though that's fairly unlikely to get in your way if you don't mind adhering to strict naming conventions for your templates.
I wrote another answer a while ago explaining the differences between the different helpers for editor templates. It deals specifically with the "label" helpers but the same principles apply to the "editor" helpers.

Categories