Blazor checkbox filtering strange rendering - checkbox

I have a list of CheckModel classes with properties int Id and bool IsChecked. I want to filter them based on the IsChecked property.
When I render them in a foreach loop filtering the already checked items, I get buggy behavior. The item is removed from the view, but the item below that takes it's place in the view renders as checked, while in fact it is not.
Here is a gif showing this behavior:
It seems that Blazor's rendering somehow lags behind with the checkboxes..
Here is the code:
#page "/"
<div>
<input id="filter-collected-checkbox" type="checkbox" #bind="FilterChecked" />
<label for="filter-collected-checkbox">Filter</label>
</div>
#foreach((CheckModel item, int index) in CheckModels.Where(x=>!FilterChecked || !x.IsChecked).Select((x,i)=>(x,i)))
{
<div style="display: flex">
#item.Id
<input id="item-collected-checkbox-#index" type="checkbox" checked="#item.IsChecked" #onchange="(e)=>MarkItemCollected(e,item)"/>
</div>
}
#code {
public List<CheckModel> CheckModels { get; set; }
public bool FilterChecked { get; set; }
protected override void OnInitialized()
{
CheckModels = new List<CheckModel>();
for (int i = 0; i < 10; i++)
{
CheckModels.Add(new CheckModel() { Id = i });
}
}
private void MarkItemCollected(ChangeEventArgs e, CheckModel item)
{
item.IsChecked = (bool)e.Value;
}
}
The reason why I'm using html checked-attribute with #onchange is because I want to have a method after the binding has occurred. If I use #bind=IsChecked with #onclick=Method, the #onclick is fired before the binding.
Anyone know how to fix this?

You need to use the #key for the loop contents so the Render engine knows what items need updating.
<div #key=#item.Id style="display: flex">
Docs are here
Working REPL
FYI:
<div>
<input id="filter-collected-checkbox" type="checkbox" #bind="filterChecked" />
<label for="filter-collected-checkbox">Filter</label>
</div>
#foreach(var item in FilteredItems)
{
<div #key=#item.Id style="display: flex">
#item.Id
<input id="item-collected-checkbox-#item.Id" type="checkbox" #bind="#item.IsChecked" />
</div>
}
#code {
List<CheckModel> checkModels = Enumerable.Range(0,10)
.Select(i => new CheckModel() { Id = i })
.ToList();
bool filterChecked;
IEnumerable<CheckModel> FilteredItems =>
filterChecked ? checkModels.Where(x=> !x.IsChecked) : checkModels;
}
Renders the same result.

Related

How to insert multiple values from .net 5.0 razor pages in SQL Server?

