Persisting data in a Razor Partial View - razor

I am using ASP.NET Core 2.0 and Visual Studio 2017.
I want to create a Razor partial view to display the menu of our application. The menu is created dynamically as each user will only have menu items for which they have the necessary permissions (so the menu will be different for each user).
The problem I'm having is persisting the menu in the Razor partial view. There is no PageModel code-behind in a Razor partial view, so the problem I'm having is that the menu disappears when you click onto another page. I've persisted the menu in session storage, but I can't figure out how to load the menu from session storage into the Razor partial view.
I've tried loading the session storage menu into ViewData but this is wiped out when you click on another page.
How do I persist data in my Razor partial views?

Instead of using partial view, consider using a view component.
public class MenuComponent : ViewComponent
{
SomeDependency userDetails;
public MenuComponent(SomeDependency userDetails)
{
//store this userDetails data to extract profile info in Invoke method
this.userDetails = userDetails;
}
public IViewComponentResult Invoke()
{
//check for logged in user profile here and return appropriate view
return View(viewName,ModelObjectForView);
}
}
1.Create required menu views
Create the appropriate views for displaying menu for each role or use
conditional rendering.
Please note view for view components will be looked in to
/Views/ControllerName/Components/ViewComponentName/ViewName.cshtml this
location instead of /Views/ControllerName/ViewName.cshtml.
2.Define Layout Page
Now, since you need to display menu for all the pages user navigates to, you
need to extract this view component in a layout page. Create a _Layout.cshthml
file and make sure views that need to show menu item uses this layout page.
In the _Layout.cshtml , you can now render the view component defined above by calling
#await Component.InvokeAsync(nameof(MenuComponent))
3.Using ViewComponent instead of PartialView
I am suggesting use of ViewComponent over PartialView in this case because
ViewComponent will allow you to have its own model (RoleDetails for user in
your case) in comparison to PartialView
where you will have to pass some child data from view model and hence will have
to keep that data in each of your view model. Otherwise , you can even use PartialView. The important thing here is layout page that will help you keep menu across all views presented to user.

Related

How to implement back button in angular 9

I'm implementing a small project with only two UIs.
1-The first UI is called Home which contains paginated users list the home component.
2-The second UI is called user details which contains user profile the user-details component
3- and of course the app component which contains the tool bar and router-outlet
let us suppose the following scenario
I'm in the home UI and in the second page of the paginated users list and I clicked on one of the users item then the user-details UI appeard and I'm now in the second UI and I now I want to navigate back to the home but when doing so the home UI is recreated and become the first page of the users list instead of the second page where we left it because this is my code of the back button
<img
routerLink = "/home" style="cursor: pointer;"
width="40"
alt="Angular Logo"
/>
<span routerLink = "/home" style="cursor: pointer;">Users</span>
my question is:
is there is any way to implement it in such a way it doesn't recreate the home page i.e. implement the correct behavior of back button?
There's a couple ways that you can approach this.
If you're using a pagination library (such as ngx-pagination), then hopefully the pagination component takes an input for the current page. You'd then need to keep track of that page either through a service or in persistent storage.
Whenever you route back to the Home UI, get the page that you were on and load that back into the pagination component.
Another way can be to use router parameters. By modifying your urls a bit, they can take on the following form:
/home/1
/home/2
/home/3
Clicking on a user in page 2 can give the url: /home/2/user-id (just an example)
The path for your home component can look like:
const routes: Routes = [
{ path: "home/:pageNumber", component: HomeComponent }
];
Upon loading the home component, you can get the get the router parameter as follows (I'm using the Router method but you can parse the URL manually as well):
export class HomeComponent implements OnInit {
constructor(private router: Router) {
const pageNumber = this.router.navigationExtras.params['pageNumber'];
}
}
You can then load this page number into your pagination component. Using this method doesn't require you to keep track of the previous pages.
There are probably better methods to do this, but this is just some suggestions :)

How to navigate from a presentational component using react-router?

I'm working on a personal project, it's a simple blog app using ReactJS. I have 1 screen for the entry list page, and 1 for the entry details page.
Now, I'm trying to follow the best practices, so I laid out my components as this:
Entry List Container: container component. Handles fetching entries retrieval and updating application state by dispatching redux actions.
Entry List: presentational component. Receives an Array of Entry objects and shows a list of them
Entry Details Container: container component. Handles more application state update logic, fetching, etc. Receives an Entry object
Entry Details: presentational component. Displays the entry.
Basically both containers renders their presentational counterpart.
Now, the problem is the navigation. In my App component I'm rendering a couple of Route components, one is rendering the EntryListContainer, and another one should render the EntryDetailContainer.
Problem is, how do I navigate from EntryList ?
My hierarchy is something like this:
EntryListContainer
EntryList
EntryRow -----> This contains a button to navigate.
EntryDetailsContainer
EntryPage
I suppose I could render a Link inside EntryRow, something like
<Link to={`${match.url}/{entry.id}`} />
But then I'd have to pass down the match object down from EntryListContainer to EntryList to `EntryRow, and it feels smelly.
How do I navigate properly?

