Syncfusion datagrid onclick selected row? - asp.net

Is there a demo (with source) of a datagrid that when we click on a selected row, it opens the details razer page of the selected row?

You are looking for the OnRecordClick parameter. Information can be found here: https://blazor.syncfusion.com/documentation/datagrid/events#rowselecting
#using Syncfusion.Blazor.Grids
<SfGrid DataSource="#Orders">
<GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="EditMode.Normal"></GridEditSettings>
<GridEvents OnRecordClick="RecordClickHandler" TValue="Order"></GridEvents>
<GridColumns>
<GridColumn Field=#nameof(Order.OrderID) HeaderText="Order ID" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=#nameof(Order.CustomerID) HeaderText="Customer Name" Width="150"></GridColumn>
<GridColumn Field=#nameof(Order.OrderDate) HeaderText=" Order Date" Format="d" Type="ColumnType.Date" TextAlign="TextAlign.Right" Width="130"></GridColumn>
<GridColumn Field=#nameof(Order.Freight) HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn>
</GridColumns>
</SfGrid>
#code{
public List<Order> Orders { get; set; }
protected override void OnInitialized()
{
Orders = Enumerable.Range(1, 75).Select(x => new Order()
{
OrderID = 1000 + x,
CustomerID = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)],
Freight = 2.1 * x,
OrderDate = DateTime.Now.AddDays(-x),
}).ToList();
}
public class Order
{
public int? OrderID { get; set; }
public string CustomerID { get; set; }
public DateTime? OrderDate { get; set; }
public double? Freight { get; set; }
}
public void RecordClickHandler(RecordClickEventArgs<Order> args)
{
// Here, you can customize your code.
}
}

Related

How to insert record into Join table in EF Core