I want to select multiple services for a client and save them to database, on single click.
When i click save, it stores only the first service.
So far, on the backend side (OnPost), i have tried with:
.add() , addrange(), foreach(), for(), but still no luck.
I cant understand what i am doing wrong!
My frontend code is as below:
<form method="post">
<div class="col-md-12">
#for (int i = 18; i < 40; i++)
{
<input asp-for="ClientServices.CliId" class="form-control" value="#Model.CliIdOk" hidden />
<input class="form-control" asp-for="ClientServices.ServId" value="#i" hidden />
<div class="border-0" style="width: 6%; height: 150px; min-width: 30px; float: left; ">
<div class="form-group form-check" style="position:relative;left:-15px;">
<label class="form-check-label btn btn-sm fw-bold border m-1 p-1" style="border-radius:16px;">
<input asp-for="ClientServices.Active"
style="border-radius:16px;position:relative;" />
<br />
#i
</label>
</div>
</div>
}
</div>
<button class="btn btn-sm btn-outline-primary fw-bold" type="submit" name="id"
style="font-size: 120%;float:unset;min-width:120px;">
Save
</button>
</form>
My backend "OnPost" code is:
public async Task<IActionResult> OnPostAsync()
{
try
{
//if (!ModelState.IsValid)
//{
// return Page();
//}
_context.ClientServices.AddRange(ClientService);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
catch (Exception e)
{
e.ToString();
return RedirectToPage("./Index");
}
}
When i click save, it stores only the first service. So far, on the
backend side (OnPost), i have tried with: .add() , addrange(),
foreach(), for(), but still no luck. I cant understand what i am doing
wrong!
Well, couple of mistake has been found on your code.
Mistake: 1:
Getting only the first object while submitting list is pretty obvious as per your shared code snippet. You cannot save all list following this way because, your post method model doesn't aware of your list. As you are sending the flat object like this asp-for="ClientServices.CliId" so only the first object will be evaluated instead you outght to send like this way asp-for="#Model.clientServices[i].CliId". So that model binding can map the object list with your backed.
Mistake: 2:
On OnPostAsync() you supposed to send list of object not sure how you are passsing it AddRange(ClientService);. I think here in ClientService you are not getting the list as well.
Solution:
cshtml:
Your view should be modified as following:
#page
#model RazorPageDemoApp.Pages.AddMultipleDataModel
<form method="post">
<div class="col-md-12">
#for (int i = 0; i <Model.clientServices.Count(); i++)
{
<input asp-for="#Model.clientServices[i].CliId" class="form-control" value="#Model.clientServices[i].CliId" hidden />
<input class="form-control" asp-for="#Model.clientServices[i].ServId" value="#Model.clientServices[i].ServId" hidden />
<div class="border-0" style="width: 6%; height: 150px; min-width: 30px; float: left; ">
<div class="form-group form-check" style="position:relative;left:-15px;">
<label class="form-check-label btn btn-sm fw-bold border m-1 p-1" style="border-radius:16px;">
<input asp-for="#Model.clientServices[i].Active" value="#Model.clientServices[i].CliId"
style="border-radius:16px;position:relative;" />
<br />
#i
</label>
</div>
</div>
}
</div>
<div>
<button class="btn btn-sm btn-outline-primary fw-bold" type="submit" name="id"
style="font-size: 120%;float:unset;min-width:120px; margin-top:100px">
Save
</button>
</div>
</form>
Note: The crucial part are Model.clientServices.Count() and asp-for="#Model.clientServices[i].CliId" if doesn't map your model this way, your model binding wouldn't be mapped accordingly.
cshtml.cs:
public class AddMultipleDataModel : PageModel
{
private readonly AppDbContext _context;
public AddMultipleDataModel(AppDbContext context)
{
_context = context;
}
public List<ClientService> clientServices { get; set; }
public void OnGet()
{
var clientServiceList = new List<ClientService>()
{
new ClientService(){ CliId ="CliId:1",ServId = "ServId-A",Active = false},
new ClientService(){ CliId ="CliId:2",ServId = "ServId-B",Active = false},
new ClientService(){ CliId ="CliId:3",ServId = "ServId-C",Active = true},
new ClientService(){ CliId ="CliId:4",ServId = "ServId-D",Active = true},
new ClientService(){ CliId ="CliId:5",ServId = "ServId-E",Active = false},
};
clientServices = clientServiceList;
}
[HttpPost]
public async Task<IActionResult> OnPostAsync(List<ClientService> clientServices)
{
try
{
//if (!ModelState.IsValid)
//{
// return Page();
//}
_context.clientServices.AddRange(clientServices);
await _context.SaveChangesAsync();
return RedirectToPage("./AddMultipleData");
}
catch (Exception e)
{
e.ToString();
return RedirectToPage("./Index");
}
}
}
Note: As you might notice, I have constructed post method as OnPostAsync(List<ClientService> clientServices) means it will accept list of object from your view.
Output:
Note: If you are interested to know more about Razor page post list and other stuff, please have a look our official document here.

passing array from angular js to Web API = null

Dears, I am sending an object from angularjs to web API. all properties are sent to API and received correctly except 2 arrays, they are received by null in c# with out knowing a reason so if you could help it will be great
in the department (dep)array i am sending 1 value $scope.dep
in the cont array i am sending multipule values , may be pushing to the array is not correctly, i dont know
HTML
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-12">
<div class="form-group">
<div class="col-xs-12">
<label class="control-label" for="name">Contracts <span class="danger">*</span></label>
</div>
<div class="col-xs-12">
<select id="example-post" class="form-control input-sm" multiple="multiple" ng-model="cont" ng-change="addcontracts(cont)">
<option ng-repeat="c in selectedcontracts" value="{{c.Sys_Key}}">{{c.Cont}}</option>
</select>
</div>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-12">
<div class="form-group">
<div class="col-xs-12">
<label class="control-label" for="name">Department <span class="danger">*</span></label>
</div>
<div class="col-xs-12">
<select class="form-control input-sm" ng-model="dep" ng-options="d.Dep_key as d.dep for d in staffdepartments | unique:'dep'"></select>
</div>
</div>
</div>
Angular js
this.Repttx_PayRollTotal_Net = function (url, Attendlog) {
return $http.post(urlBase + '/' + url, Attendlog)
};
$scope.newcontracts = [];
$scope.addcontracts = function (cont) {
console.log(cont);
$scope.newcontracts = [];
$scope.newcontracts.push(cont) ;
console.log($scope.newcontracts);
}
var Attendlog = { DFrom: $scope.from1, Fg: $scope.fg, StaffKey: $scope.staffkey, StatusServ: $scope.jbsts, Cont: JSON.stringify($scope.newcontracts), Order: $scope.sor }
AtendanceReprtingSrv.Repttx_PayRollTotal_Net("AttendanceReprting/Repttx_PayRollTotal_Net", Attendlog).then(function (response) {
$scope.ttx = (response.data);
document.getElementById("frmrepodbc").src = $scope.ttx;
$("#dialog_PrintRptObc").modal("show");
})
C# class and its properties
public class Attendlogs
{
public DateTime DFrom { get; set; }
public short Fg { get; set; }
public long StaffKey { get; set; }
public int StatusServ { get; set; }
public int[] Dep { get; set; }
public int[] Cont { get; set; }
public int Order { get; set; }
}
C#
[HttpPost]
public string Repttx_PayRollTotal_Net([FromBody] Attendlogs logs)
{
DataTable DTres = new DataTable();
DTres = null;
HR_ReportingTimeAttendDL.ReportiingDL hr = new HR_ReportingTimeAttendDL.ReportiingDL();
DTres = hr.AttendLog(logs.DFrom, logs.Fg, logs.StaffKey, logs.StatusServ, logs.Dep, logs.Cont, logs.Order);
Thanks in advance
you are sending the arrays as objects to your api
Cont: JSON.stringify($scope.newcontracts)
you might need to loop on your data and push ids into arrays , and send this array to Backend
because at the model on Attendlogs class on the Backend side , you are expecting an integer array.

In Vue, how to cancel a checkbox based on a condition?

I want to always have at least one checkbox checked but I mix the concepts of v-model and :checked.
The doc says:
v-model will ignore the initial value, checked or selected attributes
found on any form elements. It will always treat the Vue instance data
as the source of truth.
I can prevent my model to be modified but I can't prevent the checkbox to be checked...
Some code:
The template
<div class="wrapper" v-for="(s, id) in userOutputSeries" :key="id">
<input type="checkbox" :id="id" :value="id" #change="preventIfLessThanOneChecked" :checked="s.active">
<label :for="id">{{ s.display }}</label>
</div>
The model userOutputSeries
data () {
return {
userOutputSeries: {
foo: {
display: 'Awesome Foo',
active: true
},
bar: {
display: 'My Bar',
active: false
}
}
}
}
The preventIfLessThanOneChecked handler
preventIfLessThanOneChecked (event) {
// I don't update the model so it stay at the same state
// But only need to count the active series and do what we want.
console.log(event.target.value, event.target.checked)
}
Any ideas to stop the native checkbox propagation?
You should use v-model instead of :checked so that changes to the userOutputSeries data property will be reflected in the checkbox input.
Then, pass the s reference from the v-for to the method and set that object's active property to true if there are no active checkboxes:
new Vue({
el: '#app',
data() {
return {
userOutputSeries: {
foo: {
display: 'Awesome Foo',
active: true
},
bar: {
display: 'My Bar',
active: false
}
}
}
},
methods: {
preventIfLessThanOneChecked(item) {
if (item.active) {
return;
}
let items = Object.values(this.userOutputSeries);
if (!items.find(i => i.active)) {
item.active = true;
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<div class="wrapper" v-for="(s, id) in userOutputSeries" :key="id">
<input type="checkbox" :id="id" :value="id" #change="preventIfLessThanOneChecked(s)" v-model="s.active">
<label :for="id">{{ s.display }}</label>
</div>
</div>
Try using disabled on your single checked checkbox:
<div class="wrapper" v-for="(s, id) in userOutputSeries" :key="id">
<input type="checkbox" :id="id" :value="id"
:disabled="(s.active && numberOfChecked == 1) ? disabled : null"
#change="preventIfLessThanOneChecked" :checked="s.active">
<label :for="id">{{ s.display }}</label>
</div>
In the above answer given by #thanksd, my checkbox remains unchecked.
So i am writing my solution.
This my loop statement, change the variable names according to your file.
v-for="column in tableColumns"
This is my input ( if visible is true then checkbox is checked )
<input type="checkbox" v-model="column.visible" #change="event => visibleColumnsChanged(column, event)">
Then in my change method
- if there is no visible item left set the column.visible to true
- use event.target.checked = true to check the checkbox again.
visibleColumnsChanged: function(column, event){
if (column.visible) {
return;
}
if(! this.tableColumns.find(c => c.visible)){
column.visible = true;
event.target.checked = true;
}
}

Set img src property from byte[]

I'm trying to show the thumbnailPhoto property of an ActiveDirectory user on an MVC View.
I'm parsing the needed properties to a List of a class named Employee:
for (int counter = 0; counter < resultCol.Count; counter++)
{
result = resultCol[counter];
if (result.Properties.Contains("samaccountname") &&
result.Properties.Contains("displayname") &&
result.Properties.Contains("sn") &&
result.Properties.Contains("givenName"))
{
list.Add(new Employee
{
Email = (String)result.Properties["samaccountname"][0],
FirstName = (String)result.Properties["givenName"][0],
LastName = (String)result.Properties["sn"][0],
pictureBLOB = result.Properties.Contains("thumbnailPhoto") ? (byte[])result.Properties["thumbnailPhoto"][0] : null,
});
}
}
I did some research on how to display the picture on the Index View and found this as a possible solution:
<div class="container">
#foreach (var item in Model)
{
String imgSrc = String.Empty;
String base64 = String.Empty;
if (item.pictureBLOB != null)
{
base64 = Convert.ToBase64String(item.pictureBLOB);
imgSrc = String.Format("data:image;base64,{0}", base64);
}
<div id="#Html.DisplayFor(i => item.Number)" class="col col-lg-2 col-md-4 col-sm-6">
<a class="post" mailto="#item.Email" href="#">
<img src="{#imgSrc}" title="#Html.DisplayFor(i => item.LastName), #Html.DisplayFor(i => item.FirstName)" />
#*onerror="this.src = '../../Content/Images/nopicture.gif';" />*#
</a>
</div>
}
</div>
But when I call the index page, the pictures won't be shown. Is there any other possibility to show the profile pictures?
Finally I found the solution. I simply had to remove the braces around #imgSrc in
<img src="{#imgSrc}" title="#Html.DisplayFor(i => item.LastName), #Html.DisplayFor(i => item.FirstName)" />

How to display the last inserted ID (primary key) of a table in a html textbox that is in a relationship with a foreign key of another table in mvc

I'm new to asp.net and MVC, and I have a problem.
I know that this is something simple but I do not know how to do it. I seek advice and would like to thank you in advance for any help.
This is my problem:
I have 2 tables: table X: ID (primary key), Number; and table Y: ID (primary key), NID (foreign key with relationship with table X), etc.
What I want to know is how to display last inserted ID into the view of table Y on an Html editor for NID the last value of ID (table X)?
For example, I create a new row in table X, and when I want to create the row in table Y that corresponds with table X to automatically get the last ID inserted in the textbox or editor?
Can anybody give me some kind of reference or an example! Thank you for your help! Sorry for any bad spelling.
Here we go . I tested this and it returned me the model properties along with files posted . This example gives you ideea how POSt method used in MVC and how to send model propertied back to controller .
//-- this is the controller
public class FileUploadDemoController : Controller
{
//
// GET: /FileUploadDemo/
public ActionResult Index()
{
// here find the last if of the FileUploadtable
var ctx = new TestDbContext();
var maxId = ctx.Fileuploads.ToList().OrderByDescending(u => u.Id).FirstOrDefault();
var newId = maxId == null ? 1 : maxId.Id + 1;
return View("Index", new FileUploadModel { Id= newId });
}
[HttpPost]
public ActionResult PostForm(FileUploadModel model)
{
// here you have NewId in model.Id method ; Now ypour table b in my case is fileeuploadhistory I want to insert a new record with this model.Id
using (var ctx = new TestDbContext())
{
var curretFile = ctx.Fileuploads.FirstOrDefault(x => x.Id == model.Id);
if (curretFile==null)
{
curretFile=new FileUploadModel { Name=model.Name , ValidFromDate= model.ValidFromDate};
}
curretFile.History = new FileUploadHistory { InsertedDate = DateTime.Now };
ctx.Fileuploads.Add(curretFile);
ctx.SaveChanges();
}
return View("Index", model);
}
}
-- These are MY EntityFramework entities and I am using same on Views as well
public class FileUploadModel
{
public FileUploadModel()
{
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string ValidFromDate { get; set; }
public int HistoryId { get; set; }
[ForeignKeyAttribute("HistoryId")]
public virtual FileUploadHistory History { get; set; }
}
public class FileUploadHistory
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime InsertedDate { get; set; }
}
-- Finaly the cshml file . The import point is to use new { enctype = "multipart/form-data" } inside BeginForm . // the page from where you will post the data . Please change you model class in place of FileUploadModel I created for me .
#model WebApplication1.Models.FileUploadModel
#using (Html.BeginForm("PostForm", "FileUploadDemo", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="panel">
<div class="panel-body">
<div class="form-group row">
<div class="col-md-2 form-label">
<label>ID:</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.Id , new { #class = "form-control" })
</div>
</div>
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Name:</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.Name, new { #class = "form-control" })
</div>
</div>
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Date</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.ValidFromDate, new { #class = "form-control" })
</div>
</div>
<div class="col-md-10">
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Select File<i class="required-field">*</i>:</label>
</div>
<div class="col-md-8">
<input type="file" class="file-upload" style="margin: 0px;" hidden="hidden" accept=".xlsx" name="file" id="file" />
</div>
</div>
</div>
<div class="form-group row">
<div class="col-md-3 pull-right text-right">
<button class="btn btn-primary" id="process-submission" type="submit">
Submit
</button>
</div>
</div>
</div>
</div>
}

Resources