MVC controller to layout?

I have a website which allows users to send messages to eachother. My layout on the top has a notification bar, which shows the number of unread messages. My table with messages has a property of Unread (true, false). I can easily search for unread messages for the current user, I just don't know how to send it to layout. Is there a controller I can use to send to a layout?
You can define that your layout pages use a model. I would recommend using an interface as model.
public interface IMessageNotification {
public int UnreadMessagesCount {get; set;}
}
All ViewModels that use the layout page have to implement this interface.
You can access it in the Layout.cshtml like this:
#model IMessageNotification
<div class="myNotification">#Model.UnreadMessagesCount</div>
See ASP.NET MVC Razor pass model to layout
Another way would be to define a section in the layout that acts as placeholder for the notification, and every view can render the notification how it wants.
Layout:
#* No model directive required *#
#if (IsSectionDefined("Notification")) {
#RenderSection("Notification")
}
Concrete View (SomeConcreteViewModel has property UnreadMessagesCount):
#model SomeConcreteViewModel
#section Notification {
<div class="myNotification">#Model.UnreadMessagesCount</div>
}
As for the controller: you can use a helper class that fills the required data into the IMessageNotification interface implemented by the viewmodels. Call this in every action that renders a view using this layout.
I created a partial view, and used a controller to call it. I queried the DB for unread messages, and sent the count via ViewBag. I call it with #Html.Action. Didn't know this gets rendered each time user refreshes a web page. Thank you all :)

Tweaking out of the box SharePoint 2013 forms

I have the following situation. We are building a new intranet for our company and, being a charity, accomplishing AA accessibility is a must. I am a SharePoint developer and we are getting the design from a third party company. That said, designers made a custom design in a way that it is barely recalling SharePoint at all, like a kind of mixture between a completely different design like if it was a publishing site and at the same time, the idea is to work with as much out of the box functionality as possible.
That means that if I create a list, all the look and feel will be the customized one while the functionality must be kept. So I would get a settings page for the list whose functionality should be kept as is but the design should be the new design. A custom list (custom but created in SharePoint) would have a new, editing and display form which should be out of the box... but with the new design.
The problem comes with this out of the box forms. The design is using divs for displaying all the HTML controls that are linked to each of the columns of a custom list. Unfortunately, SharePoint 2013 out of the box list forms tabulate the fields using HTML tables.
Apparently there is a limit of 30000 characters in this post, so I can't paste the code from the out of the box form to show the tables, but what I did is to create a form with an example of each of the possible fields in SharePoint and then look at how many tables I was getting there. And I can't add a screenshot because I need 10 reputation...
I have removed in previous projects the tables in SharePoint webparts using a control adapter, which, by the way, I am using in this website too. But I am struggling with how I can change this HTML tables in the forms. I have tried to render the form same way that I did with the webpart zones but it is not working in a similar way.
Here you have the code for the Control Adapter for webpart zones:
public class WebpartControlAdapter : System.Web.UI.Adapters.ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
var wpz = Control as Microsoft.SharePoint.WebPartPages.WebPartZone;
if (wpz != null)
{
WebPartCollection wpColl = new WebPartCollection(wpz.WebParts);
foreach (System.Web.UI.WebControls.WebParts.WebPart wp in wpColl)
{
wp.RenderControl(writer);
}
}
}
}
And here what I tried based on that, which is basically destroying my page completely:
public class PageControlAdapter : System.Web.UI.Adapters.PageAdapter
{
protected override void Render(HtmlTextWriter writer)
{
var currentPage = Control as Page;
if (currentPage != null)
{
System.Web.UI.HtmlControls.HtmlForm form = new System.Web.UI.HtmlControls.HtmlForm();
form = currentPage.Form;
form.RenderControl(writer);
}
}
}
So my question are if anyone has a solution or way to achieve this.
Thanks for any possible help.
P.D.: before you propose that, changing the .aspx files coming OOTB from SharePoint is not an option because we hold other web applications in the same farm and making changes there would provoke changes in all other websites.
I'am not sure what you want to acomplish.
If you are creating totally custom list and want a New, Display and Edit forms completly different then you can define which forms to use in contentType:
https://msdn.microsoft.com/en-us/library/office/aa543825.aspx
Example:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Parent ContentType: Workflow Task (0x010801) -->
<ContentType ID="0x010100AF4E4BE6CF8048959C4172F4298CE42A"
Name="MyName"
Group="MyGroup"
Description="MyDescription"
Version="0">
<FieldRefs>
</FieldRefs>
<XmlDocuments>
<XmlDocument NamespaceURI ="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
<FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
<New>_layouts/MyFolder/New.aspx</New>
<Display>_layouts/MyFolder/New.aspx</Display>
<Edit>_layouts/MyFolder/Edit.aspx</Edit>
</FormUrls>
</XmlDocument>
</XmlDocuments>
</ContentType>
</Elements>
In code behind you get current item:
SPListItem item = SPContext.Current.ListItem;
Other possibility is to add script editor webpart on NewFrom, DisplayForm and EditForm. In script use jquery to change look of fields.
To auto populate scriptEditorWebparts you can make eventhandler fired on ListAdded event

