asp.net core pass Model to Layout - html

I have a layout page with the structure where I use navbar alongside #RenderBody().
later I made a method to generate pages and consequently navbar itself. thus, the navbar is not fixed anymore.
Then I made a partial view to loop through navbar items and display them in the same place in the layout. but I don't know how to import a model in the layout.
I'm wondering is there any tag helper that can trigger an action? to return the needed model
my layout looks like this
<html lang="en">
<head>
...
</head>
<body>
<header>
<partial name="_NavBar" model="????" />
</header>
<main class="mb-3">
<div class="container-fluid">
#RenderBody()
</div>
</main>
<footer id="footer" class="border-1 border-top border-primary">
<div class="container-fluid py-4">
<div class="copyright">
© Copyright
</div>
</div>
</footer>
#RenderSection("Scripts", required: false)
</body>
</html>
Any ideas to display this nav partial? or maybe using another tag helper? or any possible solution?

I agree with #Roars, and pls allow me to show some sample code here.
First, let's create a service which could gather target data for showing nav, e.g. the menus contained in the nav bar are stored in database and we need to query out first. I use mock data instead:
using System.Collections.Generic;
using WebApplication1.Models;
namespace WebApplication1.Services
{
public class ChildService : IChildService
{
public List<Menu> GetNav() {
return new List<Menu> {
new Menu{ MenuId = 1, MenuName = "name1" },
new Menu{ MenuId = 2, MenuName = "name2" }
};
}
}
}
The interface:
using System.Collections.Generic;
using WebApplication1.Models;
namespace WebApplication1.Services
{
public interface IChildService
{
public List<Menu> GetNav();
}
}
Menu Entity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1.Models
{
public class Menu
{
public int MenuId { get; set; }
public string MenuName { get; set; }
}
}
Startup.cs -> ConfigureSerivces to config injection:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IChildService, ChildService>();
}
After setting the dependency in startup.cs, we can use DI in views. I created a Razor view and inject my query result.
#using WebApplication1.Services
#inject IChildService childService
#{
var menus = childService.GetNav();
}
<h2>hello world</h2>
<div>
#foreach (var item in menus)
{
<div>#item.MenuName</div>
}
</div>
At last, using partial tag to embed this view into other views.
#page
#model WebApplication1.Pages.Childs.IndexModel
#{
}
<h2>index for child</h2>
Edit
<partial name="~/Pages/Childs/test.cshtml" />
Here's testing result:

Thank you so much, guys, #Tiny Wang #Roar S., and #Cake or Death
I found good solution for that by adding something called ViewComponent. And I thought it's better to share it with you all.
It helps to include whatever logic you need in its class and it allows to include a Model in its view.
Here are 3 steps - but first here is the Model
public class Page
{
[Key]
public Guid Id { get; set; }
public int PageOrder { get; set; } = 0;
public string pageName { get; set; }
public string pageDescreption { get; set; }
public string pageHtmlContent { get; set; }
public string NavIcon { get; set; } // svg icon
public bool IsNavBarItem { get; set; }
}
(1) ViewComponent architecture:
(2) ViewComponent class:
public class NavBarViewComponent : ViewComponent
{
private readonly ApplicationDbContext context;
public NavBarViewComponent(ApplicationDbContext _context)
{
context = _context;
}
// https://www.youtube.com/watch?v=exokw7WQQ-A
public IViewComponentResult Invoke()
{
// this is how to avoid error of can't convert IQueryable to IEnumerable
IEnumerable<Page> pages = context.pages.Where(P => P.IsNavBarItem == true).AsEnumerable<Page>();
return View(pages);
}
}
(3) ViewComponent view:
#model IEnumerable<Page>
... whatever
<ul class="navbar-nav">
#foreach (var item in Model)
{
<li class="nav-item">
<a asp-action="PageView" asp-controller="Home" asp-route-PageID="#item.Id" class="nav-link">
#Html.Raw(#item.NavIcon)
#item.pageName
</a>
</li>
}
</ul>
... whatever
And finally to display this nav anywhere just need to include component in the layout
#await Component.InvokeAsync("NavBar")

