I would like to load a list of students at moment I have a signalr connection and the request gets all students. I'd like to scroll down the list and it would load a 100 students at a time?
<ul>
#if (Students != null)
{
#foreach (var student in Students)
{
<li>
<div class="treeview__item__header">
#plan.Name
</div>
</li>
}
}
</ul>
#code
{
private List<StudentsData> Students { get; set; }
protected override async Task OnInitializedAsync()
{
Students = await StudentsConnection.GetStudents();
}
}
I have used this example with success:
https://github.com/amuste/DnetVirtualScrolling
in alternative, as Alexander stated, a simple pager calculated on the number of rows to show and seems to be the best "pure blazor" solution
Edit: Looks like Virtualize could be perfect for this use:
https://www.syncfusion.com/blogs/post/asp-net-core-blazor-component-virtualization-in-net-5.aspx
I believe that you should consider solution on store procedure level with
Select top 100 where and have a parameter #next
With witch you could control from app with button or event when you scroll down to the end of list
you can use pagination and implement a pager component. Look at these two articles, they may help you: Pager component and part of Pager component 2
Related
My project is VS-2022, .Net 6, C#, Blazor WASM hosted. For CRUD, we designed a razor page showing a list of "records" but needed a way to "page" for a list of 12 records on each page.
This post is NOT a question, but may be used by others seeking simple paging that works in similar projects. I hope this post helps others.
From this list screen, we could ADD or EDIT (delete in the future requirement). I had researched the web for how to do paging but the solutions were older and some used TagHelper which is no longer available in Blazor (or my knowledge is too weak to morph it).
So from these un-usable (in my case) solutions, I created a simple set of code to be placed on each LIST-for-CRUD-page. This process relies on threeList<> elements to hold the vehicle-data fetched from the DB once in OnParametersSetAsync() method. The paging-list is a subset of the main list of All-Vehicle-data (from the DB) and Filtered-Vehicle-data. The paging is extremely fast since paging takes place in the client-project after a single fetch of DB-data.
I show you the list page with the paging shown at the bottom of the list.
Below the image is the PagingInfo.cs class that holds the relevant paging variables. The "number of records per page" is a constructor value for this class so each CRUD-model-page can have a different number of records per page.
Followed by the pertinent HTML code that renders the paging buttons (only when there is more than a single page of 12 records).
Followed by the C#-code that drives the paging. What is NOT shown is typical Blazor code to fetch DB-data for the Vehicles and managing the Active/In-Active select control's events.
For each CRUD-model, in this case Vehicle CRUD, the C# code has a List<Vehicle> _AllVehicles variable that fetches DB-data-records for all vehicles.
A List<Vehicle> _FilteredVehicles variable that is filtered by CUSTOMER and by Active/In-Active vehicles (see the Active/In-Active select-control in the page-image).
AND a List<Vehicles> _PagedVehicles variable that contains the records to be displayed in the HTML-table (no code provided -- but see the page-image below) that is "computed/filtered" in the PageClicked() -method using Skip and Take filters for the _FilteredVehicles list.
Here is the paging-info object that is used by the page's code with information needed for the paging operations.
public class PagingInfo {
public PagingInfo(int pItemsPerPage = 8) {
ItemsPerPage = pItemsPerPage;
}
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages {
get {
return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
}
}
}
Here is the HTML paging code below the table-body the list-"table" ( not shown here).
</tbody>
#if (_PagingInfo.TotalItems > _PagingInfo.ItemsPerPage) {
<tr class="bg-warning m-0 py-0">
<td colspan="3">
<div style="height: 3.1rem; text-align: center;" class="justify-content-center">
<div class="btn-group justify-content-center" role="group" aria-label="Basic outlined example">
<button id="btnPrev" type="button" class="btn btn-outline-dark border-3 fw-bold" #onclick="PagingClickPrevious">< Previous</button>
<span id="btnPage" type="button" class="btn btn-outline-dark disabled fw-bold">Page #_PagingInfo.CurrentPage of #_PagingInfo.TotalPages</span>
<button id="btnNext" type="button" class="btn btn-outline-dark border-3 fw-bold" #onclick="PagingClickNext">Next ></button>
</div>
</div>
</td>
</tr>
}
</table>
The paging-code follows:
// Paging Functions /////////////////////////////////////////////////////////////////////
PagingInfo _PagingInfo = new PagingInfo(12); // Initialize here: number of items-per-page.
private IEnumerable<Vehicle> _PagedVehicles { get; set; }
private void initializePagingInfo(int pPageNumber) { ////////////////////////////////////////////////////////////
_PagingInfo.CurrentPage = pPageNumber;
if (_FilteredVehicles is not null && _FilteredVehicles.Count() > 0)
_PagingInfo.TotalItems = _FilteredVehicles.Count();
}
private void PagingClicked(int pItemNumber = -1) { ////////////////////////////////////////////////////////////
_PagingInfo.TotalItems = _FilteredVehicles.Count(); // Get the count of items.
switch (pItemNumber) {
case < 1: // Goto previous page.
_PagingInfo.CurrentPage = _PagingInfo.CurrentPage < 2 ? 1 : --_PagingInfo.CurrentPage;
break;
case > 99: // Goto next page.
if (_PagingInfo.CurrentPage < _PagingInfo.TotalPages)
_PagingInfo.CurrentPage++;
break;
default:
_PagingInfo.CurrentPage = pItemNumber;
break;
}
_PagedVehicles = _FilteredVehicles.Skip((_PagingInfo.CurrentPage - 1) * _PagingInfo.ItemsPerPage).Take(_PagingInfo.ItemsPerPage).ToList();
}
private void PagingClickPrevious() { ////////////////////////////////////////////////////////////
// Event from the HTML-paging "previous" paging button -- pass -1 for previous page.
PagingClicked(-1);
}
private void PagingClickNext() { ////////////////////////////////////////////////////////////
// Event from the HTML-paging "next" paging button -- pass a large number for next page.
PagingClicked(999);
}
// end of paging functions /////////////////////////////////////////////////////////////////////////////////////
I have an edit form, with multiple MudTabPanels inside.
Problem is, I have LOTS of properties for this class, and we've decided to split into multiple panels, that each contain an edit form with different forms/inputs.
Format is somewhat like this (pseudo-razor-code) :
<MudTabs>
<MudTabPanel Text="Section 1">
<EditForm>
<MudItem>
<EditField Property1>
<EditField Property2>
...
<EditField Property 10>
</EditForm>
</MudTabPanel>
<MudTabPanel Text="Section 2">
<EditForm>
<MudItem>
<EditField Propertyn11>
<EditField Propertyn12>
...
<EditField Property 20>
</EditForm>
</MudTabPanel>
..... lots of other panels here
<MudTabPanel Text="Section N">
<EditForm>
<MudItem>
<EditField Property98>
<EditField Property99>
...
<EditField Property100>
</EditForm>
</MudTabPanel>
</MudTabs>
Problem is :
I have +1000 lines of code just in this razor page!
VS 2022 Preview is struggling to give me a decent performance (on the UI seems to be working fine)but modifying just a property is a pain in the ass in VS.
I was thinking about moving each Panel into a separate component , and transmitting my entity as a Parameter.
But:
1).Right now, because I use all these into a single page razor, on the code page, let's say I have the method DoSomething(), I can use this method on each panel.
Will I need to repeat the DoSomething() on each component, if i'll split them ? Is there a way I can share that method?
2).Do you think this will impact the performance on the UI?
3).Is there any better way of doing this ?
LE: Updated my data binding example
Code behind:
private Article _article;
Example of some bindings in my first tab:
<MudNumericField T="int?" #bind-value="_article.ArticleID">
<MudSelect #bind-Value="_article.UnitPriceIntervals" OffsetY="true" Label="Unit Price Interval" Variant="Variant.Outlined" Margin="Margin.Dense" Dense="true">
#foreach (UnitPriceIntervals? item in (UnitPriceIntervals[])Enum.GetValues(typeof(UnitPriceIntervals)))
{
<MudSelectItem Value="item">#item</MudSelectItem>
}
</MudSelect>
Now, my article properties can also contain references to other data types that are stored in a different SQL table, with possibility to change them, based on a search.
Example :
_article.GeneralText1 = 1234
Why not use a
#foreach (FieldAttributes fsa in cAtribs)
{
<MudTabPanel Text=#fsa.key>
<EditForm>
<MudItem>
#for(int i=0;i<fsa.Value.Count();i++)
{
<EditField #fsa.Value.ElementAt(i) />
}
</MudItem>
</EditForm>
</MudTabPanel>
}
loop, where Dictionary<string, List<string>> cAtribs.
The key of the dictionary is "Section 1", ... "Section N" and the collection has the property names for each section.
you can build cAtribs ahead of time, or dynamically, or even using reflection.
This is more a comment than an answer, but there's not enough spacing to fit it into a comment.
Track which Tab you're in and only load the edit form for the specific tab. That will significantly reduce what needs to be rendered at one time. Something like:
#if (tabNo = 2)
{
// Edit Form 2
}
You don't show your data binding, but make sure you use a view data service to hold your model data.
Consider using a component for each edit form within a Tab?
There are many complications with multi-tab editors/wizards. How are you validating and when? Is you backend one model/data table?
If you want more detail, add a comment and I'll try and put together some demo code later today.
===== Update
First get your data out of your edit component and into a ViewService. Here's a wire framework for one.
using System.Threading.Tasks;
namespace StackOverflow.Answers
{
public class ArticleViewService
{
//set up your data access
// load as Scoped Service - one per user session
public Article Article { get; private set; }
public Task GetArticle()
{
// Your get article code here
return Task.CompletedTask;
}
public Task SaveArticle()
{
// Your save article code here
return Task.CompletedTask;
}
}
}
Next your section edit components. Your data comes from the inject view service and gets updated directly into the same service. No passing data between componnts.
<h3>Section1</h3>
<EditForm EditContext="_editContext">
<InputText #bind-Value="ViewService.Article.Name"></InputText>
// or your mud editor components
.....
</EditForm>
#code {
[Inject] private ArticleViewService ViewService { get; set; }
private EditContext _editContext;
protected override Task OnInitializedAsync()
{
_editContext = new EditContext(ViewService.Article);
return base.OnInitializedAsync();
}
}
Then you Article editor, with MudTabs. This should track the active tab and display only the correct section component. I haven't tested this but it "should" work (I don't use MudBlazor and don't have it installed.)
<MudTabs #bind-ActivePanelIndex="this.panelId">
<MudTabPanel Text="Item One" ID='"pn_one"'>
#if(this.PanelId = 1)
{
\\ Section 1 componwnt
}
</MudTabPanel>
<MudTabPanel Text="Item Two" ID='"pn_two"'>
#if (this.PanelId = 2)
{
\\ Section 2 componwnt
}
</MudTabPanel>
<MudTabPanel Text="Item Three" ID='"pn_three"'>
#if (this.PanelId = 2)
{
\\ Section 3 componwnt
}
</MudTabPanel>
</MudTabs>
#code {
private int PanelId {
get => _panelId;
set => {
if (value != _panelId )
{
_panelId = value;
StateHasChanged();
}
}
}
private int _panelId = 1;
}
I am creating questionnaire application, in which I have to load random questions and get answers from the user.
I want to know, How I should iterate the records using the button.
I know how to display the data using foreach/for loop on the view, but the requirement is I have to display one question at a time, get the answer, store it in list, and when user press the "Next" button, I have to present the next question record to the user.
I would appreciate any help in this regard.
Thanks.
I have attached my sample code here:
VIEW MODEL CLASS:
public class ExamViewModel
{
public IEnumerable<Question> QuestionsList { get; set; }
public string Heading { get; set; }
public Question GetQuestionAtIndex(IEnumerable<Question> questionsList, int index = 0)
{
return questionsList.ElementAt(index);
}
}
VIEW:
<h2>#Model.Heading</h2>
<div class="panel-body">
#{
int index = 0;
var question = Model.GetQuestionAtIndex(Model.QuestionsList, index);
}
<div class="form-group">
<div class="col-sm-8">
<ul class="list-group">
<li class="list-group-item list-group-item-heading list-group-item-warning">
#question.QuestionText
</li>
</ul>
</div>
</div>
</div>
Here is a basic outline: https://dotnetfiddle.net/xKdwlK
In that example I just load all of the answers into an array and used ajax to send it to the controller. I didn't write the controller save method because I have no idea what you need there but you can fill that in.
This is just one way to do it but you could also use a form submission instead of ajax.
One of the overloads for Html.RenderPartial allows you to pass in a model. How can you make use of that from _Layout.cshtml?
I have a model prepared with everything I need. I just can't see how to make it accessible to the Layout.
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<nav>
#{ Html.RenderPartial("pMenu", menu); }
</nav>
...
pMenu.cshtml
The partial view for the menu. Hopefully this makes it clear that the menu is dynamic and really does need a model passed to it.
#model MyApplication.Menu
#foreach (KeyValuePair<string, MyApplication.MenuGroup> group in Model.group)
{
if (group.Key != "default")
{
<div>#group.Key.ToString()</div>
}
<ul>
#foreach (MyApplication.MenuItem item in group.Value.items.Values)
{
<li>
#if (item.isCurrent)
{
<span class="#item.Icon">#item.LinkText</span>
}
else
{
#Html.ActionLink(item.LinkText, item.Action, item.Controller, item.RouteValues, new { #class = #item.Icon })
}
#if (null != item.subItems)
{
<ul class="tree">
#foreach (MyApplication.SubItem subitem in item.subItems)
{
<li>
#if (subitem.isCurrent)
{
<span>#subitem.LinkText</span>
}
else
{
#Html.ActionLink(subitem.LinkText, subitem.Action, subitem.Controller, subitem.RouteValues, null)
}
</li>
}
</ul>
}
</li>
}
</ul>
}
Where is the menu instantiated?
public class MainController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
menu = new Menu(requestContext.RouteData.Values);
Background information
This is an intranet application. It does not have the typical hard-coded menu options of "Home", "About", "Contact". Most of the menu is created from user-maintained database records. The menu is individualised to the user. No two users will have the same menu items. Eg: A manager gets a menu item for each of their staff.
The menu model also handles other factors that make the menu "dynamic" such as:
identifying whether the requested page matches a given menu item (by route data) (for styling)
adding extra menu items based on roles
sometimes adding sub-menus for the current context
This seems similar to #Render partial with a model in Layout View but I think that question went off the rails by referring to session.
Disclaimer: I'm an experienced programmer but relatively new to C# and asp.NET MVC.
ViewData to the rescue.
_Layout.cshtml
#{ Html.RenderPartial("pMenu", ViewData["menu"]); }
MainController
menu = new Menu(requestContext.RouteData.Values);
ViewData["menu"] = menu;
Modifying menu after this point still works. I'm not sure why, but I'll take it.
See also Best way to do global viewdata in an area of my ASP.NET MVC site?
I think you want to render a menu. from my view you should create a helper method for this purpose. If you want to go through this technique and if you are new to create your own tag then following this basic tutorial to get some undestanding : http://www.codeproject.com/Articles/787320/An-Absolute-Beginners-Tutorial-on-HTML-Helpers-and
You could always include a base index view that contains generic things then reference your layout in the page. You somewhat inherit the view and send in model data.
#model Model.MyIndexModel
#{Layout = "~/Views/Shared/Layouts/_SinglePageLayout.cshtml";}
been trying to fix this for a few days now and just cant get it to work!
i have a radio button list which determines the output of a form - one of the radio buttons is to download X amount of files. this radio button has a text box for the user to enter the amount (X) they wish to download.
i only need this textbox to validate if the radio button that corresponds to it is selected - this is what i have so far but cannot get it to work. any help would be appriciated.
MODEL
public class myClass
{
[Required(ErrorMessage = "Please select the type of output you wish to generate")]
public int providerType { set; get; }
public int? numOutput { set; get; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (providerType == 2 && numOutput == null)
yield return new ValidationResult("Description must be supplied.");
}
}
CONTROLLER
[AcceptVerbs(HttpVerbs.Post)]
[HttpPost, ValidateInput(false)]
public ActionResult Spin(myClass theData)
{
int? ProviderType = Convert.ToInt16(Request.Form["providerType"]);
if (ModelState.IsValid)
{
//other random processing
}
}
VIEW
<ul>
<li>
<%=Html.RadioButton("ProviderType","1")%><label>Output A Single Article (With Visible HTML Tags)</label>
</li>
<li>
<%=Html.RadioButton("ProviderType","4")%><label>Output A Single Article (With HTML Pre Rendered - Not Recommended For Articles With Videos)</label>
</li>
<li>
<%=Html.RadioButton("ProviderType", "3")%><label>Output For Mass Submission</label>
</li>
<li>
<%=Html.RadioButton("ProviderType", "2")%><label>Download Several Copies Of Article (With Visible HTML Tags)</label>
How Many Artilces (Max 20)
<%= Html.TextBoxFor(Model => Model.numOutput)%>
<%= Html.ValidationMessageFor(Model => Model.numOutput)%>
im still new to MVC so im probably doing something stupid here - any help is much appriciated. The error im getting is that when i select the radio button and dont enter anything in the textbox "Input string was not in a correct format." so obviously the validation is not firing - cant quite figure out why.
The reason why its not hitting Validation is because your model is not implementing IValidatable Object.
e.g. public class MyClass:IValidatableObject
Let me know if that works for you. I was able to get the above code working locally and can send you the code if it still doesnt work for you.