EPiServer MVC currentPage issues - c#

I'm currently having some problems with the following codesnippets which seems almost identical to me, but behaves differently.
These snippets are from two different projects I've been working on, and they are built the same way but only one of them works correctly.
These are the Forms where I enter the controllers:
Form 1, inside a twitter bootstrap dropdown menu, located in the _Layout file:
#using (Html.BeginForm("EditProfile", "ProfilePage", FormMethod.Post))
{
<li>
<button type="submit" class="dropdownButton">Redigera Profil</button>
</li>
}
Form 2, tried different locations but right now it's in a table in a Block view:
<td>
#using (Html.BeginForm("EditProfile", "ProfilePage", FormMethod.Post))
{
<button type="submit">Redigera profil</button>
}
</td>
Both seems pretty identical, right?
Now here are the controllers
Controller 1:
public ActionResult EditProfile(ProfilePage currentPage)
{
var model = new ProfilePageViewModel(currentPage);
model.CurrentUser = ConnectionHelper.GetUserInformationByEmail(User.Identity.Name);
return View("EditProfile", model);
}
Controller 2:
public ActionResult EditProfile(ProfilePage currentPage)
{
ProfilePageViewModel model = new ProfilePageViewModel(currentPage);
model.currentUser = ConnectionHelper.GetCurrentUserByEmail(User.Identity.Name);
return View("EditProfile", model);
}
Also pretty much identical.
I've added the same routing in both projects:
protected override void RegisterRoutes(RouteCollection routes)
{
base.RegisterRoutes(routes);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" });
}
Now here's the problem:
Form 1 and controller 1 works perfectly and recieves the ProfilePage currentPage without any problems, but form 2 and controller 2 doesn't work and gets null value.
As I stated earlier Form 1 is posted on the _Layout page and Form 2 is posted from a Block which is rendered within an mvc #section. I don't think this is the problem because I've tried to access the controller from different parts of the page, but it's not working anywhere - but in the other project it's working everywhere, which is driving me insane.
Does anyone have any idea why it is like this? I've stepped through both of them while debugging but the only difference is that one works and the other doesn't.
Thanks in advance
deSex
EDIT :
Here I render a section called "content", where almost everything will be rendered.
<div id="content">
#RenderBody()
#RenderSection("content", false)
</div>
My startpage has a ContentArea for blocks, rendered within this section:
#model Intranet.Models.ViewModels.StartPageViewModel
#section content{
#if (User.Identity.IsAuthenticated)
{
<div class="contentArea">
#Html.PropertyFor(x => x.MainContentArea)
</div>
}
}
And here is the controller that inherits from BlockController:
public class ProfileBlockController : BlockController<ProfileBlock>
{
public override ActionResult Index(ProfileBlock currentBlock)
{
ProfileBlockViewModel model;
if (currentBlock != null)
{
model = new ProfileBlockViewModel(currentBlock);
}
else
{
model = (ProfileBlockViewModel)Session["model"];
}
model.CurrentUser = ConnectionHelper.GetCurrentUserByEmail(User.Identity.Name);
var availableStatuses = ConnectionHelper.GetAllOfficeStatuses();
availableStatuses.Remove(model.CurrentUser.OfficeStatus);
model.AvailableStatusChanges = availableStatuses;
Session["model"] = model;
return PartialView(model);
}
}

The "currentPage" route value (i.e. parameter) will only be set by EPiServer's page route. It will always be null in a block controller.
However, you can get the page of the current request in a block controller with:
PageRouteHelper.Page
If the block is being rendered as part of a request for a profile page, you'll be able to get that profile page through PageRouteHelper.

Related

See ASP.NET MVC Index page in C# be populated with data from model without ID in the URL