Related

How do you link to other razor pages and include a query string parameter in Blazor WASM?

I've got a Blazor WASM app. It has two razor pages:
Documentation.razor:
#page "/documentation"
ViewRecord.razor:
#page "/documentation/ViewRecord"
I have a DocumentationController too.
I want to create a few hyperlinks within the Documentation razor page that have hyperlinks in this format:
/Documentation/ViewRecord?recordtype=randomWord1
/Documentation/ViewRecord?recordtype=randomWord2
/Documentation/ViewRecord?recordtype=randomWord3
Is there a cleaner way to do this, similar to using ActionLinks, instead of having to do something like this:
link1
link2
link3
This is the beauty of Blazor and Razor Components, if you find you want to something, you can create a re-usable component for it yourself. Complete flexibility. Say for instance we have the following component:
UrlBuilder.razor
#if (!string.IsNullOrEmpty(FullUrl))
{
#LinkDesc
}
#code
{
[Parameter]
public string LinkDesc { get; set; }
[Parameter]
public string Controller { get; set; }
[Parameter]
public string Action { get; set; }
[Parameter]
public string UrlParameter { get; set; }
private string FullUrl { get; set; }
protected override void OnInitialized()
{
FullUrl = $"{Controller}/{Action}?{UrlParameter}";
}
}
You can then access that component anywhere through your application like so:
<UrlBuilder LinkDesc="link 1" Controller="Documentation" Action="ViewRecord" UrlParameter="#word3" />
Is that any easier than creating a a href manually? Absolutely not, however, you could customize it to your delight.

Can't show the name from binded/relationship model in asp.core

following my previous question (asp.net core creating/updating a foreignkey), I want to display the name of the pharmacy from my Todo model. The following code are actually used:
public class Todo
{
public Pharmacy Pharmacy { get; set; }
}
public class Pharmacy
{
[Key]
public int PharmacyID { get; set; }
public string Name { get; set; }
}
the connector for my selectbox into my create/edit form:
public class PharmacynamePageModel : PageModel
{
[BindProperty]
public int SelectedPharmacy { get; set; }
public SelectList PharmacyNameSL { get; set; }
public void PopulatePharmacysDropDownList(ApplicationDbContext _context, object selectedPharmacy = null)
{
var query = (from p in _context.Pharmacy orderby p.Name select p).ToList();
PharmacyNameSL = new SelectList(query, "PharmacyID", "Name", selectedPharmacy);
}
}
and into my list (index.cshtml):
#foreach (var item in Model.Todo)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.OwnerID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Pharmacy.Name)
</td>
}
I tried without succes the following variant:
item.Pharmacy
item.Pharmacy.PharmacyID (but i want print the name)
I controlled with SQLServer Management Studio, and My column "PharmacyID" contains value after creating or editing data.
Hope you can help me ;)
Thanks per advance !
Entity Framework Core allows you to use the navigation properties in your model to load related entities. Check if you use the Include method to specify related data to be included in query results like the sample below
public async Task OnGetAsync()
{
Todo = await _context.Todo
.Include(t => t.Pharmacy).ToListAsync();
}

How do I populate a DropDown List using Razor Pages with No Controller

