mvc4 PartialView solution is needed - asp.net-mvc-4

im building a site using MVC4 and i want to display a navigation bar at the top of my _ViewStart according to my Database.
How can i do so? can i use a contoroller ActionResult that fired once the index page is loaded?
or how can i triger it by a partial view
my current ActionResult returning partial view is:
public ActionResult NavigationBar()
{
var entities = new CakesDBEntities();
var articles = entities.Articles;
List<NavBarModel> navBarList = articles.Select(nb => new NavBarModel { Title = nb.title, Url = nb.url }).ToList();
return View(navBarList);
}
my model:
namespace SimplyCakes20131009.Models
{
public class NavBarModel
{
public string Title { get; set; }
public string Url { get; set; }
}
}
my partial view:
#model IEnumerable<SimplyCakes20131009.Models.NavBarModel>
#foreach (var bar in Model)
{
<li>
#Html.ActionLink(bar.Title, bar.Url)
</li>
}
How can i integrate the nav bar to my _ViewStart?

A better option would be to use the _Layout.cshtml. _ViewStart is just calls the _Layout.cshtml.
You probably don't need partial View here. You can use a Child Action that renders PartialView results.
In your
_Layout.cshtml :
You can have
#{ Html.RenderAction("Navigation", "Home"); }
This points to the HomeController and Navigation Action
Additional Note: Html.RenderAction better because it is much faster than the Html.Action.
It can handle large amount of HTML efficiently as it will directly send the result to the Response. Html.Action just returns a strings with the result.
Navigation Action has its Navigation View which is pretty much equivalent to what you had in your view.
Home/Navigation.cshtml :
#model IEnumerable<MvcApplication1.Controllers.NavViewModel>
#foreach (var nav in Model)
{
<li>#Html.ActionLink(nav.Title, nav.Url)</li>
}
HomeController.cs :
Note that you probably inject the DB access as dependency to support the testability.
public class HomeController : Controller
{
private readonly ICakesRepository _cakesRepository;
//additional constructor to support testability.
public HomeController(ICakesRepository cakesRepository) {
_cakesRepository = cakesRepository;
}
//this can be removed if you the above with IOC/DI wire-up
public HomeController() {
_cakesRepository = new CakesRepository();
}
[ChildActionOnly]
[HttpGet]
public ActionResult Navigation() {
var articles = _cakesRepository.GetArticles();
var navBarList = articles.Select(nb => new NavViewModel { Title = nb.Title, Url = nb.Url });
return PartialView(navBarList);
}
}
Additional supporting classes :
public class NavViewModel {
public string Title { get; set; }
public string Url { get; set; }
}
public interface ICakesRepository {
IEnumerable<Articles> GetArticles();
}
public class CakesRepository : ICakesRepository {
public IEnumerable<Articles> GetArticles() {
//call to a db
//fake db data
return new List<Articles>() {
new Articles(){Title = "Title1", Url = "http://urlone.com"},
new Articles(){Title = "Title2", Url = "http://urltwo.com"},
new Articles(){Title = "Title3", Url = "http://urlthree.com"}
};
}
}
public class Articles {
public string Title { get; set; }
public string Url { get; set; }
}

Related

Blazor: How to pass multiple parameter's from NavigateTo to a WEB API controller to download a file

