PDA

View Full Version : How to implement alternative to Html.RenderAction



craigthames
Dec 14, 2020, 2:46 PM
In our application, we were using Html.RenderAction to add partial views to specific regions in the ViewPort of the main page. Since we just moved to Ext 7.2 (and consequently .Net Core). This allows us to modularly add (or leave out) partialViews to the ViewPort on the calling pages when needed. Here's a stripped down example of what we did before...

_Header

@using Ext.Net;
@using Ext.Net.MVC;
@using FIS.Presentation.EWAPPM.Models

@model HeaderModel

@(Html.X().Menu()
.ID("mnuBar")
.Layout(LayoutType.HBox)
.BodyCls("menuOverride")
.Floating(false)
.ShowSeparator(false)
.Region(Region.North)
.Height(40)
.Items(
Html.X().Label()
.Text("EWAPPM 2.0")
.Width(250)
.BaseCls("ewappmLogo")
.MarginSpec("5 0 0 15"),

//------------------------------------
// Home
//------------------------------------
Html.X().MenuItem()
.Text("Home")
.Href(Url.Content("~/Home/GoToDashboard"))
.BaseCls("menuItemStyleDefault")
.ActiveCls("menuItemStyleHover")
.Width(115),

//------------------------------------
// Profile
//------------------------------------
Html.X().MenuItem()
.Text("My Stuff")
.Href(Url.Content("~/User/GoToUserDashboard"))
.BaseCls("menuItemStyleDefault")
.ActiveCls("menuItemStyleHover")
.Width(120 + (Model.MyStuffCount.Length * 5))
.Plugins(
Html.X().Badge()
.Text(Model.MyStuffCount)
.AlignmentSpec("r-r")
.UI(UI.Danger.ToString())
.HideEmpty(true)
.RenderToBody(true)
),

Html.X().ToolbarFill(),

//------------------------------------
// Sign Out
//------------------------------------
Html.X().MenuItem()
.Cls("menuItemStyleDefault")
.ActiveCls("menuItemStyleHover")
.Text("Sign Out")
.Icon(Icon.DoorOut)
.Width(120)
.Href(Url.Content("~/Security/SignOut"))
)
)


Dashboard

@using Ext.Net;
@using Ext.Net.MVC;
@using FIS.Presentation.EWAPPM.Models

@{
Layout = "~/Views/Shared/_StandardLayout.cshtml";
ViewBag.Title = "Dashboard";
}

@model DashboardModel

<div class="grid-row">
@{ Html.RenderAction("RenderTOCSideBar", "Home"); }
@{ Html.RenderAction("RenderHeader", "Common"); }
@{ Html.RenderAction("RenderCompatibilityWarning", "Common"); }

