N2 NullReferenceException on "Html.DroppableZone("h1").Render()" - c#

I'm currently evaluating N2 CMS for use for multiple websites.
We would only like to offer the 'drag' functionality to our clients, which means they can add parts to Zones on the page, fill them out, and drag them around. The "backend" functionality of the management zone will be reserved for the developers.
Therefore, I don't use the SlidingCurtain control to render. Right now, I've made a custom Admin Panel that appears when a user with the correct role is logged in. Normally, the Sliding Curtain adds a "?edit=drag" query string to your URL when you click the 'drag' functionality button, so I add this querystring automatically after logging in.
If I do this, I get a NullReferenceException to the following line:
Html.DroppableZone("H1").Render();
As of this moment, there are no parts on this DroppableZone yet, and I suppose this is what is causing this problem. How do I get around this?
I have the following H1Controller:
namespace EmptyCMS.Controllers
{
[Controls(typeof(Models.H1))]
public class H1Controller : ContentController<Models.H1>
{
public override ActionResult Index()
{
return PartialView("H1", CurrentItem);
}
}
}
And the following partialview:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.H1>" %>
<h1><%= Model.Text %></h1>
And this is my model:
namespace EmptyCMS.Models
{
[PartDefinition("H1")]
[AllowedZones("H1")]
public class H1 : ContentItem
{
[EditableTextBox("Text", 100)]
public virtual string Text { get; set; }
}
}
Can anyone spot what I'm doing wrong? Thanks a lot for any help you can give me.

Just check for null?
var zone = Html.DroppableZone("H1");
if(zone != null)
zone.Render();

Related

Asp.Net Core MVC 7 C# - Recommended approach of passing data to and from controller from a page that has multiple forms and states