I'm trying to use NavivgateTo in Blazor to pass a file id and name to download a file from my Download controller.
What is the proper setup? I've tried a number of possibilities and I keep seeing an error: Sorry, there is nothing at this address.
Razor Page
public async Task SelectedDisplayDbItemChanged(DisplayDbItemsComboBoxItemDTO item)
{
Data = null;
Data = GetDataTable();
var fileId = await utilities.ExportDataTableToFile((DataTable)Data).ConfigureAwait(false);
//navigationManager.NavigateTo($"api/download/fileId/" + fileId + "/fileName/" + "myfile", true);
//?data1=678&data2=c-sharpcorner
navigationManager.NavigateTo($"api/Download/{fileId}/{"myfile"}", true);
}
Controller:
[HttpPost("Download/{fileId}/{fileName}")]
public async Task<IActionResult> Download(string fileId, string fileName)
{
using (var ms = new MemoryStream())
{
var fullPath = Path.Combine(DownloadPath, fileId);
await using (var stream = new FileStream(fullPath, FileMode.Open))
{
await stream.CopyToAsync(ms);
}
ms.Position = 0;
return File(ms, "application/octet-stream", $"{fileName}.xlsx");
}
}
I've seen a lot of examples from the Razor page to the Razor page, but not from NavigateTo to a controller with passing multiple parameters.
I've tried these responses as well: https://stackoverflow.com/a/71130256/9594249
https://stackoverflow.com/a/71130256/9594249
Not like Asp.net MVC or razor page, in Blazor parameters are passed by [Parameter] tag
#page "/Download/{fileId}/{fileName}"
#code {
[Parameter]
public string? fileId { get; set; }
[Parameter]
public string? fileName { get; set; }
}
please refer : https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-6.0
(Updated)
add to Program.cs or Startup.cs:
builder.Services.AddRazorPages(options => {
options.Conventions.AddPageRoute("/DownloadPage", "Download/{fileId?}/{fileName?}");
}
});
Pages/DownloadPage.cshtml
#page "{fileId?}/{fileName?}"
#model BlazorApp.Pages.DownloadModel
Pages/DownloadPage.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BlazorApp.Pages;
public class DownloadModel : PageModel
{
private readonly IWebHostEnvironment _env;
public DownloadModel(IWebHostEnvironment env)
{
_env = env;
}
public IActionResult OnGet()
{
// work with RouteData.Values["fileId"] and RouteData.Values["fileName"]
}
}
please refer :
https://learn.microsoft.com/en-us/answers/questions/243420/blazor-server-app-downlaod-files-from-server.html
https://learn.microsoft.com/ko-kr/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-6.0

razor pages core local methods

Is it OK to call a method in the #functions{} section in razor pages directly from HTML? This seems to work fine and it's much easier than calling an API, but I was wondering if there is a downside to this (security, performance, etc)?
For example, in the code...
#functions {
public class Tickets: PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
public void OnGet()
{
}
public string GetTickets(int Top) //--> THIS IS THE METHOD I AM CALLING
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}
}
And the HTML...
<div class="card alert-warning" v-for="ticket in tickets">
<div class="card-body">
<h4 class="card-title">{{ticket.TicketSubject}}</h4>
Card link
Another link
</div>
</div>
#section Scripts {
<script>
new Vue({
el: '#app',
data: {
dismissSecs: 5,
dismissCountDown: 5,
tickets: #Html.Raw(Model.GetTickets(100)), //-->THIS WORKS, BUT IT IS OK TO USE LIKE THIS?
xx: ''
}
})
</script>
}
It works because I believe the method is called when the page is being rendered and you have constant value being passed in. It would not automatically do something like update client-side without AJAX code being involved.
But to answer you question I think the 'more correct' approach is to set a binding property in the OnGet and reference that client-side
public class Tickets : PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
[BindProperty]
public string TicketData { get; set; }
public void OnGet()
{
TicketData = GetTickets(100);
}
public string GetTickets(int Top)
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}

How to invoke a View Component from controller

