I am new to MVC coming from web forms and I am struggling getting my head around how you pass parameters into a partial view.
I have the following action result. I want to be able to append a new copy of a partial view to my page when a user clicks a button passing in na new Id. The idea is you get a small section of data but the user can request more which just gets tagged on the end with a different ID value.
Currently this works only when using the routing with an id at the end, but is then static and I cannot add any more.
public ActionResult GetAllDaysByDate(int Id)
{
DateTime theTime = DateTime.ParseExact(Id.ToString(),
"yyyyMMdd",
CultureInfo.InvariantCulture,
DateTimeStyles.None);
YourDayEntities context = new YourDayEntities();
var days = context.GetAllDaysByDate(theTime);
List<Day> day = new List<Day>();
foreach (var item in days)
{
Day d = new Day { DayId = item.DayId, DayDesc = item.DayDesc, DayDate = item.DayDate, ImgUrl = item.ImgUrl };
day.Add(d);
}
return View(day);
}
I have tried various different methods to pass a param in, such as the following.
#Html.Partial("_GetAllDaysByDatePartial", new ViewDataDictionary(new { Id = 20160101 })) );
But this is throwing an error. Am I going about this all wrong? Thanks in advance.
Related
I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);
It's continue ASP.NET MVC incorect generation url when using pagination, but there I found how fix it. How fix that when using #Html.MvcSiteMap().SiteMapPath() I can't understand.
Problem in that when in actions ShowForum or ShowTopic and when I using pagination some forum or topic. In #Html.MvcSiteMap().SiteMapPath() I get url at parent page with number of page
UPDATE
For route configuration I'm using route attribute
[HttpGet]
[Route("{forumName}", Name = "showForum", Order = 6)]
[Route("{forumName}/Page/{page}", Order = 5)]
[OutputCache(Duration = 30, VaryByParam = "forumName;page", Location = OutputCacheLocation.ServerAndClient)]
public async Task<ActionResult> ShowForum(string forumName, int page = 1)
[HttpGet]
[RefreshDetectFilter]
[Block(VisibleBlock = false)]
[Route("{forum}/{topicName}", Name = "showTopic", Order = 8)]
[Route("{forum}/{topicName}/Page/{page}", Order = 7)]
[OutputCache(Duration = 30, VaryByParam = "topicName;page", Location = OutputCacheLocation.ServerAndClient)]
public async Task<ActionResult> ShowTopic(string forum, string topicName, int page = 1)
My ForumDynamicNodeProvider
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
var rootTitle = ManagerLocalization.Get("Forums", "FORUMS");
var pageParameter = new List<string> { "page" };
var url = "~/Forums";
var attr = new Dictionary<string, object> { { "Controller", "Forums" } };
var nodes = new List<DynamicNode>
{
new DynamicNode
{
Key = "forum_home",
Title = rootTitle,
Url = url,
Attributes = attr
}
};
var forums = this._forumsService.GetAllForumsForMap();
var topics = this._forumsService.GetAllTopicsForMap();
foreach (var forum in forums)
{
var forumRouteValue = new Dictionary<string, object> { { "forumName", forum.NameTranslit } };
nodes.Add(new DynamicNode
{
ParentKey = forum.ForumId != -1 ? $"forum_{forum.ForumId}" : "forum_home",
Key = $"forum_{forum.Id}",
Title = forum.Name,
PreservedRouteParameters = pageParameter,
Controller = "Forums",
Action = "ShowForum",
RouteValues = forumRouteValue,
});
var forumTopics = topics.Where(item => item.ForumId == forum.Id);
foreach (var topic in forumTopics)
{
var topicRouteValue = new Dictionary<string, object> { { "forum", forum.NameTranslit }, { "topicName", topic.TitleTranslite } };
nodes.Add(new DynamicNode
{
ParentKey = $"forum_{forum.Id}",
Key = $"topic_{topic.Id}",
Title = topic.Title,
PreservedRouteParameters = pageParameter,
Controller = "Forums",
Action = "ShowTopic",
RouteValues = topicRouteValue,
});
}
}
return nodes;
}
The problem is that you are using the same route key name {page} in two different places in the same node ancestry in combination with PreservedRouteParameters. PreservedRouteParameters gets its data from the current request. So, it is important that a route key have the same meaning in each request in the same node ancestry. For it to work correctly with PreservedRouteParamters, you need to do three things:
Use a different route key for each separate page parameter (for example, {forumPage} and {page}).
Ensure the ancestor page parameter is passed to the request of its descendants, so when building the URL to an ancestor node the value is in the current request. The simplest way is to build the URL with the page information of all ancestors ({forumName}/Page/{forumPage}/{topicName}/Page/{page}).
Any route keys that have the same meaning between nodes should stay the same ({forumName} in both routes).
Then you need to add the parameters when building the URL of the child node. You must build the URL manually within your application because the request will not have all of the parameters unless you do.
#Html.ActionLink("TheTopicName", "ShowTopic", "Forums",
new { forumName = 1, forumPage = 2, topicName = "foo", page = 1 }, null)
The reason you must supply all of the data in the child node request is because the ancestor node needs it to build its URL. It pulls this information from the request, so it must be present in the request for it to function. MvcSiteMapProvider has no way of knowing what the current page number of the ancestor node is unless it is provided in the request by a URL that is built outside of your menu.
See the MvcSiteMapProvider-Forcing-A-Match-2-Levels project in the code download for How to Make MvcSiteMapProvider Remember a User's Position for a similar configuration and the solution. In that case, it is using productId instead of forumPage as the parameter that is preserved on the descendant nodes so you can navigate back to the parent product.
Note that you could use a similar configuration (with PreservedRouteParameters and SiteMapTitleAttribute) for your entire forum rather than using a dynamic node provider. However, in that case I would suggest you disable the /sitemap.xml endpoint and roll your own.
I found how this fix, thank you to NightOwl888. I'm not the first time understood what should to do.
First I removed initialization PreservedRouteParameters in ForumDynamicNodeProvider
Second I added in action
if (forumPage > 1)
{
var node = SiteMaps.Current.FindSiteMapNodeFromKey(forumName);
if (node != null)
{
node.RouteValues["forumPage"] = forumPage;
}
}
Also I need change generation tree in ForumDynamicNodeProvider because SiteMaps.Current doesn't work in async
I'm trying to build a very simple website to display some test data being added & updated using asp.net mvc (with razor) but whenever data is posted to my Post method, my data is not being updated. I'm trying to get a unordered list (for now) to be updated the second a post is triggered.
I'm posting my data as JSON using the following code:
string jsonDeviceData = SerializeHelper.Serialize<IDeviceData>(deviceData,
ContentTypeEnum.Json, false);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(localServerUrl);
webRequest.Method = "POST";
webRequest.ContentType = "application/json"; //"application/x-www-form-urlencoded";
byte[] deviceDataBuffer = Encoding.UTF8.GetBytes(jsonDeviceData);
Task<Stream> requestTask = webRequest.GetRequestStreamAsync();
using (Stream requestStream = requestTask.Result)
{
requestStream.Write(deviceDataBuffer, 0, deviceDataBuffer.Length);
}
Task<WebResponse> responseTask = webRequest.GetResponseAsync();
using (StreamReader requestReader = new StreamReader(responseTask.Result
.GetResponseStream()))
{
string webResponse = requestReader.ReadToEnd();
Debug.WriteLine("Web Response: " + webResponse);
}
Below is the code I'm using in the POST method. Don't worry about the logic being so simplistic and probably horrible, but I'm just dabbling with this idea. Data will be stored in SQL Server database and I'll use EF if I decide to go further with this:
[HttpPost()]
public ActionResult Index(DeviceModel model)
{
if (ModelState.IsValid && model != null)
{
var deviceViewModelList = HttpContext.Application["DeviceList"]
as List<DeviceViewModel> ?? new List<DeviceViewModel>();
if (deviceViewModelList.All(m => !string.Equals(m.Name,
model.Name,
StringComparison.InvariantCultureIgnoreCase)))
{
deviceViewModelList.Add(new DeviceViewModel(model));
}
HttpContext.Application["DeviceList"] = deviceViewModelList;
var homePageViewModel = new HomePageViewModel
{
DeviceList = deviceViewModelList
};
return RedirectToAction("Index");
}
else
{
return View();
}
}
My model is passed correctly and everything works ok when the data is posted my page is not updated, even after calling RedirectToAction("Index");
The code below gets called the first time the page is loaded and after calling the RedirectToActio("Index"):
public ActionResult Index()
{
ViewBag.Title = "Test Server";
var deviceViewModelList = HttpContext.Application["DeviceList"]
as List<DeviceViewModel> ?? new List<DeviceViewModel>();
var homePageViewModel = new HomePageViewModel
{
DeviceList = deviceViewModelList
};
return View(homePageViewModel);
}
This is the code I have in my .cshtml page:
<ul>
#if (Model?.DeviceList != null)
{
foreach (var device in Model.DeviceList)
{
<li>#device.Name</li>
}
}
</ul>
If I check Fiddler, the data, in this case, the list is build correctly.
If I press F5 my data is displayed correctly.
I've read so many articles at this stage and I still haven't got a solution, one of them being View not updated after post and while I've tried ModelState.Clear(); and as you can see from my code I'm using #device.Name which is one of the suggestion. I'm not sure about the last one.
Another article I read was ASP NET MVC Post Redirect Get Pattern but again to no avail.
I'm obviously missing something.
Most articles/samples I've been looking at refer to posting via a Form and I know I'm posting, but is that the same as posting via a Form?
Also my page's viewModel is for my page and it contains a list of devices. Is that OK rather than passing the list of device as the viewmodel to the page? The reason I'm doing this is that I will want to access other lists at a later stage.
Has anyone got any suggestions?
Much appreciated.
I have already looked at these links for references.
Link 1: ASP.Net MVC and state - how to keep state between requests
Link 2: ASP.NET MVC: Keeping last page state
I have a few pages that a user will be filling out. We will call these pages Page 1. If they get to a field that they need to select from, drop down, but need to create a new item to be included in the drop down, because it will be used again later, they go to a new page, Page 2, to create the item. After create they create the item they are returned to Page 1 to finishing filling out the form. The problem is that the Page 1 is now erased because is a new page load. I would like for this to persist for when they come back so they don't have to refill out fields.
The route I am currently Link2 using a cookie. I don't know how to set the cookie's info before it gets to the next page, or how to pass it to that page before since it is going to a GET method and not a POST.
GET method for Page 1:
public ActionResult Create()
{
var courseTitles = (from title in db.CourseTitles
join type in db.CourseTypes on title.Type equals type.CourseTypeID
select new
{
CourseTitleID = title.CourseTitleID,
Title = title.Title + " - " + type.Type
});
Course course = new Course();
if (Request.Cookies["CourseInfo"] != null) //If it's not null, set the model.
{
HttpCookie cookie = Request.Cookies["CourseInfo"];
course.ClassNumber = Convert.ToInt32(cookie.Values["ClassNumber"]);
course.CourseStartDate = Convert.ToDateTime(cookie.Values["StartDate"]);
course.CourseEndDate = Convert.ToDateTime(cookie.Values["EndDate"]);
ViewBag.CourseList = new SelectList(courseTitles, "CourseTitleID", "Title", cookie.Values["CourseTitle"]);
return View(course);
}
ViewBag.CourseList = new SelectList(courseTitles, "CourseTitleID", "Title");
return View();
}
GET and POST method for Page 2:
public ActionResult NewCourseTitle()
{
ViewBag.Type = new SelectList(db.CourseTypes, "CourseTypeID", "Type");
return View();
}
//
//Post:
[HttpPost]
public ActionResult NewCourseTitle(CourseTitle courseTitle)
{
if (ModelState.IsValid)
{
db.CourseTitles.AddObject(courseTitle);
db.SaveChanges();
return RedirectToAction("Create", "Course");
}
return View();
}
Let me know if you need more code.
You can use TempData to store objects between requests:
public ActionResult Create()
{
var courseTitles = (from title in db.CourseTitles
join type in db.CourseTypes on title.Type equals type.CourseTypeID
select new
{
CourseTitleID = title.CourseTitleID,
Title = title.Title + " - " + type.Type
});
Course course = new Course();
if (TempData["CourseInfo"] != null) //If it's not null, set the model.
{
course = TempData["CourseInfo"] as Course;
ViewBag.CourseList = new SelectList(courseTitles, "CourseTitleID", "Title", course.Title);
return View(course);
}
ViewBag.CourseList = new SelectList(courseTitles, "CourseTitleID", "Title");
return View();
}
In order to store the Course simply use TempData["CourseInfo"] = course
TempData exposes couple of options that define for how long its content is going to be persisted. You can read about it here
You could use some JavaScript to modify the GET request to NewCourseTitle so that it will contain the course data that the user entered.
With jQuery it could look roughly like this:
$(function () {
var newCourseTitleLink = $('#new-course-title-link');
newCourseTitleLink.on("click", function ()
{
document.location.href = newCourseTitleLink.attr('href') + '?' + $('#course-data-form').serialize();
});
});
Then you can create a cookie in your action method NewCourseTitle:
public ActionResult NewCourseTitle(int classNumber, ... /*other form values*/)
{
var cookie = new HttpCookie("CourseInfo");
cookie.Values.Add("ClassNumber", classNumber.ToString());
...
Response.SetCookie(cookie);
ViewBag.Type = new SelectList(db.CourseTypes, "CourseTypeID", "Type");
return View();
}
I have a following View Method in an ASP.NET MVC 3 Controller that retrieves data from Amazon SimpleDb, stores it in a list and then stores that list object in a session. But at the line where I am storing the userBox object in a session (Session["userBox"] = userBox), I am getting a NullReferenceException. I am sure that userBox is not null. Even if I try to store a simple string in a session (like Session["userBox"] = "test") I still get NullReferenceException.
Here is the code:
public ActionResult SetSidebarAccountBoxSessions(string id)
{
string selectExpression = "select * from MySimpleDBDomain where itemName()='" + id + "'";
SelectRequest sreq = new SelectRequest().WithSelectExpression(selectExpression);
SelectResponse sres = sdb.Select(sreq);
List<User> userBox = new List<User>();
if (sres.IsSetSelectResult())
{
SelectResult selectresult = sres.SelectResult;
foreach (Item item in selectresult.Item)
{
string a = item.Name;
userBox.Add(new User
{
imageThug = item.Attribute[0].Value,
name = item.Attribute[3].Value,
bio = item.Attribute[1].Value
});
}
}
Session["userBox"] = userBox;
return View();
}
I am calling this SetSideBarAccountBoxSessions(id) method from another controller method:
HomeController hc = new HomeController();
hc.SetSidebarAccountBoxSessions(item.Name);
Can this be the problem? Please help.
I think this problem is related to the fact that you create HomeController by yourself. You can try to use TransferToRouteResult to transfer the action to HomeController.
You an find the code of TransferToRouteResult in this link:
How to simulate Server.Transfer in ASP.NET MVC?