I'm new to Entity Framework Core code first (using EF Core 6.07). I am attempting to insert a record into a join table with two columns, but I am getting an error of "InvalidOperationException: The value of 'MovieGenre.MovieId' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known." The join table is as follows:
public class MovieGenre
{
[Required(ErrorMessage = "Movie Id required")]
public int MovieId { get; set; }
public Movies Movies { get; set; }
[Required(ErrorMessage = "Genre Id required")]
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
Its two columns reference the ID columns of the following tables:
public class Movies
{
[Key]
public int MovieId { get; set; }
[Required(ErrorMessage = "Title cannot be Null")]
[Column(TypeName = "nvarchar(256)")]
public string Title { get; set; }
[Column(TypeName = "nvarchar(MAX)")]
public string Overview { get; set; }
[Column(TypeName = "nvarchar(512)")]
public string Tagline { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Budget { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Revenue { get; set; }
[Column(TypeName = "nvarchar(2084)")]
public string ImdbUrl { get; set; }
[Column(TypeName = "nvarchar(2084)")]
public string TmdbUrl { get; set; }
[Column(TypeName = "nvarchar(2084)")]
public string PosterUrl { get; set; }
[Column(TypeName = "nvarchar(2084)")]
public string BackdropUrl { get; set; }
[Column(TypeName = "nvarchar(64)")]
public string OriginalLanguage { get; set; }
[Column(TypeName = "datetime2(7)")]
public DateTime ReleaseDate { get; set; }
public int RunTime { get; set; }
[Column(TypeName = "decimal(5,2)")]
public decimal Price { get; set; }
[Column(TypeName = "datetime2(7)")]
public DateTime CreatedDate { get; set; }
[Column(TypeName = "datetime2(7)")]
public DateTime UpdatedDate { get; set; }
[Column(TypeName = "nvarchar(MAX)")]
public string UpdatedBy { get; set; }
[Column(TypeName = "nvarchar(MAX)")]
public string CreatedBy { get; set; }
public List<MovieCrew> MovieCrews { get; set; }
public List<MovieCast> MovieCasts { get; set; }
public List<MovieGenre> MovieGenres { get; set; }
public List<Trailer> Trailers { get; set; }
public List<Favorite> Favorites { get; set; }
public List<Purchase> Purchases { get; set; }
public List<Review> Reviews { get; set; }
}
public class Genre
{
[Key]
public int GenreId { get; set; }
[Required(ErrorMessage = "Name cannot be null")]
[Column(TypeName = "nvarchar(64)")]
public string Name { get; set; }
public List<MovieGenre> MovieGenres { get; set; }
}
I have also set up my custom DB context file to manually specify the foreign key relationships between the two primary tables and the join table. Also, I have added the two foreign key columns in the join table as a composite primary key.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Movies>()
.HasMany(x => x.MovieGenres).WithOne(x => x.Movies).HasForeignKey(x => x.MovieId);
modelBuilder.Entity<Genre>()
.HasMany(x => x.MovieGenres).WithOne(x => x.Genre).HasForeignKey(x => x.GenreId);
modelBuilder.Entity<MovieGenre>()
.HasKey(o => new { o.MovieId, o.GenreId });
}
I am attempting to insert data into the join table using a model, controller, and view. I am able to successfully insert into the Movies and Genre tables, but when I try to do so for the join table, MovieGenre, I am getting the error mentioned in bold above. The MovieID and GenreID I am attempting to insert into the join table already exist in the Movie and Genre tables. Can someone please help me learn what I am doing wrong?
Edit: Adding the code I am using to insert into the cross-reference table.
IRepositoryAsync:
using System;
namespace Antra.Movie.Core.Contracts.Repositories
{
public interface IRepositoryAsync<T> where T: class
{
Task<int> InsertAsync(T Entity);
Task<int> UpdateAsync(T Entity);
Task<T> GetByIdAsync(int id);
Task<int> DeleteAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
}
}
IMovieGenreRepositoryAsync:
using System;
using Antra.Movie.Core.Entities;
namespace Antra.Movie.Core.Contracts.Repositories
{
public interface IMovieGenreRepositoryAsync:IRepositoryAsync<MovieGenre>
{
}
}
BaseRepositoryAsync:
using System;
using Antra.Movie.Core.Contracts.Repositories;
using Antra.Movie.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Antra.Movie.Infrastructure.Repositories
{
public class BaseRepositoryAsync<T>:IRepositoryAsync<T> where T: class
{
MovieDbContext db;
public BaseRepositoryAsync(MovieDbContext _context)
{
db = _context;
}
public async Task<int> DeleteAsync(int id)
{
var entity = await db.Set<T>().FindAsync(id);
db.Set<T>().Remove(entity);
return await db.SaveChangesAsync();
}
public async Task<IEnumerable<T>> GetAllAsync()
{
return await db.Set<T>().ToListAsync();
}
public async Task<T> GetByIdAsync(int id)
{
var entity = await db.Set<T>().FindAsync(id);
return entity;
}
public async Task<int> InsertAsync(T Entity)
{
await db.Set<T>().AddAsync(Entity);
return await db.SaveChangesAsync();
}
public async Task<int> UpdateAsync(T Entity)
{
db.Entry<T>(Entity).State = EntityState.Modified;
return await db.SaveChangesAsync();
}
}
}
MovieGenreRepositoryAsync:
using System;
using Antra.Movie.Core.Contracts.Repositories;
using Antra.Movie.Core.Entities;
using Antra.Movie.Infrastructure.Data;
namespace Antra.Movie.Infrastructure.Repositories
{
public class MovieGenreRepositoryAsync : BaseRepositoryAsync<MovieGenre>, IMovieGenreRepositoryAsync
{
public MovieGenreRepositoryAsync(MovieDbContext _context):base(_context)
{
}
}
}
IMovieGenreServiceAsync:
using System;
using Antra.Movie.Core.Model;
namespace Antra.Movie.Infrastructure.Services
{
public interface IMovieGenreServiceAsync
{
Task<int> InsertMovieGenre(MovieGenreModel _movieGenreModel);
}
}
MovieGenreServiceAsync:
using System;
using Antra.Movie.Core.Contracts.Repositories;
using Antra.Movie.Core.Entities;
using Antra.Movie.Core.Model;
namespace Antra.Movie.Infrastructure.Services
{
public class MovieGenreServiceAsync:IMovieGenreServiceAsync
{
IMovieGenreRepositoryAsync MovieGenreRepo;
public MovieGenreServiceAsync(IMovieGenreRepositoryAsync _movieGenreRepo)
{
MovieGenreRepo = _movieGenreRepo;
}
public async Task<int> InsertMovieGenre(MovieGenreModel _movieGenreModel)
{
MovieGenre moviegenreentity = new MovieGenre()
{
MovieId = _movieGenreModel.MovieId,
GenreId = _movieGenreModel.GenreId
};
return await MovieGenreRepo.InsertAsync(moviegenreentity);
}
}
}
MovieGenreModel:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Antra.Movie.Core.Entities;
namespace Antra.Movie.Core.Model
{
public class MovieGenreModel
{
[Required(ErrorMessage = "Movie Id required")]
public int MovieId { get; set; }
public Movies Movies { get; set; }
[Required(ErrorMessage = "Genre Id required")]
public int GenreId { get; set; }
public Genre Genre { get; set; }
public MovieGenreModel()
{
}
}
}
MovieGenreController:
using System;
using Antra.Movie.Core.Model;
using Antra.Movie.Infrastructure.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Antra.Movie.WebMVC.Controllers
{
public class MovieGenreController:Controller
{
IGenreServiceAsync genreServiceAsync;
IMovieServiceAsync movieServiceAsync;
IMovieGenreServiceAsync movieGenreServiceAsync;
public MovieGenreController(IGenreServiceAsync _genreService, IMovieServiceAsync _movieService, IMovieGenreServiceAsync _moviegenreService)
{
genreServiceAsync = _genreService;
movieServiceAsync = _movieService;
movieGenreServiceAsync = _moviegenreService;
}
public async Task<IActionResult> Index()
{
ViewBag.GenreSelect = new SelectList(await genreServiceAsync.GetAllGenres(), "Id", "Name");
return View();
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(MovieGenreModel _model)
{
if (ModelState.IsValid)
{
await movieGenreServiceAsync.InsertMovieGenre(_model);
}
return View(_model);
}
}
}
Create.cshtml view for the MovieGenreController:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model GenreModel
<form method="post" asp-controller="MovieGenre" asp-action="Create">
<div class="form-group">
<label for="GenreName">GenreId</label>
<input type="text" name="Name" class="form-control" id="GenreName" placeholder="Enter genre name">
<span asp-validation-for="Name"></span>
</div>
<div class="form-group">
<label for="GenreName">MovieId</label>
<input type="text" name="Name" class="form-control" id="GenreName" placeholder="Enter genre name">
<span asp-validation-for="Name"></span>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
First of all You don't need mapping table as MovieGenre unless you don't have any specific properties in it.
Also you don't need to do anything in the DbContext if you use code first, the mapping and everything you need will be created automatically except the DbSets.
So I would recommend:
In the context
public virtual DbSet<Movie> Movies { get; set; }
public virtual DbSet<Genre> Genres { get; set; }
Movies
public class Movie
{
public Movie()
{
this.Genres = new HashSet<Genre>();
}
public int Id { get; set; }
// Other Props....
public virtual ICollection<Genre> Genres { get; set; }
}
Genres
public class Genre
{
public Genre()
{
this.Movies = new HashSet<Movie>();
}
public int Id { get; set; }
//Other Props....
public virtual ICollection<Movie> Movies { get; set; }
}
No need to add separates,
var genre = new Genre { Name = "Action" };
var movie = new Movie { Name = "MovieName" };
movie.Genres.Add(genre);
await context.AddAsync(movie);
await context.SaveChangesAsync();
Edit
Queries
//movie by name
var movieByName = db.Movies.Include(x => x.Genres).FirstOrDefault(x => x.Name = "MovieName");
var genres = movieByName.Genres; //Genres related to The movie
//movies by genre
var moviesByGenres = context.Movies.Include(x => x.Genres).Where(x => x.Genres.Any(x => x.Name == "Action")).ToList();
//genre by name
var genre = context.Genres.Include(x => x.Movies).FirstOrDefault(x => x.Name = "Action");
var movies = genre.Movies; //Movies related to the Genre
Edit
Adding Movie with existing Genre
var movie = new Movie { Name = "MovieName" };
var genre = context.Genre.FirstOrDefault(x => x.Name == "Action");
movie.Genres.Add(genre);
await context.AddAsync(movie);
await context.SaveChangesAsync();

Filtering a paged list in Blazor

I have a Blazor app where I have a list of objects in a List. I set up a Pager Component (shown below) and it works great. Then I set up search box functionality on a set of fields from the list. All of this works great if the Pager is on the first page. Any other pages and the searches show unpredictable results. Sometimes not even filtering correctly for items on that page. Any advice would be helpful. Thanks.
Pager.razor
#typeparam TItem
<div class="row d-flex col-9">
<div class="justify-content-center">
#if (PageCount > 1 && List.Count > PageSize)
{
<ul class="pagination justify-content-center">
<li><button #onclick="#(() => ChangePage(1))" class="btn">«</button></li>
#for (var i = StartIndex; i <= FinishIndex; i++)
{
var currentIndex = i;
#if (i == CurrentPage)
{
<li class="page-item active"><span class="btn">#i</span></li>
}
else
{
<li class="page-item"><button class="btn page-link" #onclick="#(() => ChangePage(currentIndex))">#i</button></li>
}
}
<li><button #onclick="#(() => ChangePage(PageCount))" class="btn">»</button></li>
</ul>
}
</div>
<select class="custom-select offset-1 col-1 ml-auto" bind="#PageSize" #onchange="#(e => ChangePageSize(e))">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
</div>
#code {
[Parameter]
public List<TItem> List { get; set; }
public List<TItem> Display { get; set; }
[Parameter]
public Action<List<TItem>> DisplayChanged { get; set; }
[Parameter]
public Action<bool> Rendered { get; set; }
private int PageSize { get; set; } = 10;
private int CurrentPage { get; set; } = 1;
private int StartIndex { get; set; }
private int FinishIndex { get; set; }
private int PageCount { get; set; }
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
Rendered?.Invoke(true);
}
private void ChangePageSize(ChangeEventArgs e)
{
PageSize = int.Parse(e.Value.ToString());
ChangePage(1);
}
private void ChangeDisplay()
{
DisplayChanged?.Invoke(
List
.Skip((CurrentPage - 1) * PageSize)
.Take(PageSize)
.ToList()
);
}
protected override void OnParametersSet()
{
ResetIndex();
ChangeDisplay();
base.OnParametersSet();
}
protected void ChangePage(int page)
{
CurrentPage = page;
ResetIndex();
ChangeDisplay();
}
private void ResetIndex()
{
PageCount = List.Count / PageSize;
if (List.Count % PageSize > 0)
{
PageCount++;
}
StartIndex = Math.Max(CurrentPage - 5, 1);
FinishIndex = Math.Min(CurrentPage + 5, PageCount);
}
}
Pager Use
<Pager List="#FilteredUsers" DisplayChanged="#DisplayChanged" Rendered="#PagerRendered" />
Search Function
<input class="form-control" type="text" #bind-value="#SearchTerm" #bind-value:event="oninput" />
<select class="form-control" #bind-value="#Property" #bind-value:event="onchange">
<option value="FirstName">First Name</option>
<option value="LastName">Last Name</option>
<option value="Role">Role</option>
<option value="Property">Property</option>
</select>
private string searchTerm;
private string SearchTerm
{
get => searchTerm;
set
{
searchTerm = value;
Filter();
}
}
Filter
private void Filter()
{
switch (Property)
{
case "FirstName":
FilteredUsers = Users.Where(u => u.FirstName.ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "LastName":
FilteredUsers = Users.Where(u => u.LastName.ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "Role":
FilteredUsers = Users.Where(u => u.Role.ToString().ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "Property":
if (string.IsNullOrEmpty(SearchTerm))
{
FilteredUsers = Users;
}
else
{
FilteredUsers = Users.Where(u => TicketingRosters.Any(t => t.Property.PropertyName.ToLower().Contains(SearchTerm.ToLower()) && u.UserId == t.SellerId)).ToList();
}
break;
}
StateHasChanged();
}
Edit
Here are the other functions and Properties that you may be looking for:
private List<UserDto> Users { get; set; }
private List<UserDto> FilteredUsers { get; set; }
private List<UserDto> Display { get; set; }
private bool IsPagerRendered { get; set; }
private void DisplayChanged(List<UserDto> display)
{
Display = display;
}
private void PagerRendered(bool rendered)
{
IsPagerRendered = rendered;
StateHasChanged();
}
Edit 2
private List<TicketingRoster> TicketingRosters { get; set; } = new List<TicketingRoster>();
UserDto
#nullable enable
using System;
using System.ComponentModel.DataAnnotations;
namespace TicketingSolutions.Models.DTOs
{
public class UserDto
{
public long UserId { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "First name cannot be empty.")]
[StringLength(75, ErrorMessage = "First Name too long. (75 characters)")]
public string? FirstName { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Last Name cannot be empty.")]
[StringLength(75, ErrorMessage = "Last Name too long. (75 characters)")]
public string? LastName { get; set; }
[StringLength(75, ErrorMessage = "Middle Name too long. (75 characters)")]
public string? MiddleName { get; set; }
[StringLength(150, ErrorMessage = "Title too long. (150 characters)")]
public string? Title { get; set; }
[DataType(DataType.EmailAddress)]
[StringLength(150, ErrorMessage = "Email Address too long. (150 characters)")]
public string? EmailAddress { get; set; }
[RegularExpression(#"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}", ErrorMessage = "Not a valid phone number format: (xxx) xxx-xxxx")]
[StringLength(50, ErrorMessage = "Phone Number too long. (50 characters)")]
public string? OfficePhone { get; set; }
[RegularExpression(#"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}", ErrorMessage = "Not a valid phone number format: (xxx) xxx-xxxx")]
[StringLength(50, ErrorMessage = "Phone Number too long. (50 characters)")]
public string? OtherPhone { get; set; }
public Guid? AdUserId { get; set; }
public bool IsActive { get; set; }
public int? RegionId { get; set; }
public Region? Region { get; set; }
public DateTime CreatedOn { get; set; }
public long CreatedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public long ModifiedBy { get; set; }
public Roles Role { get; set; }
public int? RoleId { get; set; }
public CommissionRole? CommissionRole { get; set; }
}
}
TicketingRoster
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace TicketingSolutions.Models
{
[Table("TicketingRosters")]
public class TicketingRoster : IValidatableObject
{
[Key]
public long TicketingRosterId { get; set; }
public int PropertyId { get; set; }
public Property Property { get; set; }
public long SellerId { get; set; }
public User Seller { get; set; }
public bool IsActive { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime? ValidTo { get; set; }
public DateTime CreatedOn { get; set; }
public long CreatedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public long ModifiedBy { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ValidTo <= ValidFrom)
{
yield return new ValidationResult("ValidTo cannot be set to a date before or equal to ValidFrom", new[] { nameof(ValidTo) });
}
}
}
}
It's a bit hard to follow as there are some external methods that you haven't shown in this example (edit maybe for clarity?) but it appears that you are using something outside your Pager.razor to dictate how your paging works. The giveaway is the DisplayChanged event callback. I can suggest that you tackle this by breaking up the concerns, and you can get everything working. (I was able to get it running fairly quickly working from what you have)
First off, let's set the Pager up so all it does is handle paging the information, and it contains all of it's own logic to do so in a self contained, reusable component. give it a list of TItem and a RenderFragment<TItem> and it knows what to do.
#typeparam TItem
<div class="row d-flex col-9">
<div class="justify-content-center">
#if (List != null)
{
#foreach (var item in DisplayList)
{
#ChildContent(item)
}
}
#if (PageCount > 1 && List.Count > PageSize)
{
...Nothing here was changed, eliminated for brevity...
}
</div>
<select class="custom-select offset-1 col-1 ml-auto" bind="#PageSize" #onchange="#(e => ChangePageSize(e))">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
</div>
#code {
[Parameter]
public List<TItem> List { get; set; }
public List<TItem> DisplayList { get; set; } = new List<TItem>();
[Parameter]
public RenderFragment<TItem> ChildContent { get; set; }
//[Parameter]
//public Action<List<TItem>> DisplayChanged { get; set; }
//[Parameter]
//public Action<bool> Rendered { get; set; }
private int PageSize { get; set; } = 10;
private int CurrentPage { get; set; } = 1;
private int StartIndex { get; set; }
private int FinishIndex { get; set; }
private int PageCount { get; set; }
//protected override void OnAfterRender(bool firstRender)
//{
// base.OnAfterRender(firstRender);
// Rendered?.Invoke(true);
//}
private void ChangePageSize(ChangeEventArgs e)
{
PageSize = int.Parse(e.Value.ToString());
ChangePage(1);
}
private void ChangeDisplay()
{
DisplayList = List
.Skip((CurrentPage -1) * PageSize)
.Take(PageSize)
.ToList();
}
protected override void OnParametersSet()
{
// Edited
ChangePage(1);
}
protected void ChangePage(int page)
{
CurrentPage = page;
ResetIndex();
ChangeDisplay();
}
private void ResetIndex()
{
PageCount = List.Count / PageSize;
if (List.Count % PageSize > 0)
{
PageCount++;
}
StartIndex = Math.Max(CurrentPage - 5, 1);
FinishIndex = Math.Min(CurrentPage + 5, PageCount);
}
}
You'll see that a few things in the #code block are commented out. You won't need these to get this working. We'll handle the values in the initial list in a minute in the Parent component. You'll see a RenderFragment<TItem> parameter, and a new property for DisplayList that is NOT a parameter. You'll also notice in the markup, we have a #foreach block rendering an instance of our RenderFragment for each item in the DisplayList property. If you follow the logic from the OnParametersSet method and your handlers for clicking the page numbers and arrows, you'll see that we are creating and rendering a sub-list of our List parameter based on page count and page number, and that is all that is rendered. This component is now responsible for paging items from a list it's given, and it has no external dependencies to run other than a list to render, and instructions on how to render each item in the form of a RenderFragment<TItem>.
Next, in the Parent component, we set up the call to the pager like so:
<Pager TItem="User" List="FilteredUsers">
<h6>#context.FirstName #context.LastName is in #context.Role </h6>
</Pager>
You can set this up however you'd like, I used <h6> tags for illustration, but follow the directions here if you need more depth, creating tables, lists, etc. This component now accepts the child content between the <Pager> tags and renders 1 each for every item in it's own paged list.
So far, we've decoupled the pager's logic from the rest of the page, so now it's a rendering tool only and the paging logic is internal. Now we can focus on the filtering and forget about the paging.
Search filtering:
First off, I set an initial value for the search "Property" and set a backing field like so:
private string property = "FirstName";
private string Property
{
get => property;
set
{
property = value;
Filter();
}
}
This now aligns with the drop down starting value, since the <select> only updated this on change, and also updates search results when you change the dropdown.
I left your SearchTerm property like you had it.
Now in the Filter method:
private void Filter()
{
if (string.IsNullOrEmpty(SearchTerm))
{
FilteredUsers = Users;
}
else
{
switch (Property)
{
case "FirstName":
FilteredUsers = Users.Where(u => u.FirstName.ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "LastName":
FilteredUsers = Users.Where(u => u.LastName.ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "Role":
FilteredUsers = Users.Where(u => u.Role.ToString().ToLower().Contains(SearchTerm.ToLower())).ToList();
break;
case "Property":
FilteredUsers = Users.Where(u => u.Role.ToString().ToLower().Contains(SearchTerm.ToLower())).ToList();
//FilteredUsers = Users.Where(u => TicketingRosters.Any(t => t.Property.PropertyName.ToLower().Contains(SearchTerm.ToLower()) && u.UserId == t.SellerId)).ToList();
break;
default:
FilteredUsers = Users;
break;
}
}
StateHasChanged();
}
This now checks for a search value first, and returns the full Users list if it's empty. If not, then you enter the switch. The cases all work as I have them, however you'll see that I commented out the Where logic you had originally. Honestly, TicketingRosters is not familiar to me and I can tell your logic is in a domain I know nothing about, so you'll have to reason about that last case on your own. BUT, if everything else works, you now have a focused approach to where to find your bug.
So now the Filter method sets up a filtered list, the Pager takes that entire list and handles paging on it's own, and you have nicely separated concerns, so if you have a problem you know where to look. Not paging correctly -> it's in the pager. Not filtering correctly -> it's in the filter logic.
Edit
After the OP added the TicketingRoster and UserDto class definitions, I made the mistake of thinking I had found the issue in the query. However, through some back and forth the OP cleared up that the Pager I outlined above was still not functioning quite right and the queries were, and after another look I cleared up the OnParametersSet method to call ChnagePage(1), which will both reset the current page to 1 and reset the page count. A test on my side confirmed the OP's issue with my original response had been cleared up.
My first edit contained quite a bit about the query structure and theory, and I removed that section because a) it is no longer relevant to this discussion, and b) looking back, it may have come off as condescending, though that was never my intention at any point.

How to include another table to view its data in Views in asp.net mvc

I have this code -
var add = (from h in db.Hotels
where h.Address.Contains(hotels.Address)
select h).Take(2);
ViewBag.Related = add;
Now, in the View, I want to display the images, so I'm using this code -
<img src="~/img/#item.FirstOrDefault().Image" />
This is giving me this error -
'System.Data.Entity.DynamicProxies.Hotels_D1EE6FD2E11BD1D9436F26FEA6336CFE76F33C59111E2ABC7C1BBE456FF61C23' does not contain a definition for 'FirstOrDefault'
I've tried using 'joins' also but same error occurs. Please help me out in this! :(
My Hotels class -
public class Hotels
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Address { get; set; }
[StringLength(8)]
public string PinCode { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string FilledBy { get; set; }
public DateTime DateAdded { get; set; }
//public int ImageId { get; set; }
public int TotalRooms { get; set; }
public bool Available { get; set; }
public virtual ICollection <Rooms> Rooms { get; set; }
public virtual ICollection <Images> Images { get; set; }
public virtual ICollection<Ameneties> Ameneties { get; set; }
public virtual ICollection <Bookings> Bookings { get; set; }
public virtual ICollection<NearByLocations> Nearby { get; set; }
public virtual ICollection<Ratings> Ratings { get; set; }
public virtual ICollection<RoomType> RoomTypes { get; set; }
public virtual ICollection<CustomerReviews> Reviews { get; set; }
public virtual ICollection<HotelRules> HotelRules { get; set; }
}
My Images class -
public class Images
{
[ScaffoldColumn(false)]
public int id { get; set; }
public string Image { get; set; }
public int? HotelId { get; set; }
public virtual Hotels Hotels { get; set; }
//public ICollection<Hotels> Hotels { get; set; }
}
I have used this type of collections...
This is my Details View Controller code -
public ActionResult Details(int? id)
{
IEnumerable<Images> galleries = (from gallery in db.Images
where gallery.Hotels.Id == id
select gallery);
ViewBag.Images = galleries;
ViewBag.ImgCount = galleries.Count();
IEnumerable<Ameneties> ameneties = (from a in db.Ameneties
where a.Hotels.Id == id
select a);
ViewBag.Ameneties = ameneties;
IQueryable<Rooms> rooms = (from room in db.Rooms
where room.Hotels.Id == id
select room);
var ratings = (from rating in db.Ratings
where rating.Hotels.Id == id
select rating.Points);
ViewBag.Ratings = ratings;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Hotels hotels = db.Hotels.Find(id);
if (hotels == null)
{
return HttpNotFound();
}
var add = db.Hotels.Include("Images").Where(h => h.Address.Contains(hotels.Address))
.Select(h => h)
.Take(2)
.ToList();
var model = new MyViewModel { Hotels = add };
ViewBag.Reviews = hotels.Reviews;
ViewBag.Ratings = hotels.Ratings;
ViewBag.NearBy = hotels.Nearby;
ViewBag.RoomTypes = hotels.RoomTypes;
ViewBag.Rules = hotels.HotelRules;
return View(hotels);
}
Could you add ToList() at the end of the query?
var add = (from h in db.Hotels
where h.Address.Contains(hotels.Address)
select h).Take(2)
.ToList();
ViewBag.Related = add;
Then you could call item.Images.FirstOrDefault()?.Image.
#foreach (var item in ViewBag.Related)
{
<img src="~/img/#item.Images.FirstOrDefault().Image" />
}
If it still doesn't work, you will need to explicitly load Image when you query Hotel. For example,
public IActionResult Index()
{
var add = db.Hotels
.Include("Images")
.Where(h => h.Address.Contains(hotels.Address))
.Select(h => h)
.Take(2)
.ToList();
var model = new MyViewModel { Hotels = add };
return View(model);
}
View
#model YourNameSpace.Models.MyViewModel
#foreach (var item in Model.Hotels)
{
<img src="~/img/#item.Images.FirstOrDefault().Image" />
}
Model
public class MyViewModel
{
public List<Hotels> Hotels { get; set; }
}

select list by specific id using two models in one view- Asp.Net MVC Razor

Classes
public partial class Category
{
public Category()
{
this.SubCategories = new HashSet<SubCategory>();
}
public int Cat_id { get; set; }
public string Cat_Name { get; set; }
public string Cat_Desc { get; set; }
public virtual ICollection<SubCategory> SubCategories { get; set; }
}
public partial class SubCategory
{
public int SubCat_id { get; set; }
public byte SubCat_icon { get; set; }
public string SubCat_Name { get; set; }
public string SubCat_Desc { get; set; }
public int Cat_id { get; set; }
public virtual Category Category { get; set; }
}
Controller
public ActionResult Index()
{
List<object> myModel = new List<object>();
myModel.Add(db.Categories.ToList());
myModel.Add(db.SubCategories.ToList());
return View(myModel);
}
View
#model IEnumerable<object>
#{
List<ProjName.Models.Category> IstCategory = Model.ToList()[0] as List<ProjName.Models.Category>;
List<ProjName.Models.SubCategory> IstSubCategory = Model.ToList()[1] as List<ProjName.Models.SubCategory>;
}
<h1>Category</h1>
#foreach(var item in IstCategory)
{
<div>#item.Cat_Name</div><br />
}
<hr />
<h1>SubCategory</h1>
#foreach(var item in IstSubCategory)
{
<div>#item.SubCat_Name</div><br />
}
How to pass distinct/specific id in foreach() loop and where condition, when data is selected in list?
Are you trying to achieve this? Each Category has its SubCategories list.
<h1>Category</h1>
#foreach(var cat in IstCategory)
{
<div>#cat.Cat_Name</div><br />
<hr />
<h1>SubCategory</h1>
#foreach(var sub_cat in IstSubCategory.Where(s => s.Cat_id == cat.Cat_id))
{
<div>#sub_cat.SubCat_Name</div><br />
}
}

ASP.NET MVC 3 Model null on submit

View
#model Survey.Models.TakeSurveyViewModel
#{
Layout = "~/Views/Shared/_SiteLayout.cshtml";
}
<h2>SURVEY : #Model.Title</h2>
<h3>#Model.Description</h3>
<hr />
#using (Html.BeginForm("SubmitSurvey", "HomePage", FormMethod.Post, new { id = "surveyForm" }))
{
for (int index = 0; index < Model.SurveyQuestions.Count; index++)
{
#* Editor Template - Null result *#
#*SurveyQuestionModel item = Model.SurveyQuestions[index];
#Html.EditorFor(x => item);*#
<p>#Model.SurveyQuestions[index].QuestionText</p>
#Html.DropDownListFor(item => Model.SurveyQuestions[index].OptionId, new SelectList(Model.SurveyQuestions[index].Options, "OptionId", "OptionText"), string.Empty)
}
<input type="submit" value="SubmitSurvey" />
}
ViewModel
public class TakeSurveyViewModel
{
public string Title { get; set; }
public string Description { get; set; }
public int SurveyId { get; set; }
public List<SurveyQuestionModel> SurveyQuestions { get; set; }
public TakeSurveyViewModel() { }
public TakeSurveyViewModel(int surveyId)
{
//Populate data - works ok.
}
}
DropDownList Model
public class SurveyQuestionModel
{
public int QuestionId { get; set; }
public string QuestionText { get; set; }
[Required(ErrorMessage = "Please select an option.")]
public int OptionId { get; set; }
public IEnumerable<QuestionOption> Options { get; set; }
}
The page is rendering fine, with all the drop-downs with correct options. The id and name of each select is also unique -
id="SurveyQuestions_3__OptionId" name="SurveyQuestions[3].OptionId"
Controller Action
[HttpPost]
public ActionResult SubmitSurvey(TakeSurveyViewModel model)
{
return !ModelState.IsValid ? TakeSurvey(model.SurveyId) : null;
}
But clicking on the submit button, controller action model is null.
Edit: Removed the 2x HTML.BeginForm
Edit 2: SurveyQuestions now has public setter. The issue seem to be still there. Please see this image:
Is the problem that you have SurveyQuestions being a private set?
-old answer-
You have 2 x
(Html.BeginForm(Html.BeginForm
I once forgot to have my form within a using statement which did the same sort of thing.

Resources