Is it possible to invoke a View Component from controller and render it to a string? I am really looking for some code example for this. Any help will be much appreciated.
As of beta7 it is now possible to return a ViewComponent directly from a controller. Check the MVC/Razor section of the announcement
The new ViewComponentResult in MVC makes it easy to return the result
of a ViewComponent from an action. This allows you to easily expose
the logic of a ViewComponent as a standalone endpoint.
So now the code for returning the sample view component just needs to be:
public class HomeController : Controller
{
public IActionResult Index()
{
return ViewComponent("My");
}
}
Please refer to example from official ASP.NET article on ViewComponent
In their example, the view component is called directly from the controller as follows:
public IActionResult IndexVC()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}
You can do that but you have to apply following thing as It is render by DefaultViewComponentHelper.
You have to create instance of this and to create that you need IViewComponentSelector and IViewComponentInvokerFactory.
To do this I have done following thing.
public class HomeController : Controller
{
Microsoft.AspNet.Mvc.DefaultViewComponentHelper helper = null;
Microsoft.AspNet.Mvc.Razor.RazorView razorView = null;
public HomeController(IViewComponentSelector selector,IViewComponentInvokerFactory factory,IRazorPageFactory razorPageFactory,IRazorPageActivator pageActivator,IViewStartProvider viewStartProvider)
{
helper = new DefaultViewComponentHelper(selector, factory);
razorView = new Microsoft.AspNet.Mvc.Razor.RazorView(razorPageFactory, pageActivator, viewStartProvider);
}
public IActionResult Index()
{
ViewContext context = new ViewContext(ActionContext, razorView, ViewData, null);
helper.Contextualize(context);
string st1 = helper.Invoke("My", null).ToString();
return View();
}
}
Here is my sample View Component.
public class MyViewComponent : ViewComponent
{
public MyViewComponent()
{
}
public IViewComponentResult Invoke()
{
return Content("This is test");
}
}
Here's a tag helper that I created to embed components via HTML like syntax. Invoking from a TagHelper like this should closely match invoking from a Controller.
ViewComponent Tag Helper
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace TagHelperSamples.Web
{
[HtmlTargetElement("component")]
public class ComponentTagHelper : TagHelper
{
private DefaultViewComponentHelper _componentHelper;
[HtmlAttributeName("name")]
public string Name { get; set; }
[HtmlAttributeName("params")]
public object Params { get; set; }
[ViewContextAttribute] // inform razor to inject
public ViewContext ViewContext { get; set; }
public ComponentTagHelper(IViewComponentHelper componentHelper)
{
_componentHelper = componentHelper as DefaultViewComponentHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
_componentHelper.Contextualize(ViewContext);
output.Content.AppendHtml(
await _componentHelper.InvokeAsync(Name, Params)
);
}
}
}
Usage
<component name="RecentComments" params="new { take: 5, random: true }"></component>
Code from dotnetstep's answer updated for MVC 6.0.0-beta4 (VS2015 RC):
public class HomeController : Controller
{
Microsoft.AspNet.Mvc.ViewComponents.DefaultViewComponentHelper helper = null;
public HomeController(IViewComponentDescriptorCollectionProvider descriptorProvider, IViewComponentSelector selector, IViewComponentInvokerFactory invokerFactory)
{
helper = new DefaultViewComponentHelper(descriptorProvider, selector, invokerFactory);
}
public IActionResult Index()
{
ViewContext context = new ViewContext(ActionContext, null, ViewData, null, null);
helper.Contextualize(context);
string st1 = helper.Invoke("My", null).ToString();
return View();
}
}
Based on https://gist.github.com/pauldotknopf/b424e9b8b03d31d67f3cce59f09ab17f
public class HomeController : Controller
{
public async Task<string> RenderViewComponent(string viewComponent, object args)
{
var sp = HttpContext.RequestServices;
var helper = new DefaultViewComponentHelper(
sp.GetRequiredService<IViewComponentDescriptorCollectionProvider>(),
HtmlEncoder.Default,
sp.GetRequiredService<IViewComponentSelector>(),
sp.GetRequiredService<IViewComponentInvokerFactory>(),
sp.GetRequiredService<IViewBufferScope>());
using (var writer = new StringWriter())
{
var context = new ViewContext(ControllerContext, NullView.Instance, ViewData, TempData, writer, new HtmlHelperOptions());
helper.Contextualize(context);
var result = await helper.InvokeAsync(viewComponent, args);
result.WriteTo(writer, HtmlEncoder.Default);
await writer.FlushAsync();
return writer.ToString();
}
}
}
and
public class NullView : IView
{
public static readonly NullView Instance = new();
public string Path => string.Empty;
public Task RenderAsync(ViewContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return Task.CompletedTask;
}
}

