ASP.NET - PartialView continues using layout - c#

I have a checkout flow consisting of several steps. For performance reasons I work with ajax partial updates, thus I only want to return partial views without the layout.
Note that I am using ASP.NET Mvc, not ASP.NET Core!
First, I have an index method that loads a view that then will the partial of the step the order currently is in.
public override ActionResult Index(TradeInCheckOutPage currentPage)
{
var model = new BaseCheckoutStepViewModel(bulkOrderViewModel, currentPage,
GetCurrentStep(orderViewModel));
return View(Index.cshtml", model); // View handles redirect to correct view
}
The view of that method:
if (Model.Step.Equals(CheckoutStep.Confirm))
{
Html.RenderAction("confirm", "CheckoutPage",
new
{
currentPageId = Model.CurrentPage.PageId,
});
}
else if (Model.Step.Equals(CheckoutStep.ChooseSenderAddress))
{
Html.RenderAction("chooseAddress", "CheckoutPage",
new
{
CurrentPage = Model.CurrentPage.PageId,
BulkOrderId = Model.BulkOrder.Id
});
}
The order is in the state 'Confirm', so the method Confirm is called via Html.RenderAction.
public ActionResult Confirm(Guid currentPageId)
{
var model = new CheckoutConfirmViewModel(null, GetCurrentPage(currentPageId));
return View("Confirm.cshtml", model);
}
The view of that method, that initiates an ajax call for a partial:
#{
Layout = "_LayoutCheckOut.cshtml";
}
#using (Ajax.BeginForm(
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "content"
}))
{
}
I have the following code in my controller that gets hit by the ajax call:
[HttpPost]
public ActionResult Confirm(Guid currentPageId, bool confirm = false)
{
if (ModelState.IsValid)
{
return chooseAddress(currentPageId, bulkOrder.Id);
}
public PartialViewResult chooseAddress(Guid currentPageId, Guid bulkOrderId)
{
...
return PartialView("ChooseAddress.cshtml", model);
}
Problem is that for some reason the chooseAddress method viewresult is still using the layout despite it being a PartialViewResult! What's causing this?
I've tried specifying Layout = null in the ChooseAddress view too, but still the layout is being rendered.

Try:
Html.RenderPartial("chooseAddress", "CheckoutPage",...
Instead of Html.RenderAction("chooseAddress", "CheckoutPage",...
Also add the [ChildActionOnly] attribute to your chooseAddress Action. Its a good practice.

I managed to fix the issue!
Cause was that I had the following lines in _LayoutCheckOut (literally like this, for a quick local set-up), which loaded scripts:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.2/jquery.validate.min.js" integrity="sha512-UdIMMlVx0HEynClOIFSyOrPggomfhBKJE28LKl8yR3ghkgugPnG6iLfRfHwushZl1MOPSY6TsuBDGPK2X4zYKg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js" integrity="sha512-f04GBpoqEZhbyjlRTuXeg8FIHDb+xfCJ0LVdqiN1fEl5B3jz3Z0SPe9IxDumOVdTeeXmKMcMJhb26VuGf1Laqw==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js" integrity="sha512-Sw6xGAALk16gIMo01Nzur7z1lrF5DLbIy/cd9JiBy4yvcQsXwEnJRMHtTg2/OIO76WGv4C1yR+mCUkOtENKDTA==" crossorigin="anonymous"></script>
Apparently when you load in scripts in your view even though it is a partial and you specify layout = null, the layout will still be used!

Related

Loading different partial view with Ajax Beginform

I just created a small test project to see if it's possible to load a different partial view on an ASP.NET MVC Razor page, but it seems like I failed to do so. Instead of replacing the "partial" div by "Partial", it opens "Partial" as a new page...
Can anyone tell me what I'm doing wrong?
Current situation:
Overview/Index.cshtml:
#model dynamic
#{
ViewBag.Title = "title";
}
<h2>title</h2>
#Html.Partial("AnotherPartial")
Overview/AnotherPartial.cshtml:
#model dynamic
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#{
ViewBag.Title = "title";
}
<h2>title</h2>
#using(Ajax.BeginForm("Post", "Overview", new AjaxOptions{InsertionMode = InsertionMode.Replace, UpdateTargetId = "partial", HttpMethod = "POST"}))
{
<button type="submit">Submit</button>
}
<div id="partial">
</div>
Report/Partial.cshtml:
#model dynamic
#{
ViewBag.Title = "title";
}
<h2>Partial</h2>
OverviewController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace AjaxWebTest.Controllers
{
public class OverviewController : Controller
{
// GET: Overview
public ActionResult AnotherPartial()
{
return PartialView();
}
public ActionResult Index()
{
return View();
}
[HttpPost]
public PartialViewResult Post()
{
return PartialView("~/Views/Report/Partial.cshtml");
}
}
}
ReportController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace AjaxWebTest.Controllers
{
public class ReportController : Controller
{
// GET: Report
public ActionResult Index()
{
return View();
}
public ActionResult Partial()
{
return PartialView();
}
}
}
EDIT:
Found the solution:
Found the problem, jQuery wasn't defined on my page, so the unobtrusive-ajax js file didn't load properly. Added following reference BEFORE my unobtrusive-ajax include:
<script src="~/Scripts/jquery-1.10.2.js"></script>
You should put your unobtrusive-ajax script reference in the Scripts section of your main view.
#model dynamic
#{
ViewBag.Title = "title";
}
<h2>Main Index page title</h2>
#Html.Partial("AnotherPartial")
#section Scripts
{
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
}
In the Layout file, #RenderSection("scripts", required: false) is called at the very bottom after loading jQuery library. The unobtrusive-ajax script is expecting jQuery to be available/already loaded.
<div id="pageContent">
#RenderBody()
</div>
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts", required: false)
So if you want to execute some js files from your other views, you need to include those in the scripts section so that when the page is fully rendered, it will be added to the bottom ( After other dependeny libraries like jQuery is loaded);
Found the problem, jQuery wasn't defined on my page, so the unobtrusive-ajax js file didn't load properly.
Added following reference BEFORE my unobtrusive-ajax include:
<script src="~/Scripts/jquery-1.10.2.js"></script>