In my application I am using Entity Framework 6 and ASP.NET MVC in C#.
I have a table that has records that I plan on populating my Index page with. How do I populate the index page without having the system add the id of the record to the URL. See example below. I have already looked at routing but with adding custom route you are forced to add more text to the url when all I want is the URL to show up as example.com. I don't want and don't need example.com/MenuRecords/Details/20 for a user to see.
So example.com should load the following data from the model below in the index view of the HomeController.
index.cshtml page calling the model data shown below:
#model example.Models.tblMenuRecords
#Model.ThisWeeksBestDrink
#Model.ThisWeeksBestAppetizer
#Model.ThisWeeksBestDesert
#Model.ThisWeeksBestLunchSpecial
This is the cntroller action method:
public ActionResult Index()
{
return View();
}
How do I get that to work properly for the Index page? Since this is the home page that is calling data from a model I cannot have the URL have anything other than example.com .... but I do understand that when calling data from a model you do need some sort of ID but I just do not really understand how to do that.
I know that there is the route config that includes this default route that allows you to show only the name of the domain...But how is this done when you are trying to load data from the database.
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }
Is this the correct way to pass an instance of the tblMenuRecords to the view?
public ActionResult Index()
{
tblMenuRecords tblMenuRecords = db.tblMenuRecords();
return View(tblMenuRecords);
}
I think you have to fix the action
public ActionResult Index()
{
tblMenuRecords tblMenuRecords = db.tblMenuRecords.FirstOrDefault();
return View(tblMenuRecords);
}
I think your view is missed with model. View code should be like below
#model Models.MenuRecords
#{
ViewBag.Title = "Menu Records";
}
<h2>Details</h2>
<div>
<h4>Menus</h4>
<hr />
<table>
<tr>
<td>
#Html.DisplayFor(model => model.ThisWeeksBestDrink)
</td>
<td>
#Html.DisplayFor(model => model.ThisWeeksBestLunchSpecial)
</td>
</tr>
</table>
</div>
I hope, it will help you.

System.StackOverflowException after HttpPost returning view - asp mvc

The error that I get is this (no more info) :
System.StackOverflowException
Everything in my project is working fine but as soon as I post a data and after that, it should return view I get that error
and in my layout as soon as I remove the
part it working fine
#Html.Action("TuorMenu", "Home", new { area = "Site" })
simplify layout
#{
Layout = null;
}
<!DOCTYPE html>
html dir="rtl" lang="fa">
<head>
</head>
<body>
#Html.Action("TuorMenu", "Home", new { area = "Site" })
#RenderBody()
</body>
</html>
and the partial created like this
[HttpGet]
[OutputCache(Duration = 86400, VaryByParam = "none")]
[ChildActionOnly]
public ActionResult TuorMenu()
{
MenuViewModel vmg = new MenuViewModel();
vmg.TourGroup = _repoTourGroup.Where(p => p.Id != 15).ToList();
//vmg.BlogGroup = _repoBlogGroup.Select();
return PartialView("_TuorMenu", vmg);
}
...
#model test.ViewModels.Home.MenuViewModel
#{
Layout = null;
}
.......loading menu
I don't know is there anything wrong with the loading partial view
or its just problem with returning view after HttpPost be its just forking fin in other pages but get that error when I try to access a view with a form HTTP post action
and about the HttpPost,
let assume that there is view "A" that had the Form that is HttpPost and after that fires, we should get the created view BUT it just returns the above error
just get your info in LayoutView
if you using the DI
first inject your repo
var _repoMenu = DependencyResolver.Current.GetService<IMenuRepository>();
var MenuModel = _repoMenu.Select();
then instead of
#Html.Action("TuorMenu", "Home", new { area = "Site" })
use partial
#Html.Partial("~/Areas/.../_TourMenu.cshtml",MenuModel)

Best way to handle add/view/delete on one page