@(Html.X().Viewport()
.Layout(LayoutType.Border)
.ID("vpEWAPPM")
.Items(
Html.X().Panel()
.Layout(LayoutType.Border)
.Region(Region.Center)
.Items(
Html.X().Portal()
.ID("portalDashboard")
.Border(false)
.Region(Region.Center)
.Items(
Html.X().PortalColumn()
.Items(
Html.X().Portlet()
.Title("My Favorites")
.HideCollapseTool(true)
.Closable(false)
.Height(250)
.Frame(false)
.Border(true)
.Layout(LayoutType.Fit)
.Icon(Icon.PageWhiteStack)
.MarginSpec("0 0 10 0")
.Tools(
Html.X().Tool()
.Type(ToolType.Help)
.ToolTip("Help")
.DirectEvents(de =>
{
de.Click.Url = Url.Action("Dashboard_Legend");
})
)
.Items(
Html.X().GridPanel()
.EnableColumnHide(false)
.EnableColumnMove(false)
.Store(
Html.X().Store()
.ID("storeMyItems")
.DataSource(Model.UserFavoriteDocuments)
.AutoLoad(true)
.Model(Html.X().Model()
.Fields(
Html.X().ModelField().Name("DocKey").Type(ModelFieldType.Int),
Html.X().ModelField().Name("DocumentNumber").Type(ModelFieldType.String),
Html.X().ModelField().Name("DocTitle").Type(ModelFieldType.String)
)
)
)
.ColumnModel(
Html.X().Column()
.Hidden(true)
.Hideable(false)
.DataIndex("DocKey"),
Html.X().Column()
.DataIndex("DocumentNumber")
.Text("Document #")
.Flex(1)
.Resizable(false)
.Hideable(false)
.Filter(Html.X().StringFilter()),
Html.X().Column()
.DataIndex("DocTitle")
.Text("Document Title")
.Flex(3)
.Resizable(false)
.Hideable(false)
.Filter(Html.X().StringFilter())
)
.Plugins(Html.X().GridFilters())
.View(
Html.X().GridView().TrackOver(true)
.DirectEvents(de =>
{
de.ItemDblClick.Url = Url.Action("GoToViewDocumentDashboard");
de.ItemDblClick.ExtraParams.Add(new Parameter { Name = "docKey", Value = "record.data.DocKey", Mode = Ext.Net.ParameterMode.Raw });
})
)
.BottomBar(Html.X().StatusBar().Text("Double-click a row to view the item."))
)
),
Html.X().PortalColumn()
.Items(
Html.X().Panel()
.Title("What's New")
.Icon(Icon.New)
.BodyPadding(5)
.Height(250)
.Frame(false)
.Border(true)
.Layout(LayoutType.Fit)
.Html(Model.PanelInfo.PanelHTML)
.HideCollapseTool(true)
.Closable(false)
.AutoScroll(true)
.MarginSpec("0 0 10 0")
.Tools(
Html.X().Tool().CustomType("edit").ToolTip("Edit").Hidden(!Model.AllowUpdateWhatsNew)
.DirectEvents(de =>
{
de.Click.Url = Url.Action("GoToEditPanel", "Admin");
de.Click.ExtraParams.Add(new Parameter { Name = "panelID", Value = "pnlWhatIsNew", Mode = Ext.Net.ParameterMode.Value });
de.Click.ExtraParams.Add(new Parameter { Name = "panelName", Value = "What's New", Mode = Ext.Net.ParameterMode.Value });
})
)
)
)
)
)
)
</div>

CommonController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using Ext;
using Ext.Net;
using Ext.Net.MVC;
using FIS.Presentation.EWAPPM.Models;
using FIS.Presentation.EWAPPM.Helpers;
using FIS.Presentation.EWAPPM.Security;
using FIS.Infrastructure.EWAPPM.Domain;

namespace FIS.Presentation.EWAPPM.Controllers
{
public class CommonController : Controller
{
#region Global Variables

private HomeService _ewappmService = new HomeService(MvcApplication.Environment);
private UserService _userService = new UserService(MvcApplication.Environment);
private TrainingService _trainingService = new TrainingService(MvcApplication.Environment);

#endregion Global Variables

[AllowAnonymous]
public ActionResult RenderCompatibilityWarning()
{
// This variable was originally used to handle the text in the label.
// The message was moved to the label itself, but I left the plumbing in case we can leverage it for other purposes later.
string labelMessage = "";

return new Ext.Net.MVC.PartialViewResult
{
ViewName = "_CompatibilityWarning",
RenderMode = RenderMode.AddTo,
ContainerId = "vpEWAPPM",
Model = labelMessage
};
}

[EWAPPMAuthorize(ClaimType = "role", ClaimValues = "user,approver,admin")]
public ActionResult RenderHeader()
{
HeaderModel model = new HeaderModel();

return new Ext.Net.MVC.PartialViewResult
{
ViewName = "_Header",
RenderMode = RenderMode.AddTo,
ContainerId = "vpEWAPPM",
Model = model
};
}
}
}

We've used this technique to do Headers, Footers and various Sidebars appropriate to the site and specific content. We are hoping to find something that will allow us to be able to do the same thing in the new environment.

It looks like we would need to use ViewComponents, but aren't quite sure how to handle the concept of adding directly into a given container ("vpEWAPPM" in the case of my example) since we don't have PartialViewResult.

Hope all this makes sense... and this question helps others out in the future.

Thanks for any help you guys can provide!
Craig

craigthames
Dec 15, 2020, 6:52 PM
Anybody out there have a suggestion? I think my biggest hurdle with this approach is that we haven't located an EXT.Net implementation of the PartialViewResult in this version of EXT (before we were getting it from Ext.Net.MVC).

PartialViewResult is also going to be a hurdle for my team when we do more complex popup windows.