I'm building a simple web portal using the .Net Core 7 MVC template in VS2022 (It's challenging to know if this is the latest recommended format and or template, Microsoft doesn't really state what is the current approach from a stack perspective).
As part of building out and focusing on MVC I looked at if PageModels were the recommended method of binding to the page or if using a simple Model and populating the page using razor syntax (Which is my preference) was the most modern recommendation (Again Microsoft documentation isn't exactly specific).
That aside, the specific problem that I have is that I'm building out a settings page that has a number of different forms and I initially thought that in order to maintain the page state of other inputs and fields that when a form posts to the controller it sends the entire page model with the form bound properties populated, perform some logic, and pass a modified model back the the main Index ActionMethod. However I can't seem to see anything specific around sending the entire model back that was used by the razor page as part of individual form submits. Or moreover, if it's even a good idea.
The page structure looks like this.
Form - Team Name
Button - Submit, just saves the team name.
List - Team Members currently in team.
Form - Search for Users by Email.
List - List of Users matching Search input, with button to Add To Team
The issue i have is that if I pass back the form properties which are bound to the model from the search input, i don't get all of the other model properties used for populating current team members list, or the team name, so I assume that would mean that the controller has to do the work of getting that data again? This seems a bit inefficient in my mind. I'm assuming there is a clever way of doing this and achieving an appropriate outcome but I'm struggling to see the light through the dark with this particular consideration. Any suggestions or nods in the right direction are appreciated.
Example Model
public class SettingsPageModel
{
public SettingsPageModel() { }
public SettingsPageModel(string teamName)
{
TeamName = teamName;
}
public SettingsPageModel(SearchModel searchModel)
{
SearchModel = searchModel;
}
public SettingsPageModel(string teamName, List<TeamMember> teamMembers, SearchModel searchModel)
{
TeamName = teamName ?? throw new ArgumentNullException(nameof(teamName));
TeamMembers = teamMembers ?? throw new ArgumentNullException(nameof(teamMembers));
SearchModel = searchModel ?? throw new ArgumentNullException(nameof(searchModel));
}
public string TeamName { get; set; } = string.Empty;
public List<TeamMember> TeamMembers { get; set; } = new();
public SearchModel SearchModel { get; set; } = new();
public DialogModel? DialogModel { get; set; }
}
public class SearchModel
{
public string? SearchTerm { get; set; }
public List<User>? SearchedUsers { get; set; }
}
Every time you submit a form you send back ONLY the input elements on that form (not the entire model).
If you have a search input and want to update only a portion of the page you would have to either:
Add more hidden input fields to that form to send for example TeamName and populate the model completely (not ideal)
Use Ajax when the form is submitted, have an Action in your controller that gets the filtered users and return a PartialView() with the list of users, and update the DOM using JavaScript when you get your ajax response (HTML)

Interpret Data produced by View Component ASP Razor MVC

I am using a View Component to create a portion of an MVC Page. The View component goes to a database, and produces a table of results if the query produces results. The query producing results means there's a problem, so I've set up the view component to only display if there were results. This query is meant to be re-usable in different parts of the site, too, because its results can impact multiple pages.
Works great for showing the error only when it's an issue!
However, the main page has a form handler for sending an E-Mail message, and I want to disable that capability when the data is invalid. I use ViewData.ModelState.IsValid to check whether the model is valid or not. However, because my Component is indicating whether or not the data is valid, I've run into an issue!
I have no idea how to check whether the component is populated or not, without somehow hardcoding it back to the original page. I can't seem to pass view data from the Component to the calling page (although the other direction works great), I can't even subscribe the page to an event because I see no way to associate the Component instance with the page!
I don't need to check the contents of the component before invalidating the page, I just need to know whether or not the component produced anything at all.
Can anybody help me do this?
Here is the code for the Component:
namespace Reports.Shared.Validation
{
public class IdentifierValidationViewComponent : ViewComponent
{
private readonly IdentifierValidationDB _IdentifierValidationContext;
public IdentifierValidationViewComponent(IdentifierValidationDB IdentifierValidationContext)
{
_IdentifierValidationContext = IdentifierValidationContext;
}
public List<IdentifierValidation> InvalidIdentifiers { get; set; }
public async Task<IViewComponentResult> InvokeAsync(string date)
{
InvalidIdentifiers = await _IdentifierValidationContext.IdentifierValidations.FromSqlRaw("EXEC Reports.IdentifierValidation {0}", date).ToListAsync();
return View(InvalidIdentifiers);
}
}
}
Here is the Partial View that the Component is rendering:
#model List<Reports.Models.Shared.Validation.IdentifierValidation>
#if (Model.Count() > 0)
{
<div id="InvalidIdentifiers" class="alert-danger">
<h2>Values are missing Identifiers!</h2>
<ul>
#foreach (var invalid in Model)
{
<li>
#invalid.SecName has no Identifier.
</li>
}
</ul>
</div>
}
Finally, I invoke the component on the main page with this line
#await Component.InvokeAsync("IdentifierValidation", new { date = Model.Date })
All I want to do is have the main page check if the component is actually producing any HTML or not, and then invalidate ViewData.ModelState.IsValid if so.
I figured it out!
In my case, because I only need to check during the initial load of the page whether or not the table has produced results, and because the status doesn't need to be retained between refreshes, I can use ViewContext.HttpContext.Items[] to store a flag indicating whether or not the table has been populated. I needed to make changes to the Component and the main page, but with the following changes, it seems to work!
Controller:
namespace Reports.Shared.Validation
{
public class IdentifierValidationViewComponent : ViewComponent
{
private readonly IdentifierValidationDB _IdentifierValidationContext;
public IdentifierValidationViewComponent(IdentifierValidationDB IdentifierValidationContext)
{
_IdentifierValidationContext = IdentifierValidationContext;
}
public List<IdentifierValidation> InvalidIdentifiers { get; set; }
public async Task<IViewComponentResult> InvokeAsync(string date)
{
InvalidIdentifiers = await _IdentifierValidationContext.IdentifierValidations.FromSqlRaw("EXEC Reports.IdentifierValidation {0}", date).ToListAsync();
if(InvalidIdentifiers.Count() > 0)
{
ViewContext.HttpContext.Items["InvalidIdentifier"] = "Detected";
}
return View(InvalidIdentifiers);
}
}
}
And now, we can invoke the controller as such:
#await Component.InvokeAsync("IdentifierValidation", new { date = Model.Date })
#if (ViewContext.HttpContext.Items["InvalidIdentifier"] != null)
{
ViewData.ModelState.AddModelError("InvalidIdentifier", "An Invalid Identifier has been Detected!");
}
When we do this, we are invalidating the model state only if the partial produced results when it was loaded on page refresh!
Of course, if you're doing something fancier with it, like using javascript/ajax to refresh the content of the partial on the client side, this will not work.

How to get dynamic form submission record count in Orchard CMS

I made a register page with dynamic form in Orchard CMS, and received new requirements of checking record count.
I have no idea about how to do this, I looked into the SubmissionAdminController.cs in Orchard.DynamicForms.Controllers folder, but still could not find a way.
I'm thinking to get the record count from my cshtml view page and check it in different parts, is it possible?
To get the record count of the stored submissions, inject or resolve an IRepository<Submission>, and use the Count() method to count all items. Note that the Count() method accepts an expression, which allows you to filter by form name for example. For reference, this is what the Submission class looks like:
namespace Orchard.DynamicForms.Models {
public class Submission {
public virtual int Id { get; set; }
public virtual string FormName { get; set; }
[StringLengthMax]
public virtual string FormData { get; set; }
public virtual DateTime CreatedUtc { get; set; }
}
}
When you have an IRepository<Submission>, this is how you would count all submissions in a form called "MyForm":
var count = submissionRepository.Count(x => x.FormName == "MyForm");
If you don't have a controller or custom part or anything to inject this IRepository into, then you could resolve the repository directly form your view like this:
#{
var submissionRepository = WorkContext.Resolve<IRepository<Submission>>();
var submissionCount = submissionRepository.Count(x => x.FormName == "MyForm");
}
Make sure to import the proper namespaces:
Orchard.DynamicForms.Models for Submission
Orchard.Data for IRepository<T>
However, if you need to display this number in multiple places, it's best to create a shape so that you can reuse it. Even better would be to not resolve the repository from the shape template directly, but via an IShapeTableProvider. The primary reason for that becomes clear when you start overriding your shape template or start providing shape alternates, both in which cases you'll have duplicate logic in all of your shape templates, which isn't very DRY of course. And there's the more philosophical issue of separation of concerns: you don't want data access code in your views. Rather, use that code from a controller, driver or shape table provider.