How to use more than 1 Model on a page by using asp.net mvc 5?

I want to use 2 models. The first is on Index.cshtml page, and the second is on _Layout.cshtml page
In the controller which contains the action public ActionResult Index(){...}, I declare some values and return it to View(). Like this:
public ActionResult Index()
{
HomePageViewModel model = new HomePageViewModel();
// do something...
return View(model);
}
And in MyProjectName.Models, I write some classes to check login account and put it on the page _Layout.cshtml. Like this:
On page _Layout.cshtml:
#using MyProjectName.Models
#model MyProjectName.Models.LoginModel
#if (Model.LoginAccount != null)
{
foreach(Account acc in Model.LoginAccount)
{
#Html.ActionLink(#acc.Email, "SomeAction", "SomeController", null, new { id = "loginEmail" })
#Html.ActionLink("Logout", "SomeAction", "SomeController", null, new { id = "logout" })
}
}
The code on page _Layout.cshtml doesn't work. It said that: I have returned a model (HomePageViewModel model), but some values which I want to render is referenced from MyProjectName.Models.LoginModel
Main requirement is: the first model is used to display the content on page Index.cshtml, and the second model is used to check user login (on page _Layout.cshtml).
Can you tell me how to do that? Thank you!
In your layout use Html.Action() or Html.RenderAction() to call a ChildActionOnly method that returns a partial view for LoginModel
[ChildActionOnly]
public ActionResult Login()
{
LoginModel model = // initialize the model you want to display in the Layout
return PartialView(model);
}
and create a partial view that displays the links, then in the Layout
# { Html.RenderAction("Login", "yourControllerName") }
A better approach would be to use partial views and the ViewBag.
in your controller you would do something similar to this:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Accounts = new AccountsViewModel();
ViewBag.HomePage = new HomePageViewModel();
return View();
}
}
From here you'd pass your model from the ViewBag to a partial view
#{
AccountViewModel Accounts = (AccountViewModel)ViewBag.Accounts;
}
#Html.Partial("_accountPartial", Accounts)

Make a View partial

I have a view called drawGraph , this is accessed through the controller via;
#Html.ActionLink("DrawGraph", "drawGraph", new {id =item.data})
When i use this link the page loads on a new page (as it would) i would like to display it as a part of the page that the link is on, is there any easy way to achive this?
my controller looks like this;
public ActionResult drawGraph(string data)
{
*Bunch of code
return View(chart);
}
Yes you can, however you need to use Ajax, luckily ASP.NET MVC provides some nice convenience methods like Ajax.ActionLink
#Ajax.ActionLink("DrawGraph", "drawGraph", new {id =item.data},
new AjaxOptions
{
UpdateTargetId = "yourDiv",
InsertionMode = InsertionMode.Replace,
})
<div id="yourDiv"> </div>
Then you need to return a partial view from your action with PartialView:
public ActionResult drawGraph(string data)
{
//Bunch of code
return PartialView(chart);
}
You should not forget to include the jquery.unobtrusive-ajax.min.js in your layout/view in order to the Ajax helpers work.

Given the HttpContext.Handler how can i register Script on startup in MVC3