WebApiContrib Jsonp and Attribute Routing

Per the WebApiContrib.Formatting.Jsonp GitHub readme, it appears that in the RouteConfig.cs this should be entered:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional }
);
I currently don't have a RouteConfig.cs file in my AppStart. I created it using the Web API 2 template and I don't think I changed anything structurally. I do have a WebApiConfig.cs where I have set:
public static void Register (HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
how do I include it such that all routes have the ability to return Jsonp?
You could create a custom route attribute which implements IHttpRouteInfoProvider (which Web API route builder looks for when adding routes to route table) and then modify the template that is being generated by appending {format}
Example:
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[CustomRoute(Order = 1)]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}
[CustomRoute("{id}")]
public string GetSingle(int id)
{
return "value";
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class CustomRouteAttribute : Attribute, IHttpRouteInfoProvider
{
public CustomRouteAttribute()
{
Template = String.Empty;
}
public CustomRouteAttribute(string template)
{
if (template == null)
{
throw new ArgumentNullException("template");
}
if (template == string.Empty)
{
Template = template + "{format?}";
}
else
{
Template = template.TrimEnd('/') + "/{format?}";
}
}
public string Name { get; set; }
public int Order { get; set; }
public string Template { get; private set; }
}
I found this comment in a pull request but I don't understand if this is yet implemented into the production package nor if it got pulled at all.
If you are using Attribute Routing, you should add "/{format}" after each route if you plan to use the URI mapping for jsonp, e.g. [Route("api/value/{id:int}/{format?}")]. If you will require the Content-Type header to specify text/javascript, then you can leave your routes alone. (See the sample applications for examples.)

Displaying Error in a View

Is there any standard practice to display errors in a view? Currently it is being displayed from TempData.
I implemented a derived a class from Base Controller and used that derived class in every one of my controller. Then assign error or success messages from controller.
public class TestController : Controller
{
public string ErrorMessage
{
get { return (string) TempData[CommonHelper.ErrorMessageKey]; }
set
{
if (TempData.ContainsKey(CommonHelper.ErrorMessageKey))
{
TempData[CommonHelper.ErrorMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.ErrorMessageKey,value);
}
TempData.Remove(CommonHelper.SuccessMessageKey);
}
}
public string SuccessMessage
{
get { return (string)TempData[CommonHelper.SuccessMessageKey]; }
set
{
if(TempData.ContainsKey(CommonHelper.SuccessMessageKey))
{
TempData[CommonHelper.SuccessMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.SuccessMessageKey, value);
}
TempData.Remove(CommonHelper.ErrorMessageKey);
}
}
}
CommonHelper Class
public class CommonHelper
{
public const string SuccessMessageKey = "successMessage";
public const string ErrorMessageKey = "errorMessage";
public static string GetSuccessMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
public static string GetErrorMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
}
Then created a partial view having this
#using Web.Helpers
#if (!string.IsNullOrEmpty(CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])
</div>
}
else if (!string.IsNullOrEmpty(CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])
</div>
}
And in every view, the partial view is rendered.
<div>
#Html.Partial("_Message")
</div>
Here is a pretty common implementation of displaying errors.
Controller
public class UserController : Controller
{
[HttpPost]
public ActionResult Create(User model)
{
// Example of manual validation
if(model.Username == "Admin")
{
ModelState.AddModelError("AdminError", "Sorry, username can't be admin")
}
if(!ModelState.IsValid()
{
return View(model)
}
}
}
Model
public class User
{
[Required]
public string Username {get; set;}
public string Name {get; set; }
}
View
#Html.ValidationSummary(true)
#using(Html.BeginForm())
{
// Form Html here
}
You don't need all of the infrastructure you created. This is handled by the framework. If you need a way to add success messages you can checkout the Nuget Package MVC FLASH
I prefer to use ModelState.AddModelError()