Adding feedback feature to all pages - asp.net-mvc-3

I have got asp.net mvc 3 website, I want to add feedback feature to all pages.
I created the partial view for this purpose and render it in master layout.
#model FeedbackHelper
Name:<br />
#Html.TextBoxFor(o=>Model.Name)
for example in Questions page , MVC returns the exception because that page binded the POST entity, as far as I check in StackOverflow I have got 2 solution
create a parent model and add POST and FeedbackHelper as properties
use Tuple
at the moment, changing all models is too risky for me.
Is there any good solution ?!

You could use child actions. The idea is to define a specific controller action that will serve the partial view and then include it using the #Html.Action helper in your Layout.
So:
public ActionResult Feedback()
{
FeedbackHelper model = ...
return PartialView(model);
}
then you will of course have a partial in the Shared folder:
#model FeedbackHelper
Name:<br />
#Html.TextBoxFor(o => o.Name)
and include it in your Layout:
#Html.Action("Feedback", "ControllerContainingTheFeedbackAction")

Related

ASP.NET Core populate model only once for all views

I'm new with ASP.Net Core (3.0 in this case) and I´m trying to create a menu that is visible on all views of a WebApplication, is created dynamically and must be populated only once. Below i explain the steps and try outs i did to reach the goal needed (if required i can share the code I'm using).
This is what i did:
In a simple way, using the "_Layout.cshtml" page, i created a static HTML menu and made all other views simply inherit that layout. So far, so good;
Next challenge comes from the fact that the menu items are dynamically created after a User has logged-in, which i managed to overcome by setting a ModelView inside a controller (HomeController.cs with Index action in this case), and then delivering it to the view. For this case works OK, because the default page is ~\Home\Index\, problem is when i change to a different view with a different controller, the menu has to be rendered again, and so i have to replicate the code (a problem dealt create a BaseController and BaseModel based on this post along side the OnActionExecuted to host the menu generating code)
Now, the biggest problem is the fact that i can only populate the menu once, after the user logs-in. Each time there is a redirect between different controllers/views (post-back of same controller/view works fine), the model is null inside the OnActionExecuted, I tried using ViewData, ViewBag, TemData, but all are null.
So, my question is, how to keep that specific data alive and shared, basically across all the views, and only gets populated once (after each user login) between redirects from different views?
I have been reading around and found several solutions besides the one i did, but i did not found any that could keep data alive throughout the user session the way I need:
ViewBag, ViewData and TempData
Can the shared layout view have a controller in ASP.NET MVC?
Pass data to layout that are common to all pages
To sum up, my flow at this moment, is like this:
User Logged-in
Redirect to default: ~\Home\Index
MenuModelView.cs for the menu gets built and HomeController.cs returns to Index.cshtml with the model attached to it.
Index.cshtml receives the populated ModelView and it uses _Layout.cshtml
The _Layout.cshtml builds the HTML tags for the menu based on the MenuModelView.cs data
User navigates to a different view and steps 3 to 5 are repeated from a specific controller/view
If you want to create a control that can be accessible in all pages without changing every controller, I strongly suggest creating a view component. For a view component has no relationship with your controller, but can access dependencies like database and full HTTP context.
For example, you want to build a custom nav menu, you can just create a view component named NavHeader
using Microsoft.AspNetCore.Mvc;
namespace YourProject.Views.Shared.Components.NavHeader
{
public class NavHeader : ViewComponent
{
public NavHeader(YourDbContext context)
{
// you can access your dependencies, like database.
}
public IViewComponentResult Invoke()
{
// your own logic. You can access HTTPContext here.
var model = new YourOwnModel();
return View(model);
}
}
}
And just call it in any view or layout.
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
#*Render your component like this*#
<vc:nav-header></vc:nav-header>
</body>
For more details about view component, please reference:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1
https://anduin.aiursoft.com/post/2020/1/4/share-view-component-between-different-aspnet-core-web-project

ASP.NET MVC 3 - pass data to a partial view in Layout

I'm working on a ASP.NET MVC 3 application, but I'm rather new to MVC in general.
I have a partial view in a my application layout view that needs to have data passed to it. this will appear on every page. Is there a way to make this happen so I don't have to load that data into the view model for every action in the entire site?
As in, if a user navigates to Mysite/admin/settings, I would like to have the partial view on the layout be able to somehow receive the data that it needs without me needing to put that code in the Settings action in the Admin controller.
On this same note, how do you pass data to the layout view of an application anyway?
In these situations I usually use a base ViewModel for my Views
public class ApplicationViewModel
{
public string UserName {get; set;}
....
}
public class SettingsViewModel : ApplicationViewModel
{
}
all your views would inherit from that ViewModel. Your layout would expect it as well
_layout.cshtml:
#model ApplicationViewModel
....
<h1>hello #Model.UserName</h1>
hopefully this answers your question
Partial only renders a view. You need to provide the model manually.
You can create an action for the view you want and render it with Html.Action( actionName ).
Make an action for example menu which will create a model that will be provided to the menu view.
Now you can call the #Html.Action("menu") from wherever, and it will be rendered autonomously. (you can ofcourse provide a controller name as well, and even custom routeData)
You might also want to set Layout = null; in the view to avoid using the master layout of the whole site.
This is how I pass a value to the partial view from my layout page:
Layout page code:
Html.RenderPartial("_SubMenuLeft", new ViewDataDictionary { {"category", "MMG"} });
and in my _SubMenuLeft.cshtml (partial view)
#if (ViewData["category"] == "MMG")
{
...
}
Hope it helps someone for future reference.

