/// <summary> /// Manages script separation of concerns convention by loading the different javascript portions of each partial into a context item. /// </summary> private void RegisterJavaScript(ControllerContext controllerContext, string viewPath) { Guid?guid = null; ExtendedControllerContext extendedContext = GetExtendedControllerContext(controllerContext); if (extendedContext != null) { // When we render the view, we add JavaScript to the provided context, we identify contexts by using Guids. guid = extendedContext.Guid; } StringRenderingController controller = controllerContext.Controller as StringRenderingController; if (controller == null) { throw new InvalidOperationException(Resources.Error.ControllerBaseTypeMismatch); } if (viewPath.EndsWith(Resources.Constants.JavaScriptViewNamingExtension)) // sanity. { return; // prevent StackOverflowException. } string partial = controller.JavaScriptPartialViewString(viewPath, controller.ViewData.Model); if (partial != null) { IJavaScriptHelper javaScriptHelper = kernel.Resolve <IJavaScriptHelper>(); javaScriptHelper.Register(viewPath, partial, guid); } }
/// <summary> /// Renders a partial view as a string. /// </summary> /// <param name="partialViewName">Either the fully qualified virtual path to the View, or the View name in the current context.</param> /// <param name="controller">The controller name.</param> /// <param name="model">The view model.</param> /// <param name="context">The controller context.</param> public string PartialViewString(string partialViewName, string controller, object model, ExtendedControllerContext context) { Func<string, ControllerContext, ViewEngineResult> findView = (name, ctx) => ViewEngines.Engines.FindPartialView(ctx, name); return InternalGetViewString(partialViewName, controller, model, findView, context); }
/// <summary> /// Gets a view string aftering producing a ControllerContext where it should be rendered. /// </summary> private string InternalGetViewString(string viewName, string controller, object model, Func<string, ControllerContext, ViewEngineResult> findView, ExtendedControllerContext context) { if (!controller.NullOrEmpty()) { context.RouteData.Values["controller"] = controller; } if (viewName.NullOrEmpty()) { viewName = context.RouteData.GetActionString(); } IView view = findView(viewName, context).View; string result = RenderViewToString(context, view, model); return result; }
/// <summary> /// Used when changes are required to render a view as a string, without altering the ControllerContext for the actual request. /// </summary> private ExtendedControllerContext CloneControllerContext() { ControllerContext copy = new ControllerContext(Request.RequestContext, this); // ControllerContext property might be null, that's why we don't use that overload. /* * We would never want to have partial views rendered outside of the view, i.e. with @Html.Partial(), * emitting JavaScript code into the actual view, because that's not what would be intended. * * This simple overwrite fixes a few scenarios collaterally: * - JavaScript partials don't try to emit JavaScript, because they are rendered using the ExtendedControllerContext. * - The same applies to both partial and JavaScript views being rendered by the AjaxTransformAttribute engine. * * In addition, the only scenario likely to ever want this to happen would be something like an AJAX call, * where we'd like to return both the HTML and the JavaScript, but that can already be handled through the existing methods. */ ExtendedControllerContext context = new ExtendedControllerContext(copy); // required for requests to *.cshtml physical files, in order to render the error view. if (!context.RouteData.Values.ContainsKey(Resources.Constants.RouteDataController)) { context.RouteData.Values.Add(Resources.Constants.RouteDataController, Resources.Constants.RouteDataControllerNotFound); context.RouteData.Values.Add(Resources.Constants.RouteDataAction, Resources.Constants.RouteDataActionNotFound); } return context; }