mvc controller returning raw html instead of the normal view - validation

Last Edit
After hours spent googling, it turns out that bootstrap tab and server-validation do not play along. Thank you guys for the help.
Edit
The culprit is the repopulation of the selectlistitem. I am presented with a catch-22:
- remove the repopulation of the selectlistitem == error in view (null reference).
- add the repopulation of the selectlistitem == raw html.
Have anybody experience this issue?
I am experiencing something weird, I think. My controller is returning a raw html instead of a normal view when !modelstate.isvalid.
The GET:
[HttpGet]
public ActionResult AddNewProduct()
{
...
return View(productVM);
}
The Post:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddNewProduct(ProductVM prodVM, IEnumerable<HttpPostedFileBase> images)
{
if (ModelState.IsValid)
{
...
}
else
{
//repopulate dropdownlistfor
prodVM.slCategory = GetCategory(prodVM.prodDetailVM.storeID);
prodVM.selectedCat = 0;
prodVM.slAuctionType = GetAuctionType();
return View(prodVM);
}
}
The dropdownlist is a cascading type.
Can anybody give me a hint as to what is wrong? I have googled with no success. Thx.
Edit
I am not sure if the View is the problem, it renders ok on load, but displaying raw html on model not valid.

Related

How to display a fully formed HTML page as an MVC view?