mvc3 - using partial views in a different area

I have two questions regarding partial views...
When to use Partial views vs #helper methods, i have used both
interchangeably and would like to get more consistent in their
usage. What do you guys do?
How do you reference a partial view from another area.
I have an area called admin and i have a partial view in the regular Views directory. How do i use it .. i have tried the following which dont work as it cant be found.
#Html.Partial(VirtualPathUtility.ToAbsolute("~/Views/ControllerName/_PartialView"),
Model)
other i have tried -
#Html.Partial("~/Views/ControllerName/_PartialView", Model)
I'm not sure if you mean Html helpers, or razor helpers when you say "helpers" In any case, I only create Html helpers when it's a small, idividual item like a control.
If you mean Razor helpers, then they are different from Partials in that you can call them like functions, passing whatever parameters you want. Partials are largely stuck with the "model" system (and of course Temp/ViewData/Bag.
It's all about how you want to work with the code.
As for your Partial. You have to include the suffix.
#Html.Partial("~/Views/ControllerName/_PartialView.cshtml", Model)
Since the questioner asked about areas here's how to do it in an area
#Html.Partial("~/Areas/Store/Views/Pages/Checkout.cshtml")
I am just giving specific and simple example of what I'm trying to do.
I need to be able to logoff from an area page using the partialview located in the main shared folder. Here's what I did:
At the area view I reference the partial view by
<div class="float-right">
<section id="login">
**#Html.Partial("~/Views/Shared/_LoginPartial.cshtml")**
</section>
</div>
At the Main shared folder where the _LoginPartial code was located I added {new = area ("")}, from:
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
to:
using (Html.BeginForm("LogOff", "Account", **new { area = "" },** FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
Hope that helps in some way!
Another option is to make the partial view you want to share between areas SHARED.
So you put it in the main ~/Views/Shared/ folder, e.g.
~/Views/Shared/_MyPartialView.cshtml.
You can then refer to it from any area by saying
#Html.Partial("_MyPartialView")
Make sure your Controllers in Areas have the [Area("MyArea")] annotation. As of this post, pulling in Partial Views from across Area boundries via Ajax div updates in ASP.NET Core works for me with Tag Helpers and #Html.ActionLink.

ASP.NET MVC 3 Partial View dynamically rendered and linked from dynamic list in view

In my MVC 3 application, I will have a view that will contain a partial view. The view itself will have a list of dynamically generated links. The link has to cause the partial view to render detailed information for that linked item.
Would I use Ajax for this? If so, since I haven't worked with Ajax before, is there any documentation for using it in a MVC 3 app?
Also when the view is first loaded, the partial view will either not be loaded or ideally show another separate partial view. Any thoughts on a good way of doing this?
Thanks for the help.
Create an action method which returns a PartialViewResult:
[HttpGet]
public ActionResult DetailedLinkInfo(int someIdentifier)
{
var detailedLinkInfo = GetFromSomewhere();
return PartialView(detailedLinkInfo );
}
Then create a partial view, strongly-typed to the type of detailedLinkInfo (let's say it's an DynamicLink.
#model WebApplication.Models.DynamicLink
#* bunch of HTML for the detailed info *#
Then use jQuery on the client-side. Give all your links a class so it makes it easier to hook up the event:
$(function() {
$('a.dynamic-link').click(function() {
$.get('/SomeController/DetailedLinkInfo', someIdentifier: $(this).attr('id'), function(data) {
$('#some-div').html(data);
});
});
});
End result: you click one of the links, the jQuery will perform an AJAX GET to your controller action, then bind the result to the div.
The easiest way of solving this problem that I found was using Ajax helpers that come with the MVC 3 framework. The Ajax video for MVC 3 on Pluralsight did a phenomenal job at succinctly explaining the basics of how to use this feature.

How does ASP.NET MVC arbitrate between two identically named views (aspx and razor)?

Using ASP.NET MVC3 I created a new Razor view and gave it the same name as the existing .aspx view that I had been using. I noticed that controller continued to pick up the .aspx view (which has the same name as the action) which is pretty much what I expected. I then renamed the .aspx view and action picked up the razor .cshtml view.
So if I have two views called myview.aspx and myview.cshtml and an Action called MyView() that does a return View(), it will pick up the myview.aspx view and return that.
How does MVC3 decided which view-type to default to?
Is there a way to change this default behavior to prefer a razor view over an .aspx view?
Everything stems down to the order of view engines in the ViewEngines.Engines collection. Here's how the ViewEngines static constructor looks like (as seen with Reflector in ASP.NET MVC 3 RTM):
static ViewEngines()
{
ViewEngineCollection engines = new ViewEngineCollection();
engines.Add(new WebFormViewEngine());
engines.Add(new RazorViewEngine());
_engines = engines;
}
which explains why WebForms is the preferred view engine.
So you could perform the following grotesque hack in Application_Start to inverse the preference towards Razor :-)
var aspxVe = ViewEngines.Engines[0];
var razorVe = ViewEngines.Engines[1];
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(razorVe);
ViewEngines.Engines.Add(aspxVe);
I would imagine its down to the order in which view engines are registered. Earlier registered view engines will be queried first. If you want to change the order:
ViewEngines.Engines.Insert(0, ...);

Resources