What I want to do
I am very new to MVC.
I'm trying to create a page that allows users to perform the following actions on the same page:
View the list (table)
Add a new item (Filling the form and clicking the Add button should update the table)
Delete an item from the list (Clicking the Delete button in a row should update the table)
A simple example looks like this but I actually have two lists on one page (Fees and Costs):
Question
What would be the best way to achieve this?
Should I go with Dylan Beattie's method posted here which would look something like this?
public ActionResult MyAction(string submitButton, MyViewModel form)
{
switch (submitButton)
{
case "AddFee":
return (AddFee(form));
case "AddCost":
return (AddCost(form));
case "RemoveFee":
return (RemoveFee(form));
case "RemoveCost":
return (RemoveCost(form));
}
}
public ActionResult AddFee(MyViewModel form)
{
Fee newFee = ....; // Get entered data from `form`
_repository.InsertFee(newFee);
return View("Create"); //Back to the original page
}
Or is there any other recommended methods to handle this such as using JavaScript?
You could create the table as a partial view and re render this via ajax.
Wrap the partial view in a div and Wrap the form in #using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.
Here is a simple example
public class HomeController : Controller
{
public ActionResult Index()
{
MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };
return View(vm);
}
public ActionResult DA(MYvm vm)
{
vm.name = "CHANGED";
return PartialView("Part", vm);
}
View:
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "Home Page";
}
#using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId = "cont", HttpMethod = "Get" }))
{
<div>
Id: #Html.EditorFor(model => model.id)
</div>
<div>
Name: #Html.EditorFor(model => model.name)
</div>
<input type="submit" value="SubmitForm" />
}
<div id="cont">
#{Html.RenderPartial("part", Model);}
</div>
Partial View
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "part";
}
<h2>part</h2>
#Model.name
Should I go with [previous SO answer]
No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).
Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.
As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:
the containing page
the edit form
the list with remove
the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).
1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)
controller:
[HttpGet]
public ActionResult AddCost()
{
var model = new Cost();
return PartialView(model);
}
[HttpPost]
public void AddCost(Cost model)
{
if (ModelState.IsValid) {
db.SaveCost(model);...
}
}
form Views/Home/AddCost.cshtml:
#using (Ajax.BeginForm(...
{
<div class='editor-label'>#Html.LabelFor(model=>model.Description)</div>
...etc...
}
I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)
controller
public ActionResult CostList()
{
var model = db.loadCosts(); ...
return PartialView(model);
}
list, Views/Home/CostList.cshtml
#model IEnumerable<ViewModels.Cost>
<table>
<thead>
<tr>
<th>Cost Description</th>
...
<tbody>
#foreach (var cost in Model.Costs)
{
<tr data-id='#cost.Id'>
<td>#Html.DisplayFor(x=>cost.Description)</td>
...
<td><a href='#' class='remove-button'>Remove</a></td>
}
...
2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:
<div id="body">
<div id="formWrapper">
#Html.Action("AddCost")
</div>
<div id="listWrapper">
#Html.Action("ListView")
</div>
</div>
if you already load the data for the page, you can pass it directly to the partial, but there's no need:
#Html.Partial("ListView", Model.Costs)
this allows you to refresh the list via an ajax call, something like:
function reloadCostList() {
$(".listWrapper").load("Home/CostList");
}
(ideally, $.ajax and add some fancy UI to indicate loading)
3 Add a remove action to your controller
[HttpPost]
public void RemoveCost(int id)
{
}
4 Wire up the Remove link
$(function() {
$(".remove-button").click(function() {
var id = $(this).closest("tr").attr("id");
$.post("/Home/RemoveCost/" + id, null, function() {
$(".listWrapper").load("Home/CostList");
// or reloadCostList(); from above
// or:
//$(".listWrapper tr[id=" + id + "]").hide();
});
});
}
rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)

Get the name of view who called controller in ASP.NET MVC