geoffrey.mcgill
Dec 15, 2020, 9:54 PM
Hi. Thanks for the code samples. We will investigate and work out how this technique would be accomplished in Ext.NET Classic 7+.

geoffrey.mcgill
Dec 17, 2020, 4:22 PM
There are issues in Ext.NET Classic v7+ with implementing your approach. We are still investigating and will figure out a solution.

fabricio.murta
Jan 08, 2021, 7:48 PM
Hello Craig!

How do you think the ComponentLoader approach would fit your needs? Maybe that's worth a try? We had a recent thread asking about it and, at least the scenario therein, worked for me just fine. Take a look on the thread:
- ComponentLoader Rendering (https://forums.ext.net/showthread.php?63062-componentLoader-rendering).

We are still working in ways related to actual partial rendering in a fashion that resembles more your scenario, but just in case that, that alternative could count towards a possibility for you, we're posting this.

Let us know what you think.

fabricio.murta
Jan 18, 2021, 8:15 PM
Hello again, Craig!

We didn't receive a follow-up from our last posts but I'll leave a few leads here for some other alternatives based on the current supported implementation of partial views in Ext.NET 7.

All these solutions works using the new TagHelpers syntax (https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro), introduced by ASP.NET Core as an alternative for the discontinued WebForms technology. For brevety on the answer, I'll draw short code blocks suppressing context. If you feel any of these are hard to understand, we can post full example, or publish a full example using it in our examples explorer github repo (https://github.com/extnet/examples.ext.net) and website (https://examples.ext.net/).

- Expression conditionals in the view

This option allows for simpler partial views while the logic of inclusion will lie within the main view.

Example:

partial view


<ext-component html="My Ext.NET Components here" />


main view


@{
if (Model.Role != Project.Roles.Guest)
{
<ext-partial name="PartialViewName" />
}
}


The partial view doesn't need an entry within the controler to be rendered. And it is just an ordinary view file without context (layout, thus html/body/head blocks), but it can have a model passed via ext-partial via model="Model" or even part of the model (model=Model.MyObject).

- Expression conditionals within each partial view

This one will look cleaner in the main page, and the partial view would basically be wrapped in condition. In this case, passing a model should usually be necessary (unless you have access to the condition thru other means, like global/static variables).

partial view


@model Project.Models.MyPageModel

@{
if (Model.Role != Project.Roles.Guest)
{
<ext-component html="My Ext.NET Components here" />
}
}


main view


@model Project.Models.MyPageModel

<ext-partial name="PartialViewName" model="Model" />


Which could as well be:

partial view


@model bool

@{
if (Model)
{
<ext-component html="My Ext.NET Components here" />
}
}


main view


@model Project.Models.MyPageModel

<ext-partial name="PartialViewName" model="Model.Role != Project.Roles.Guest" />


- View Components

This may be the most complex one for a short example, but probably the most scalable option among the one(s) above, for benefitting from different features already in ASP.NET Core. Here's the feature specific documentation, essential to get it to work: View components in ASP.NET Core | Microsoft Docs (https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components)

As far as Ext.NET is concerned, it basically involves instructing the ViewComponents to be drawn to specific page sections.

Some examples within the view page would be:



@await Component.InvokeAsync("AdminPanel")
@await Component.InvokeAsync("Header", new { role = Model.Role })
@await Component.InvokeAsync("Login", new { isLogged = Model.Role != Example3Model.Roles.Guest })


Respectively, the signatures in the view components' code files would be:

AdminPanel


public IViewComponentResult Invoke()


Header


public IViewComponentResult Invoke(Project.Models.MyPageModel.Roles role)


Login


public IViewComponentResult Invoke(bool isLogged)


The key here then is to define the location of the components in the main view and in the component.

So a view with the simpler AdminPanel component would involve:

Component definition
AdminPanel.cshtml


<ext-section target="AdminPanelViewComponent">
<ext-panel region="South" title="Administrator Panel" html="Administration panel contents" />
</ext-section>


View referencing the component
Index.cshtml


@await Component.InvokeAsync("AdminPanel")

(...)

<ext-section name="AdminPanelViewComponent" required="false" />


I tried to be as brief as possible with the examples and still give good clues how things should look in the project. Let us know if you'd needed a little further in any of these options to get started. We can post further parts of each approaches as required.

Hope this helps!