MVC - Custom Route Hijacking with custom model and existing templates
Hello all,
I am trying to work on the CWS reboot where I am rendering out a member's profile page into the Umbraco site chrome, so I am currently hijacking the route with a custom controller as mentioned in the docs here:
http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
I am painfully close to resolving this after a lot of great help from the community. The final hurdle I am trying to overcome is that my navi partial view is throwing a YSOD
The model item passed into the dictionary is of type
'CWSStart.Web.Models.ViewProfileViewModel', but this dictionary requires a model item of type
'Umbraco.Web.Models.RenderModel'.
If I remove the partial in my top level Master.cshtml view/template then the page renders perfectly fine by mixing my custom model in with the rest of the umbraco page layout as soon as I add my partial back in it breaks.
@Html.Partial("~/Views/Partials/Navi.cshtml")
Here is my navi partial I am using UmbracoViewPage as I need some Umbraco stuff like .IsAncestorOrSelf()
Because the line won't work as the model is totally different.
var home = Model.Content.AncestorOrSelf("CWS-Home");
YSOD that it throws, which is because my view model does not have Content in it.
CWSStart.Web.Models.ViewProfileViewModel' does not contain a definition for 'Content' and no extension method 'Content' accepting a first argument of type 'CWSStart.Web.Models.ViewProfileViewModel'
Never mind, I missed that you were hijacking. Try what Morten said.
Ps. Why ARE you hijacking and still doing a lot of logic in the view? Isn't the point of hijacking that you can make your views really clean because you can do businesslogic in the controller?
It's a genuine question, I still haven't been able to wrap my head around the benefits of hijacking routes, it seems so unnecessary to me for most things except for maybe cleaning up your view.
@Morten I am unable to do that either as my top level master template/view is using this
@inherits UmbracoViewPage<dynamic>
So I am unable to pass Model.Content as a model to the partial . If i have try to do that I gett this YSOD.
'System.Web.Mvc.HtmlHelper<dynamic>' has no applicable method named 'Partial' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method
So I have a custom route in the app startup event:
//Create our custom MVC route for our member profile pages
RouteTable.Routes.MapRoute(
"memberProfileRoute",
"profile/{profileURLtoCheck}",
new
{
controller = "ViewProfile",
action = "Index"
});
Here is the controller for binding our member to the ViewProfile ViewModel
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using CWSStart.Web.Models;
using umbraco.cms.businesslogic.member;
using Umbraco.Core.Models;
using umbraco.MacroEngines;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace CWSStart.Web.Controllers
{
public class ViewProfileController : RenderMvcController
{
public ViewProfileController() : this(UmbracoContext.Current)
{
}
public ViewProfileController(UmbracoContext umbracoContext) : base(umbracoContext)
{
}
public override ActionResult Index(RenderModel model)
{
//Get profileURLtoCheck
string profileURLtoCheck = Request.RequestContext.RouteData.Values["profileURLtoCheck"].ToString();
//Create a view model
ViewProfileViewModel profile = new ViewProfileViewModel();
//Check we have a value in the URL
if (!String.IsNullOrEmpty(profileURLtoCheck))
{
//Try and find member with the QueryString value ?profileURLtoCheck=warrenbuckley
Member findMember = Member.GetAllAsList().FirstOrDefault(x => x.getProperty("profileURL").Value.ToString() == profileURLtoCheck);
//Check if we found member
if (findMember != null)
{
//Increment profile view counter by one
int noOfProfileViews = 0;
int.TryParse(findMember.getProperty("numberOfProfileViews").Value.ToString(), out noOfProfileViews);
//Increment counter by one
findMember.getProperty("numberOfProfileViews").Value = noOfProfileViews + 1;
//Save it down to the member
findMember.Save();
//Got the member lets bind the data to the view model
profile.Name = findMember.Text;
profile.MemberID = findMember.Id;
profile.EmailAddress = findMember.Email;
profile.Description = findMember.getProperty("description").Value.ToString();
profile.LinkedIn = findMember.getProperty("linkedIn").Value.ToString();
profile.Skype = findMember.getProperty("skype").Value.ToString();
profile.Twitter = findMember.getProperty("twitter").Value.ToString();
profile.NumberOfLogins = Convert.ToInt32(findMember.getProperty("numberOfLogins").Value.ToString());
profile.LastLoginDate = DateTime.ParseExact(findMember.getProperty("lastLoggedIn").Value.ToString(), "dd/MM/yyyy @ HH:mm:ss", null);
profile.NumberOfProfileViews = Convert.ToInt32(findMember.getProperty("numberOfProfileViews").Value.ToString());
}
else
{
//Couldn't find the member return a 404
return new HttpNotFoundResult("The member profile does not exist");
}
}
else
{
//Couldn't find the member return a 404
return new HttpNotFoundResult("No profile URL parameter was provided");
}
//Return template with our profile model
return CurrentTemplate(profile);
}
}
}
And here is my view to render the profile out of the member in my site chrome:
@Html.Action("Index", "uCommentsyContactFormSurface", new { Model.CurrentPage })
public class uCommentsyContactFormSurfaceController : SurfaceController
{
public ActionResult Index(IPublishedContent currentPage)
{
// do some stuff
return PartialView("/Views/Partials/uCommentsy/Forms/uCommentsyFormContact.cshtml", new ContactFormModel());
}
You could go another direction alltogether, and make your navigation a SurfaceController instead, and the call Html.Action()? That would solve the issue?
MVC - Custom Route Hijacking with custom model and existing templates
Hello all, I am trying to work on the CWS reboot where I am rendering out a member's profile page into the Umbraco site chrome, so I am currently hijacking the route with a custom controller as mentioned in the docs here: http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
I am painfully close to resolving this after a lot of great help from the community. The final hurdle I am trying to overcome is that my navi partial view is throwing a YSOD
If I remove the partial in my top level Master.cshtml view/template then the page renders perfectly fine by mixing my custom model in with the rest of the umbraco page layout as soon as I add my partial back in it breaks.
Here is my navi partial I am using UmbracoViewPage as I need some Umbraco stuff like .IsAncestorOrSelf()
Would love any ideas you have on how to resolve this please?
Thanks. Warren
The partial view needs:
And you should call it with:
Hi Warren
The Html.Partial has an overload, that might be useful.
@Html.Partial("ViewName", partialModel)
So depending on how your master.cshtml look, you may be able to pass in the current page like this:
@Html.Partial("Navi.cshtml", Model.Content)
@Seb I am not sure that will work, just tried it.
Because the line won't work as the model is totally different.
YSOD that it throws, which is because my view model does not have Content in it.
Cheers
Warren
Never mind, I missed that you were hijacking. Try what Morten said.
Ps. Why ARE you hijacking and still doing a lot of logic in the view? Isn't the point of hijacking that you can make your views really clean because you can do businesslogic in the controller?
It's a genuine question, I still haven't been able to wrap my head around the benefits of hijacking routes, it seems so unnecessary to me for most things except for maybe cleaning up your view.
@Morten I am unable to do that either as my top level master template/view is using this
So I am unable to pass Model.Content as a model to the partial . If i have try to do that I gett this YSOD.
Cheers
Warren
@Seb I am hijacking a route for a custom member profile view
http://site.co.uk/profile/seb
So I have a custom route in the app startup event:
Here is the controller for binding our member to the ViewProfile ViewModel
And here is my view to render the profile out of the member in my site chrome:
Hi Warren,
Try to inherit your ViewProfileModel from RenderModel.
It is stated somewhere on this page : http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
This is how I did it in uCommentsy..
In the layout:
@Html.Action("Index", "uCommentsyContactFormSurface", new { Model.CurrentPage })
Hope that helps.
How do I inherit from RenderModel on my custom view model? Tried just doing this but this is not quite right
public class ViewProfileViewModel : RenderModel { [HiddenInput(DisplayValue = false)] public int MemberID { get; set; }
}
Why do you have the
In the master? Seems to me it serves no purpose?
You could go another direction alltogether, and make your navigation a SurfaceController instead, and the call Html.Action()? That would solve the issue?
I have removed the inherits in the top level template/view as you suggested.
Yes it seems it may be the route I have to go, but seems a bit overkill to me to have a surface controller to do a simple navigation.
If I can do it without a surface controller that would be fantastic if anyone can come up with a solution.
Cheers,
Warren
Any resolution on this? I'm trying to get a property from a node with your Warren's new starter kit and am running into the same issue.
is working on a reply...