Kendo MVC non-unique id issues

Example: We have an employee list page, that consists of filter criteria form and employee list grid. One of the criteria you can filter by is manager. If the user wants to pick a manager to filter by, he uses the lookup control and popup window is opened, that also has filter criteria and employee list grid.
Now the problem is, that if the popup window is not an iframe, some of the popup elements will have same names and ids as the owner page. Duplicate ids cause Kendo UI to break as by default MVC wrapper generates script tags with $("#id").kendoThingie.
I have used iframe in the past, but content that does not fit in iframe window like long dropdown lists gets cut off and now IE11 especially causes various issues like https://connect.microsoft.com/IE/feedback/details/802251/script70-permission-denied-error-when-trying-to-access-old-document-from-reloaded-iframe.
What would be the best solution here? Generate unique ids for all elements on Razor pages? Modify partial page content that is retrieved by Ajax making ids unique? Something else?
It sounds like you are using a partial page as the content to a Kendo window. If this is the case then just provide your partial with a prefix like so at the top of the page.
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "MyPrefix"
}
Now when you create a kendo control via the MVC wrapper like so
#(Html.Kendo().DropDownListFor(o => o.SomeProperty)
.....
)
The name attribute will be generated as "MyPrefix.SomeProperty" and the id attribute will be generated as "MyPrefix_SomeProperty". When accessing it within Jquery I like a shorter variable name so I usually do
string Prefix = ViewData.TemplateInfo.HtmlFieldPrefix
After setting the prefix. Then use
var val = $('##(Prefix)_SomeProperty').data('kendoDropDownList').value();
Note after this change. If you are posting a form from that partial you will need to add the following attribute to your model parameter on the controller method like so. So that binding happens correctly.
[HttpPost]
public ActionResult MyPartialModal([Bind(Prefix = "MyPrefix")] ModeViewModel model) {
.....
}
Now with all of that said. As long as you keep your prefixes different for each partial your control ids and names will be unique. To ensure this I usually make my prefix name be the same as my cshtml page that I am creating. You would just need to worry about JS function names. Also, note when closing a kendo window all DOM still exist. You just hide it. If this causes you the same issue you just need to be sure to clear the DOM of the modal on close. Similar to how BurnsBA mentioned. Note because of this is the reason why I try to make sure I use the least amount of kendo windows as possible and just reuse them via the refresh function pointing to a different URL.
$('#my-window').data('kendoWindow').refresh({
url: someUrlString
, data: {
someId: '#Model.MyId'
}
}).open().center();
Then on the modal page itself. When posting I do the following assuming nothing complicated needs to happen when posting.
var form = $('#my-form'); //Probably want this to be unique. What I do is provide a GUID on the view model
$('#my-window').data('kendoWindow').refresh({
url: form.attr('action')
, data: form.serialize()
, type: 'POST'
}).open().center();
We do something similar, and have the same problem. We have create/edit/delete popups that fetch data via ajax. Different viewmodels might reference the same model on the same page, and if you open multiple popups (create item type 1, create item type 2) then the second and subsequent popups can be broken (kendo ui error such that a dropdown is now just a plain textbox). Our solution is to delete all dom entries when the popup is closed so there are no conflicts between ids in different popups. We use bootstrap, so it looks like
<script type="text/javascript">
$('body').on(
// hook close even on bootstrap popup
'hidden.bs.modal', '.modal',
function () {
$(this).removeData('bs.modal');
$(this).find('.modal-content').html(''); // clear dom in popup
});
</script>
Note that our popup has some outer html elements and identifiers, but the content is all in
<div class="modal-content"> ... </div>