I have a page which open a new window with External Url (Twitter Authentication Url) now that window will authenticate the user and call back to an action method , in that action method i have to close the current window (popUp) and inform the opener that authentication succeeded,
in web forms i used to do something like this on a (postback):
(HttpContext.Current.Handler as System.Web.UI.Page).ClientScript.RegisterStartupScript(
this.GetType(),
"winPop",
"window.opener.doSomething(true);window.close()",
true);
in MVC i tried this but didnt work since there is no Current but there is :
HttpContext.CurrentHandler
which can be casted to Mvc.MvcHandler now the question arise i know MVC is totally Different but is there is any way to register script which would run at the response startup ?
I know i can make an extra view and open it in the new window and then redirect to Twitter Url and then on the call back to the same Window i would call the script simply window.opener......etc but that would mean i have to make an extra view that only have nothing but a line of javascript at the startup, so i was wondering if i can do one of the following :
Option 1: Register Start up Script like i explained above.
Option 2: Return javascript code that runs at the client (no actual view).
Description
This does not exist in MVC but you can use a ViewModel or the ViewBag for this.
Sample
_Layout.cshtml
....
<head>
<title>#ViewBag.Title</title>
#if (ViewBag.StartScript != null)
{
<script>
#Html.Raw(ViewBag.StartScript);
</script>
}
....
Your ActionMethod
public ActionResult Index()
{
ViewBag.StartScript = "alert('Hello World')";
return View();
}
You can simply forget what you were doing classic WebForms as it won't be of much of a help in ASP.NET MVC.
In ASP.NET MVC you have Models, Controllers and Views (as its name suggests). So if you wanted to call a javascript function in your view and pass it some value from your model you could do the following:
#model MyViewModel
<script type="text/javascript">
alert(#Html.Raw(Json.Encode(Model.SomeValue)));
</script>
where obviously your controller action will populate this model and pass it to the view:
public ActionResult Foo()
{
var model = new MyViewModel
{
SomeValue = "Hello world"
};
return View(model);
}
and of course a corresponding model:
public class MyViewModel
{
public string SomeValue { get; set; }
}
See? Model, View, Controller.
UPDATE:
in that action method i have to close the current window (popUp) and
inform the opener that authentication succeeded,
You don't need a model in this simple case:
public ActionResult CallbackAction()
{
return View();
}
and inside CallbackAction.cshtml:
<script type="text/javascript">
window.opener.doSomething(true);
window.close();
</script>

How do I pass the number in the route through the view to my contreller

How do I pass the number in the route throught the view to my contreller
I call a route like
(localhost)/MyThing/Create/3
What I want to do is use that 3 param and set it in the MyThing object when I insert it into the database.
I created an Edit view that is strongly typed to my thing. So by default the view asks the user for this value (but I don' want to do that).
I have these methods in my controller
public ActionResult Create()
{
var thing= new MyThing();
return View(thing);
}
and
[HttpPost]
public ActionResult Create(MyThing newThing, int thingID)
{
if (ModelState.IsValid)
{
try
{
newThing.ThingID= thingID;
DB.MyThing .AddObject(newThing);
DB.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error creating new Thing");
}
}
return View(newThing);
}
I tried commenting out the thingID code in the view but the I get an error that it can't be null. I also tried changing the first method here to take the param and set it when newing up the object but the route did work.
If I use the code above with the default view the number is editable and is zero on the screen. And when you get back to the controller the param is reset to 0.
What would be ideal is for the view to get this value (3 in my example) and show it but be read only. How would I do that?
I am using ASP.net 2 if it matters.
You have a problem with your post action as it has the thingId parameter twice: once as a property of the MyThing object and once as a separate parameter meaning that the following line is useless as the default model binder will already assign the property:
newThing.ThingID = thingID;
Now as far as your view is concerned you didn't show it but you could include this id parameter either as part of the form or as a hidden field.
For example:
<% using (Html.BeginForm(new { thingID = ViewContext.RouteData.Values["thingID"] })) { %>
...
<% } %>
And if you wanted to include it as readonly textbox:
<% using (Html.BeginForm()) { %>
<%= Html.TextBoxFor(x => x.ThingId, new { #readonly = "readonly" }) %>
...
<% } %>
Finally your post action would become:
[HttpPost]
public ActionResult Create(MyThing newThing)
{
if (ModelState.IsValid)
{
try
{
DB.MyThing.AddObject(newThing);
DB.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error creating new Thing");
}
}
return View(newThing);
}
I think you can just setup the route correctly and it will pass through when you call
(localhost)/MyThing/Create/3
So in your Global.asax or Area route registration file, you would need to do something like this:
context.MapRoute("thing_route",
"MyThing/Create/{thingID}",
new
{
controller = "MyThing",
action = "Create"
}
);

Categories

Resources