MVC/ASP.NET/C#/html/javascript newbie question:
I'm trying to move some legacy software into an MVC solution. I have an MVC controller ViewResult method that makes an API call to the legacy system and returns a string which is a fully formed HTML page (including the HTML start and end tags). Some time in the future, I'll rewrite the logic as an MVC view, but for right now I need to just display that page (preferably in a new tab).
I've tried this in the controller:
return View((object)calendar);
(where "calendar" is the string containing the HTML document)
In my view I have
#model string
#{ Layout = null; }
#Model
But that didn't work.
Any ideas?
Model binding is binding the object of your model class.
For example, ([Solution].[Models].[Model class]),
#model PassDatainMVC.Models.Record
To pass the data from controller to view,
Approach 1: ViewBag
Controller:
string data = "testing";
ViewBag.see = data;
return View();
View:
#using PassDatainMVC.Models
#ViewBag.see
Or:
Approach 2: Model binding
Controller (Class):
public string recordProperty;
View:
#model PassDatainMVC.Models.Record
#Model.recordProperty
While you have to set the property under the model class in the data field for the second approach.
Ref. https://www.c-sharpcorner.com/article/asp-net-mvc-passing-data-from-controller-to-view/
If you want to just one data you can use a ViewBag. This is simple.
Also you want to send with model. You should use this code.
Class
public class Calendar
{
public string CalendarName { get; set; }
}
Controller
Calendar newModel = new Calendar();
newModel.CalendarName = "test name...";
return View(newModel);
View
#model ModelNamespace.Calendar
<h1> #Model.CalendarName </h1>
Thanks Reha! But unfortunately neither of your suggestions did the trick.
For your first suggestion I used ViewBag. In the controller I replaced
return View((object)calendar);
to
ViewBag.calendar = calendar;
return View();
And replaced the view with just
#{ Layout = null; }
#ViewBag.calendar
The result was that the user is left looking at the actual HTML code instead of what the HTML code is supposed to render.
For your 2nd suggestion, I did exactly as you suggested (except I changed
Model.CalendarName = "test name...";
to
Model.CalendarName = calendar;
The result is the same, the user is left looking at the HTML code.

Partial View HttpPost invoked instead of HttpGet

I'm working on the admin part of an MVC webapp. I had the idea to use "widgets" for a single Admin panel. I'll explain my intentions first.
I have a languages table, and for that I'd like to create a partial view with a dropdownlist for those languages and a single button "Edit", that would take the user to a non-partial view to edit the language. After clicking save, the users would be redirected to the Index view, which would just show the dropdownlist again.
So I have a "Index.cshmtl", and an "EditLanguage.cshtml" as non-partial views, and a "LanguageWidget.cshtml" as a partial view.
First the user sees the Index view.
public ViewResult Index()
{
return View();
}
This view has the following code in it:
#using CodeBox.Domain.Concrete.ORM
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Administration</h2>
#Html.Action("LanguageWidget")
The Partial view "LanguageWidget" just contains the following code, and when the user submits it posts to the HttpPost annotated method in my controller:
#using (Html.BeginForm("LanguageWidget", "Admin"))
{
#Html.DropDownListFor(model => model.SelectedItem, Model.Languages)
<input type="submit" value="Edit"/>
}
This is the HttpPost method for the widget:
[HttpPost]
public ActionResult LanguageWidget(LanguageWidgetModel model)
{
var lang = langRepo.Languages.FirstOrDefault(l => l.LanguageId == model.SelectedItem);
return View("EditLanguage", lang);
}
This takes the user to the language edit page, which works fine.
But then! The user edits the language and submits the page, which invokes the "EditLanguage" HttpPost method, so the language is saved properly.
[HttpPost]
public ViewResult EditLanguage(Language model)
{
if (ModelState.IsValid)
{
langRepo.SaveLanguage(model);
TempData["message"] = string.Format("{0} has been saved!", model.Name);
return View("Index");
}
else
{
return View(model);
}
}
So, when I return the "Index" view - which seems logical I guess - the controller still assumes this is a HttpPost request, and when it renders the Index view, it invokes the "LanguageWidget" method, assuming it has to render the HttpPost method.
This leads to the LanguageWidget HttpPost method, which returns a full view with layout, returning just that, so I have my layout, with view, containing a layout, with the editview.
I don't really see how I could fix this?
I'm pretty sure it's a design flaw from my part, but I can't figure it out.
Thanks in advance!!
Consider using:
return RedirectToAction("Index")
instead of:
return View("Index");
It might seem more logical if the user is actually redirected to Index instead of
remaining at the EditLanguage. And if the user hits the refresh button no data will be resent using this approach.

MVC3/Razor to Controller Ajax call

I have a Razor view with a couple of dropdown lists. If the value of one of the dropdown's is changed I want to clear the values in the other drop down and put new ones in. What values I put in depends on the values in the model that the view uses and that is why I need to send the model back from the view to the controller. The controller will then also need to be able to modify the dropdown by sending back data to the view. Note, that I am not saying that I want to go back to the controller from a form submit using Ajax. I am going back to the controller using a form submit, but not using Ajax.
Please can someone give me some simple code or some pointers to show how this might be done.
Thanks
I personally use ViewBag & ViewData to solve this condition.
Controller:
public ActionResult Index()
{
ViewBag.dd1value = default_value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
Return View();
}
View:
In the first dropdownlist add an onchange javascript.
<select onchange="javascript:this.form.submit();">
#foreach (var item in ViewBag.dd1) {
if (ViewBag.dd1value = item.dd1value)
{
<option selected value="#item.dd1value">#item.dd1text</option>
}
else
{
<option value="#item.dd1value">#item.dd1text</option>
}
}
Then, on submit button give it a name.
<input type="submit" name="Genereate" value="Generate" />
In the controller, create 2 ActionResult to receive data.
For dropdownlist:
[HttpPost]
public ActionResult Index(int dd1value)
{
ViewBag.dd1value = dd1value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
Return View();
}
For submit button:
[HttpPost]
public ActionResult Index(int dd1value, int dd2value, FormCollection collection)
{
ViewBag.dd1value = dd1value;
ViewBag.dd2value = dd2value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
ViewBag.result = Result(dd1value, dd2value);
Return View();
}
If you don't need button:
[HttpPost]
public ActionResult Index(int dd1value, int dd2value)
{
ViewBag.dd1value = dd1value;
ViewBag.dd2value = dd2value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
ViewBag.result = Result(dd1value, dd2value);
Return View();
}
Please note that if you use ViewBag / ViewData, all the help you get from the compiler is disabled and runtime errors/bugs will occur more likely than if the property has been on a "normal" object and typos would be catched by the compiler.
I would implement a different solution from DragonZelda.
I would create a ViewModel object containing the data that you need on the page, that the View binds to.
Then, I would create controls that bind to that Model, like:
#Html.DropDownListFor(x => x.SomeDDLSelected, ......)
x.SomeDDLSelected would be a property in your ViewModel object that would automatically get the selected value in the dropdownlist when the automatic model binder gets in action.
Then, to finalize it, the Controller action would receive your ViewModel object as a parameter:
public ActionResult MyAction(MyViewModelObject obj)
{...}
And you get all your data nice and tidy, all strong typing.

MVC 3 Razor view BeginForm is not posting to controller in FF, but works in IE 9

