I'm trying to sort my result page (which is in another view than the filtration page). I have faced this weird issue I do not understand why keeps happening to me.
All the codes provided in very short form, please ask me if you need any other parts of my code for more information.
My Index view(where user filters results):
#model IEnumerable<Cars.Models.CarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post,
new
{
id = "CategoryFormID",
data_modelListAction = #Url.Action("ModelList"),
data_makeListAction = #Url.Action("MakeList"),
data_editionListAction = #Url.Action("EditionList")
}))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
}
My SearchResult view:
#model IEnumerable<Cars.Models.Car>
Make
My model:
public class Car
{
public String Make { get; set; } //is my table model
}
public class CarFilter {
public String carMake { get; set; }
}
public class CarSearch {
public CarFilter CarFilter { get; set; }
public byte PageSize { get; set; }
public short PageNumber { get; set; }
public int TotalRows { get; set; }
}
My Controller:
public ActionResult SearchResult(String sortOrder, CarFilter filters)
{
ViewBag.CurrentFilters = filters;
return View();
}
All I'm trying to do is to get carMake from Index post it to controller in CarFilter form (since in my code there are LOTS of fields in the form and I don't want to write them all down) and when user clicks on sort by Make it GET the SearchResult method and it's supposed to set filters = ViewBag.CurrentFilters which is the value user inputted from beginning.
Now the funny part is, when I replace CarFilter filters with String carMake and other places respectively. It works like a charm.
My question:
Why?
How can I do this with CarFilter filters?
UPDATE:
Problem is that filters = ViewBag.CurrentFilters in my SearchResult view does not work with the type CarFilter, because it keeps giving me NULL value when user clicked on the sort by Make.
Second UPDATE:
I tried changing filters = ViewBag.CurrentFilters with CarFilter = ViewBag.CurrentFilters. Now CarFilter filters in my SearchResult(...)method in my controller is not and null object, but ALL the values of the objects in the model class is null (which shouldn't be). I mean the filters object exists but it seems like the values of CarFilter class in my model haven't been passed by ViewBag.CurrentFilters to the view.
when you canged the name it worked because framework found property name and the bind it to what you have within action parameters doesnt work so nicely with objects. My advice is to stick with simple types
Here is similiar case:
How to send model object in Html.RenderAction (MVC3)
Its not a ViewBag problem thants how it works in general. Its the prime reason for using flatted models :/
Related
Is it right if I return a Model object from Controller method to the View to be able to set the datasource to the datagridview in the View?
I'm trying to use MVC in WinForms.
I have a wrapper class that holds two models and which I pass to the Controller.
public class TwoModels
{
public UserInfo user { get; set; }
public List<UserInfo> Users { get; set; }
public BindingList<MedicineProduct> Products { get; set; }
}
I have a method in my View which loads data and sets the DataGridView's datasource.
private void LoadCache()
{
productsCache = new Products();
productsCache = XMLToObjectToXML.LoadData(productsCache, path);
dataGridView2.DataSource = productsCache.Products_;
userCache = XMLToObjectToXML.LoadUser(username);
}
I want to move this method to controller. But, I don't know if it's a right approach. Should I load data in controller or should I do it inside wrapper class?
Yes. Ensure that the ActionResult method has the same name as a view you want to return and that the view is expecting a Model object the same type as you are returning in the controller.
I would need to see some example code to help determine what you plan on achieving with use of datagridview.
Complete MVC Noob warning.(2 hours learn time)
I've looked at a lot of MVC3 examples online but I havent found a simple example to do what I am trying to do.
What I want to do is two join two models and get some data into a view. The most obvious
public partial class Model1
{
public int ID { get; set; }
public int StudentID { get; set; }
public int CoachID { get; set; }
public String StudentName {get;set;}
}
public partial class Model2
{
public int CoachID { get; set; }
public String CoachName { get; set; }
}
Basically in my view I have to just join Model 1 and model 2 on CoachID and print
StudentName and CoachName in a grid.
How do I do this? How do I create the view model for this ?
How do I iterate through the view and print the joined data?
Can I instead just create a view in the database and directly attach a model and a view to it ?
Sounds simple, but Ive spent the last three hours online completely baffled
Create a StudentCoachViewModel with exactly the properties you need in it to display, nothing more, nothing less.
Populate a list of this viewmodel in your controller and send it to your view. Code sample to follow shortly.
Enumerate that list in your view
public class StudentCoachViewModel
{
public string CoachName { get; set; }
public string StudentName { get;set; }
}
In your controller something along the following lines (just typing this out, haven't checked in compiler)
public ActionResult Index()
{
//code to populate your model1 and model2 already assumed
var viewModels = (from m in model1List
join r in model2List on m.CoachId equals r.CoachId
select new StudentCoachViewModel(){ StudentName=m.StudentName,
CoachName = r.CoachName }).ToList();
return View(viewModels);
}
In your view, something along the lines of (clearly you want to format and use proper layout, table, etc which can be auto generated by visual studio)
#model IEnumerable<StudentCoachViewModel>
//other html content here
#foreach(var viewModel in Model)
{
#Html.DisplayFor(o=>o.CoachName) #Html.DisplayFor(o=>o.StudentName)
}
Now if you are only wanting a single one here rather than a list its even easier
public ActionResult Index(int id)
{
//code to load model1 and model2 already assumed to be in place. also assuming you loaded this data from a database by the id field being passed into this method.
return View(new StudentCoachViewModel(){ StudentName = model1.StudentName, CoachName = model2.CoachName});
}
And the view becomes then simply
#model StudentCoachViewModel
//other html here, h1, divs, etc whatever is in your view as html content.
#Html.EditorForModel()
or if you like to display each one instead of the above one line call:
#Html.LabelFor(o=>o.CoachName)
#Html.EditorFor(o=>o.CoachName)
try this
public partial class Model3{
public Model1 model1{get;set;}
public Model2 model2{get;set;}
}
bind Model3 into view.
View page can accept only one model, so you can't pass two model at the same time,
So you have to Create a another model with all the members of that two model...
then in controller, you should convert those two model to one model and pass it to view with help of new model one
or else you can create a another new model with those two model
There is something weird going on in my app. It's not dammageable, but it's a curious behavior and I'm reaching out to you to understand what's happening.
I was working on some partial view based on a model, and it worked. I figured out I had to replace a lot of stuff with the correct input.
So here's a snippet of my old model:
public class SearchObjInfo
{
public string m_ObjName { get; set; }
public string m_ObjType { get; set; }
public decimal? m_ObjNumber { get; set; }
public string m_ObjSymbol { get; set; }
public string m_ObjPower { get; set; }
}
And here's the same snippet with the new class I made to construct this partial view:
public class SearchObjInfoPartial
{
public string m_ObjName { get; set; }
public IEnumerable<SelectListItem> m_ObjType { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:0}")]
public int m_ObjNumber { get; set; }
public IEnumerable<SelectListItem> m_ObjPower { get; set; }
public IEnumerable<SelectListItem> m_ObjSymbol { get; set; }
}
Now the way the render is made is actually quite identical, and not. I used lots of stuff like these before:
<label>
Text: Write a name, part of a name, or a word.
</label>
Object Name: #Html.TextBox("_objectName") <br/>
Object Number: <input type="number" min="0" max="9999" name="_objNumber" value="decimal" style="width: 70px"/><br/>
Type: #Html.DropDownList("_objType", "All") <br/>
Power: #Html.DropDownList("_objSymbol", "=") #Html.DropDownList("_objValue", String.Empty)<br/>
But now I render my partial view this way:
#model MyApp.Utilities.SearchObjInfoPartial
Object Name: #Html.TextBoxFor(item => item.m_ObjName, Model.m_ObjName, String.Empty) <br/>
Object Number: #Html.EditorFor(item => item.m_ObjNumber)<br />
Power: #Html.DropDownListFor(item => item.m_ObjPower, Model.m_ObjPower, String.Empty) #Html.DropDownListFor(item => item.m_ObjSymbol, Model.m_ObjSymbol, String.Empty)
Type: #Html.DropDownListFor(item => item.m_ObjType, Model.m_ObjType, String.Empty) <br/>
Before rendering I deal with the SelectLists, no problems here.
Now here's where it gets interesting:
In my controllers I used to have methods receiving huge amounts of data (see here: How to deal with many possible values to make a query?)
But now I made something else. Without thinking, I tried to add the old search model in the controller method like this:
public ActionResult BrowseObjectList(SearchObjInfo searchObj, string _objName, (...))
And I just found out that it works even if the receiving object is not the same as the one used in my partial view model. How is that even possible? I mean, the proper fields will fill up and I can "safely" deal with my searchObj item, though I do not find this secure after all...
Thats what MVC framework does for you man.
Browser simply sends the form collection to server as Name Value Collection. As the request hits server, MVC framework will match the values with parameter in the Action method.
Form collection values are mapped to Model object properties. This is done by doing a match with Property Name and Name of the value in Form collection. Just check the client side code by view source, you can see that the input tags will have an attribute 'name' which matches with the property name of model.
QueryString values will also be mapped to parameters in Action method based on name.
Even you add a hidden field and specify a parameter with same name in action method...tada you will get the value of hidden field in that variable on post back
In your case though the model is different, its property name are same m_ObjName, m_ObjType, m_ObjNumber, m_ObjSymbol, m_ObjPower. So MVC do a match for you.
Try with different property name and see the results ;-)
I'm trying to learn MVC by building a full-featured website. I'm a little stuck when it comes to dealing with forms, and posting data, and models....
BTW: I'm using EF Code-First w/MS SQL CE
Here's the Models in question:
public class Assignment
{
public int AssignmentID { get; set; }
public int? CourseID { get; set; }
public string Name { get; set; }
// etc...
public virtual Course Course { get; set; }
}
public class Course
{
public int CourseID { get; set; }
// etc...
}
I'm loading a partial view that allows the user to add a new assignment
Controller:
public ActionResult Assignments()
{
var assignments = myContext.Assignments.OrderBy(x => x.DueDate);
return View(assignments);
}
[HttpPost]
public ActionResult AddAssignment(Assignment assignment)
{
myContext.Assignments.Add(assignment);
myContext.SaveChanges();
return RedirectToAction("Assignments");
}
// Returns a strongly-typed, partial view (type is Assignment)
public ActionResult AddAssignmentForm()
{
return PartialView();
}
Here's where I'm stuck: I want this form to have a drop down list for the different courses that an assignment could possibly belong to. For example, an assignment called "Chapter 3 Review, Questions 1-77" could belong to course "Pre-Algebra". However, if I use the code below, I have to explicitly declare the SelectListItems. I thought that with the given Assignment model above, I should be able to have the drop down list for Courses automatically generated using MVC awesomeness. What am I doing wrong?
AddAssignment Partial View:
#model MyModels.Assignment
#using(Html.BeginForm("AddAssignment", "Assignments"))
{
// Can't I create a drop down list without explicitly
// setting all of the SelectListItems?
#Html.DropDownListFor(x => x.Course, ....
}
Basically you are confusing/mixing your business model and your UI model.
The quick fix here is to add the data for the dropdown list to the ViewBag (a dynamic object).
Alternatively you could create a class AssignmentModel that contains the relevant Assignment properties and the List.
And No, this is not well supported in the templates.
You do realize you'll need some error handling in the Post method(s)?
I'm trying to add a form to allow users to comment on posts on my blogging application. So far, I've added a form to the post details view and I can submit comments, adding them to my database correctly. However, I have a problem with displaying validation errors to the user. The comment form is contained within a partial view and is rendered using Html.RenderAction inside the post details view. I'd like to stress that I don't want to use AJAX for this as I'd like to approach this from a progressive enhancement point-of-view.
Here's the relevant posting action:
[HttpPost, Authorize]
public ActionResult AddComment(CommentViewModel newComment)
{
if (ModelState.IsValid)
{
Comment comment = new Comment(_userRepository.GetByUsername(User.Identity.Name));
Mapper.Map(newComment, comment);
_commentRepository.Add(comment);
_postsRepository.CommentAdded(comment.Article);
return RedirectToAction("Index", new { id = newComment.PostID });
}
// What do I do here?
}
I've tried several ways of returning views here but my issue is further complicated by some controller parameter validation that I have going on in the parent action:
//
// GET: /Posts/5/this-is-a-slug
public ActionResult Index(int id, string slug)
{
PostViewModel viewModel = new PostViewModel();
var model = _postsRepository.GetByID(id);
if (model != null)
{
if (slug == null || slug.CompareTo(model.Slug) != 0)
{
return RedirectToActionPermanent("Index", new { id, slug = model.Slug });
}
else
{
_postsRepository.PostVisited(model);
Mapper.Map(model, viewModel);
viewModel.AuthorName = _userRepository.GetById(model.AuthorID);
}
}
return View(viewModel);
}
This action basically mimics how SO's URLs work. If a post ID is supplied, the post is fetched from the database along with a slug which is created when the post is created. If the slug in the URL doesn't match the one in the database, it redirects to include the slug. This is working nicely but it does mean I'm having issues trying to populate my parent viewmodel, which is the following:
public class PostViewModel
{
public int PostID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Slug { get; set; }
public DateTime DatePublished { get; set; }
public int NumberOfComments { get; set; }
public int AuthorID { get; set; }
public string AuthorName { get; set; }
public List<CommentViewModel> Comments { get; set; }
public CommentViewModel NewComment { get; set; }
}
What I was hoping would work is to populate PostViewModel.NewComment, test to see if it has data and then using it to display any model errors. Unfortunately, I'm lost as to how to accomplish that. This question helped me shape my approach, but it didn't quite answer my problem.
Could someone give me a gentle push in the right direction? If my approach seems unreasonable, I'd love to find out why and what a potential fix would be.
Many thanks in advance.
Forgot to fill in my answer here. For anyone that happens to stumble on this, the answer was to use TempData to store the ModelState errors and then repopulating ModelState in the relevant controller action.
Firstly, I declared a key in the controller which would be used to reference the data inside TempData. I decided to base this on the CommentViewModel type as both actions depend on it.
public class PostsController : Controller
{
private static readonly string commentFormModelStateKey = typeof(CommentViewModel).FullName;
// Rest of class.
}
In this first action, the code checks to see if TempData contains data assigned to the key. If it does, it's copied into ModelState.
// GET: /posts/comment
[ChildActionOnly]
public PartialViewResult Comment(PostViewModel viewModel)
{
viewModel.NewComment = new CommentViewModel(viewModel.PostID, viewModel.Slug);
if (TempData.ContainsKey(commentFormModelStateKey))
{
ModelStateDictionary commentModelState = TempData[commentFormModelStateKey] as ModelStateDictionary;
foreach (KeyValuePair<string, ModelState> valuePair in commentModelState)
ModelState.Add(valuePair.Key, valuePair.Value);
}
return PartialView(viewModel.NewComment);
}
This action determines if the ModelState is valid before adding a comment to the database. If the ModelState is not valid, it is copied to TempData, which makes it available to the first action.
// POST: /posts/comment
[HttpPost, Authorize]
public ActionResult Comment(CommentViewModel newComment)
{
if (!ModelState.IsValid)
{
TempData.Add(commentFormModelStateKey, ModelState);
return Redirect(Url.ShowPost(newComment.PostID, newComment.Slug));
}
// Code to add a comment goes here.
}