Property accessible from all pages but change only once per session

I don't think the title is the best description for my problem but, the problem is as following:
I have a base class like:
public class BasePage : System.Web.UI.Page
{
public static ProjectDTO Project { get; set; }
// some other code
// Setting the project once per session.
public void SetProject()
{
Project = (ProjectDTO)HttpContext.Current.Session["Project"];
SiteMaster masterPage = Master as SiteMaster;
masterPage.Project = Project;
}
}
And then i have an aspx page like:
public partial class SomePage: BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
//callling the base method to set the project
SetProject();
}
//some db method which requires the Project.Id property.
GetSomeInfo(Project.Id)
{
//irelevant code
}
}
Everything works fine, but, when i have 2 users online at the same time, they will obviously call the SetProject method, and if one of them uses GetSomeInfo method, the Project.Id property will be the latest one set, not the one from the current user session.
Can anyone help me fix this problem?
PS:
A dirty fix would be to read the session variable every time i have to use the Project, won't be a problem, but my page has like 10 methods requiring that property (a lot of redundant code)
A secondary fix would be to declare a public property on SomePage and use that, but then, i would find Project from the BasePage redundant and i don't like that approach because there are quite a few pages requiring the Project property and using the BasePage (filters, searches, etc on objects belonging to that Project)
EDIT After some more testing, 2 different users at the same time, and after Glubus comments, the problem happens only when the page is loading for one of the users (the user which is not loading anything will get wrong results from the database while the other user is loading a page.)
EDIT2 The workflow is as following:
User goes to home page where a list of projects are available (read from db) -> clicks on one project (when the user clicks the project i'm setting a session variable to be read later). Then the user can see/use other data related to this project.
EDIT3
When a user click on a project, they will navigate to a Dashboard page. Here, on the Page_Load even i'm setting the session variable, like:
public partial class Dashboard : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
int projectId;
int.TryParse(Request.QueryString["projectId"], out projectId);
if (projectId > 0)
{
Session["Project"] = ProjectSvc.GetProjectById(projectId);
SetProject();
}
}
}
ProjectDTO class:
public class ProjectDTO
{
public int idProject { get; set; }
public string Title { get; set; }
public string Users { get; set; }
public string Description { get; set; }
}
I'm setting the Project to the Site Master because i have a label which requires to be seen on the screen with the Project Name and description.
In order to access the Project from the current Session from all places, including WebMethods, declare a static readonly property in BasePage that directly accesses the Session.
public class BasePage : System.Web.UI.Page
{
public static ProjectDTO Project
{
get {return (ProjectDTO)HttpContext.Current.Session["Project"];}
}
// some other code
// Passing the Project to the Master.
public void SetProject()
{
SiteMaster masterPage = Master as SiteMaster;
masterPage.Project = Project;
}
}
You can get rid of the SetProject() call altogether if you also use BasePage.Project to access the current project from the Site Master.
During testing, make sure that you are not using the same browser instance for testing. When session managements happens via cookies and you have two users logged in into the same browser instance, they will actually use the same ASP.net session.
Perhaps a worst case scenario solution - but....
If you are really intent on making the Project variable accessible to all subsequent users, you maybe need to move away from sessions and just declare it as a static?
This way it will be shared application wide, retaining the last assigned value.