I have a form in MVC 3 razor view that I am trying to post to my controller.
I need these:
1) Post the form to the controller action.
2) The action should do something with the data &return a string status (OK if success or NOK if failed)
3) Based on the result I might redirect the user after a brief delay.
4) I also want to prevent duplicate submission (if possible)
This is how my view looks (I trimmed it):
#model <MyNameSpace.Model>
#{
ViewBag.Title = "Save";
Layout = "~/Views/Shared/MyMaster.cshtml";
}
#using (Html.BeginForm("save", "my_controller"))
{
<div>
#Html.TextBoxFor(m => m.Host, new { #style = "width: 520px" })
... set other fields on the form ...
<input type="submit" id="btnSubmit" value="Submit"/>
</div>
}
This is my controller:
public String Save(<ModelName> model)
{
return "OK";
}
This seems working in IE9. But nothing happens in FF 4 or Opera. HttpFox shows no activity.
What is missing?
Thanks
In ASP.NET MVC it is considered good practice to have your controller actions return ActionResults instead of strings. This way proper content type headers will be set, etc...
So for example:
[HttpPost]
public ActionResult Save(ModelName model)
{
return Content("OK", "text/plain");
}
or if you wanted to return some view:
[HttpPost]
public ActionResult Save(ModelName model)
{
return View("Success");
}
Your example looks kosher, so either something critical is missing from your example code, or you need to view the generated HTML to see what's missing.
Did you omit the code that actually displayed the view to the user? I'm unsure of how this would function without a bit more. I mocked up your code and put in what I considered correct.
[HttpGet]
public ViewResult Save()
{
var vm = new ModelTest();
return View(vm);
}
[HttpPost]
public ActionResult Save(ModelTest model)
{
//do stuff with model
//Set a value in TempData -- Meets requirement of storing a status
TempData["Message"] = "OK";
//RedirectToRoute -- Meets requirement of preventing multiple posts partially. Some javascript will also help with this
RedirectToRoute("routename");
}
Post happened as expected in FF 4.01/5.0
Your model looks ok, it's just the controller code appears lacking.
Hopefully this helps.

MVC3 Ajax.BeginForm odd behavior

I have a very simple view that has a DropDownListFor and a Button inside an Ajax.BeginForm helper. Clicking the button renders the whole view again inside the div I have set to update including the layout page (I also notice a spike in the cpu when clicking the button multiple times)
Here is the Ajax.BeginForm inside the view:
#using (Ajax.BeginForm("About2", "Home", new AjaxOptions { UpdateTargetId = "property22" }))
{
<div>
<div id="property22">
#Html.DropDownListFor(m => m.SomePropertyToBind, new SelectList(Model.list, "property1", "property2"))
</div>
<button type="submit" id="test">
Click me</button>
</div>
}
Any ideas where I'm going wrong?
I uploaded the whole project if someone has a couple of minutes to take a look at it:
http://www.sendspace.com/file/siu3r31 (free provider so there may be a popup)
Thanks
You are using a wrong overload of the Ajax.BeginForm helper. It should be like this:
#using (Ajax.BeginForm(
"About2",
"Home",
null,
new AjaxOptions { UpdateTargetId = "property22" },
new { #id = "refreshe" }
))
Notice the additional null value I am passing as the routeValues parameter. Also in the example you uploaded you forgot to include the TestView.cshtml view. This being said in order to fix the problem you have two possibilities:
Either return a partial view:
public ActionResult About2()
{
Random randomizer = new Random();
int random = randomizer.Next(1, 1000000000);
ModelTest newModelTest = new ModelTest();
string[] stringList = new string[] { "Red", "Blue", "Green" };
newModelTest.list = from test in stringList
select new ModelTestList
{
property1 = test,
property2 = test
};
newModelTest.SomePropertyToBind = stringList[random % 2];
return PartialView("TestView", newModelTest);
}
or disable the layout in the TestView.cshtml view:
#{
Layout = null;
}
Unfortunately from your explanation above and from the code, I am not sure what you are trying to achieve. However, I think your most worry is about having Ajax working in your view.
In your About2 action method, you are trying to return a complete view which is TestView (in that case, it doesnt exist) and passing it the newModelTest view Model. I would advise changing to return either a PartialView or JsonResult.
For example, changing the return statement of About2 action method to
public ActionResult About2()
{
...
return Json(newModelTest);
}
or changing it to a return type to string and returning "TestResult"
public String About2()
{
...
return "TestResult";
}
or you could change the return statement to return a PartialView
Thanks for your replies.
I just realized that About2 should have returned the "About" view instead of the "TestView". I had tried creating a partial view with the Ajax.BeginForm code but I came across the same problem.
This is my first attempt at Ajax.BeginForm (so far I have always used jquery), and I was under the impression that it works in a similar fashion in the sense that by specifying the target id only the contents of that element will get updated, not that the target will actually get replaced by the whole response object.
Thanks for your help, not only did I get it to work, but I now understand how it should work.
I suspect that what's happening is that you're returning the a complete View (including the layout template) in the Ajax response. Try changing your "Home" controller "About2" action temporarily to the following:
public ContentResult About2() {
return Content("Hello World");
}
I tested this sample Action with your Razor markup and clicking the button properly replaced your dropdown list with "Hello World!".
If this is indeed what's happening, then you'll want to return a View from "About2" without the layout by declaring the following at the top of the View that you're returning.
#{
Layout = null;
}

Resources