Is there any way to get the name of View that called method in controller and save it for example in some custom variable inside that controller's method?
For example:
I have one View that uses Ajax to get to InfinateScroll method in controller:
<div class="container-post">
<div id="postListDiv">
#{Html.RenderAction("PostList", "Posts", new { Model = Model });}
</div>
<div id="loadingDiv" style="text-align: center; display: none; margin-bottom: 20px;">
<img alt="Loading" src="#Url.Content("~/images/ajax-loader.gif")" />
</div>
</div>
<script src="#Url.Content("~/Scripts/jquery-1.10.2.min.js")"></script>
<script type="text/javascript">
var BlockNumber = 2;
var NoMoreData = false;
var inProgress = false;
$(window).scroll(function () {
if ($(window).scrollTop() == $(document).height() - $(window).height() && !NoMoreData && !inProgress) {
inProgress = true;
$("#loadingDiv").show();
$.post("#Url.Action("InfinateScroll", "Posts")", { "BlockNumber": BlockNumber },
function (data) {
BlockNumber = BlockNumber + 1;
NoMoreData = data.NoMoreData;
$("#postListDiv").append(data.HTMLString);
$("#loadingDiv").hide();
inProgress = false;
});
}
});
</script>
I use this View on two pages. In one case I'm using it to show only posts from specific user (user who is logged in), and on the other view I'm showing posts from all users in database(similar to Facebook wall where you can see only your post, and NewsFeed where you can not only your's but also posts from your frineds).
For some reason I would like to know which page was active when call for InfinateScroll method was made.
This is the method where I would like to make some differences between those two pages so I can do some check out's later.
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber)
{
int BlockSize = 5;
var posts = PostManager.GetPosts(BlockNumber, BlockSize);
JsonModel jsonModel = new JsonModel();
jsonModel.NoMoreData = posts.Count < BlockSize;
jsonModel.HTMLString = RenderPartialViewToString("PostList", posts);
return Json(jsonModel);
}
This method gets posts using helper method GetPosts and it's used for showing more posts on scroll.
You can get the name of the current View from inside the view using the following:
#Path.GetFileNameWithoutExtension(Server.MapPath(VirtualPath))
Source: How to get the current view name in asp.net MVC 3?
so you could add this as a routevalue into your #Url.Action like so:
#Url.Action(
"InfinateScroll",
"Posts",
new{callingView=Path.GetFileNameWithoutExtension(Server.MapPath(VirtualPath))})
Then you could add a parameter to your controller method
public ActionResult InfinateScroll(int BlockNumber, string callingView)
You can create a hidden variable in the html like this -
<input type="hidden" id="pageName" value="myPage1" />
Add an extra parameter to your Action -
public ActionResult InfiniteScroll(int BlockNumber, int pageName)
And then, in your jquery code, when you post, send in pageName as well.
$.post("#Url.Action("InfinateScroll", "Posts")", { "BlockNumber": BlockNumber, "pageName": $('#pageName').val() },
Hope this helps.
In one case I'm using it to show only posts from specific user... and
on the other view I'm showing posts from all users in database...
Putting your desired logic on the view is unsafe, especially if showing data is user-based or user-specific. However, if you insists on having the logic on the view then you should pass along another variable to the controller like so:
$.post("#Url.Action("InfinateScroll", "Posts")",
{ "BlockNumber": BlockNumber, "UserId": userId },
// rest of your code goes here...
});
You then should have another parameter in your controller:
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber, int userId)
{
//filter your data based on the "userId" parameter
}
But like I mentioned this is unsafe because someone can easily pass in a valid "userId" and get to the data when you don't want them to. So the safest (or safer) way is to have the "filtering logic" in your controller like so:
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber)
{
// a context based logic
var userId = GetLoggedInUserId();
// that method could return null or zero
// and depending on how you approach it
//filter your data based on the "userId"
}

A view that was not created gets displayed

I don't have a view called Test. But, there's a method in the Home controller called Test.
Everything works fine, the Test method gets executed and redirected to Index view as given in the code. However, in the browser the URL is ../Home/Test and not ../Home/Index. I don't have a View called Test so why is this getting displayed ? I don't want this URL to be displayed in the browser. How can i solve this ?
View:
#using (Html.BeginForm("Test", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />
..// Other code
}
C#
public ActionResult Test(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return View("../Home/Index", m); // Will be returning the Index View
}
In URL
"../Home/Test"
Test is action thus it is working fine.
If you don't want this URL to be displayed, renamed the Action "Test" to "Index" and also update its references
and use (optional)
return View("Index", m)
If you are using the standard setting for MVC, it is not the View, but The Method, that is displayed in the browser. The method then by default returns the View (but that is optional).
So what you need to do is rename you Test Method to Index and place [HttpPost] on top of it.
[HttpPost]
public ActionResult Index(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return View(m);
}
return View("../Home/Index", m);
This will not redirect you to Index; it will simply display your Index view. If you want browser to automatically change your URL from /Test to /Index you have to instead do this:
return RedirectToAction("Index");
Try this:
public ActionResult Test(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return RedirectToAction("Index");
}

Categories

Resources