ASP.NET MVC navigation route that changes based on viewed data

I am in the process of creating tabbed navigation where the route location can vary. The parameter used to render the tabs should be based on the presently viewed data (which when it is a user, may not be the logged in user).
In the example image this is a user. Therefore, if I am looking at Andrew Steele then the links should be contextual to Andrew Steele (Andrew's summary, computers, accounts etc.). When I am looking at Bruce Hamilton the links should be contextual to Bruce Hamilton (Bruce's summary, computers, accounts etc.).
I've solved this by sticking the necessary parameter value in each ViewModel and then passing the value onto a partial to render the links; this feels kludgey. I'd prefer to not shove the linking parameter data into each ViewModel. It would seem reasonable to use Html.Action or Html.RenderAction combined with a ViewData Dictionary value, but I tend to shy away from using "magic strings" where possible.
Is there a better way to get the parameter value to the view that I am missing?
I wrote a site a while back where I had different tabs that went to different places for different users. I'll walk you through my solution hopefully I understood the question correctly and some of this helps.
As far as getting data to and from the View, I do use the ViewDataDictionary. To the best of my knowledge, that's what it's for when your model doesn't consist of a single simple object. In order to get around the "magic strings" of view keys, I create a bunch of extension methods on the ViewDataDictionary. This has the drawback that you end up with a slew of extra methods, but at least all of your string keys are isolated in a single location. You could even go the extra step of create constants in the class, but it seems redundant when only this class uses them. Extension properties would be better but...
/// <summary>
/// Gets the list of tabs to show.
/// </summary>
/// <param name="dictionary"></param>
/// <returns></returns>
public static IList<TabItemDisplay> TabListGet(this ViewDataDictionary dictionary)
{
IList<TabItemDisplay> result;
if (dictionary.ContainsKey("TabList"))
result = dictionary["TabList"] as IList<TabItemDisplay>;
else
result = null;
return result;
}
/// <summary>
/// Sets the list of tabs to show.
/// </summary>
/// <param name="dictionary"></param>
/// <param name="tabList"></param>
/// <returns></returns>
public static IList<TabItemDisplay> TabListSet(this ViewDataDictionary dictionary, IList<TabItemDisplay> tabList)
{
dictionary["TabList"] = tabList;
return tabList;
}
You'll notice that I have an explicit view object, TabItemDisplay, that I pass into the dictionary. This contains all of the values necessary to pass to Html.ActionLink.
public class TabItemDisplay
{
public string Name { get; set; }
public string Action { get; set; }
public string Controller { get; set; }
public object RouteValues { get; set; }
}
Since this view is not the main content of the page, I prefer to put the logic of creating the tab items, including destination parameters, into an ActionFilter. This allows me to reuse the tab creation logic across different actions and controllers. Any View that contains the tab partial control gets the CreatTabAttribute slapped across the corresponding Action or Controller and it's good to go.
This may be more than you needed, but I hope some of it helps.
EDIT: Just realized I didn't include what this looks like in the partial view. I actually have an HtmlHelper extension that renders a more intricate tab, but you get the idea.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<div id="tabs">
<%
if (null != ViewData.TabListGet()) {
foreach(var item in ViewData.TabListGet()) {
%>
<%= Html.ActionLink(item.Name, item.Action, item.Controller, item.RouteValues, null)%>
<%
}
}
%>
</div>
EDIT: Adding a short example of the ActionFilter I use.
public class CreateContentTabsAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResultBase;
if (null == result) return;
var routeValues = filterContext.RouteData.Values;
var repository = ObjectFactory.GetInstance<ITabRepository>();
var context = filterContext.HttpContext;
var userName = context.User.Identity.Name; // Or get id from Membership.
var tabs = repository.ReadByUserId(userName);
TabItemDisplay defaultTab = null;
var tabItems = new List<TabItemDisplay>();
foreach (var tab in tabs)
{
var tabItem = new TabItemDisplay
{
Name = tab.Name,
Action = "View",
Controller = "Tab",
RouteValues = new { key = tab.Key }
};
tabItems.Add(tabItem);
}
if (context.Request.IsAuthenticated)
{
tabItems.Add(new TabItemDisplay
{
Name = "Account",
Action = "ChangePassword",
Controller = "Account",
RouteValues = new { siteKey = site.Key }
});
}
result.ViewData.TabListSet(tabItems);
}
}
This is only a basic example of pulling tabs from a repository (instantiated using StructureMap), and a simple check to see if the user is authenticated. But you can do other things such as pull the requested user id for the user being displayed from routeValues.
You can create a viewmodel for this that exposes the tab preference. If each tab is totally different you could have a viewmodel base class that exposes the tab preference and each of the tabs could have their own view model.
Personally, I'd use either RenderAction from the MVC futures or a render partial call here depending on performance requirements and taste.
Advantage to RenderAction is you won't need to pass all the required rendering data to the delegate controller action as it could do the lookup itself. Whereas a partial view would require your main page have enough view data to render the tab. Either way its just two for loops (one for the tab list, one for the tab rendering) and a bit of extra data in your Model.
one solution is to create base controller and have all ur controllers inherit from it. in base controller u can add necessary value into viewmodel
public applicationController:Controller
{
ViewData["linkvals"] = someValues;
}
public HomeContorller:ApplicationController{}
second solution is to create a base view model and have all ur viewmodels inherit it. in constructor of base viewmodel u can create link values and assign them to ur properties
public baseViewModel
{
public LinkVal{get;set;}
public baseViewModel()
{
//calculate link vals
}
}
public ViewModelA:baseViewModel{}

Categories