I am stuck on this one part. When I do a search all that comes up is examples with controllers, viewbags and other examples in MVC.
I am trying to populate a dropdown list from a database. Here is my code so far
Category.cs
public class Category
{
[Key]
public int CategoryID { get; set}
public string CategoryName { get; set; }
}
Editor.cshtml.cs
public class Editor : PageModel
{
private readonly DatabaseContext _context;
public Editor(DatabaseContext databasecontext)
{
_context = databasecontext;
}
public void OnGet()
{
List<Category> categoryList = new List<Category>();
categoryList = (from Category in _context.Category select Category).ToList();
categoryList.Insert(0, new Category { CategoryID = 0, CategoryName = "Select" });
}
}
What would be my next steps in attaching the dropdownlist to my Razor View Page?
You can use the select tag helper with razor pages as well.
Add 2 other public properties to your page model. One for the collection of items to be used to display the option items and the other for storing/passing the selected value.
public class Editor : PageModel
{
private readonly DatabaseContext _context;
public Editor(DatabaseContext databasecontext)
{
_context = databasecontext;
}
[BindProperty]
public int SelectedCategoryId { set; get; }
public List<SelectListItem> CategoryItems { set; get; }
public void OnGet()
{
CategoryItems = _context.Category
.Select(a=> new SelectListItem {
Value = a.CategoryId.ToString(),
Text = a.CategoryName })
.ToList();
}
}
Now in your view, use the SELECT tag helper.
#page
#model Editor
<form method="post">
<select asp-for="SelectedCategoryId" asp-items="#Model.CategoryItems">
<option>Select one</option>
</select>
<div>
<button type="submit" class="btn">SAve</button>
</div>
</form>
When user submits the form, You can read the selected option value in the SelectedCategoryId property of the page model.
public IActionResult OnPost()
{
var selectedCategoryId = this.SelectedCategoryId;
// to do : return something
}

Why is my code doing nothing?

When I run the debugger, I get 0 errors, the layout loads, and "Movies" shows up but literally nothing else. I'm fairly new to MVC and some input would be appreciated. The code for the View, Model, and Controller are below. The database contains over 100 items and a list a links is intended to appear but its just white space instead.
View:
#model IEnumerable<MyWebPage.Models.Movie>
<h1>Movies</h1>
<ul>
#foreach (var item in Model.OrderBy(x => x.Name))
{
<li>#Html.ActionLink(item.Name, "Movies", new { id = item.ID })</li>
}
</ul>
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyWebPage.Models;
namespace MyWebPage.Controllers
{
public class HomeController : Controller
{
private MoviesDbContext db = new MoviesDbContext();
public ActionResult Index()
{
return View(db.Movies.ToList());
}
public ViewResult Movies(int id)
{
Movie moviesdb = db.Movie.Find(id);
return View(moviesdb);
}
}
}
Model:
using System;
using System.Data.Entity;
namespace MyWebPage.Models
{
public class Movie
{
public int ID { get; set; }
public string Name { get; set; }
public string Cover { get; set; }
}
public class MoviesDbContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
}
Thanks in advance!

How to bind DbContext from Model to View

I'm new to MVC and have this simple Problem (I think).
I have a DbContext
public class UsersContext : DbContext
{
public UsersContext() : base("Share_DB") {}
public DbSet<ItemDB> Items { get; set; }
}
which contains a list of items with id and Title:
[Table("Items")]
public class ItemDB
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
}
and I want to bind a list with all the itemes in the Database table to the model. However I can't figure out how to do that.
In the View (cshtml file) I guess I have define which model I have to use ( #model XXX.Models.ItemModel ) then Display it with something like:
<ul>
#foreach (XXX.Models.ItemModel.ItemDB item in #Items)
{
<li>#item.Title</li>
}
</ul>
However it can't find the #Items property is not found. How do I write this?
The way MVC works is when you return a view in your controller, whatever gets passed to View() ends up as the Model property. In other words:
public ActionResult Index()
{
return View("Hello World");
}
Will pass the string "Hello World" to your View. Then at the top of your Index.cshtml file, you need to declare the model type:
#model string
And usage becomes:
<div>#Model</div>
In your case, you just need to pass your list of Items to the view, and tell it to expect the right type of model:
public ActionResult Index()
{
using (var db = new UsersContext())
{
var items = db.Items.ToList();
return View(items);
}
}
and then in your view:
#model IEnumerable<ItemDB>
and you can use it as:
#foreach (var item in Model)
{
<div>#item.Title</item>
}