Re-render Blazor Component - c#

I have a catalogue page that renders a Blazor component which displays a list of products with the following code:
#(await Html.RenderComponentAsync<ProductList>(RenderMode.ServerPrerendered, new { MinimumPrice = Model.MinimumPrice, MaximumPrice = Model.MaximumPrice }))
Now, I want to change the values of MinimumPrice and MaximumPrice from the catalogue page and have the blazor component re-rendered after the values have changed.
How can I do that from the catalogue page?
Thanks.

You want to change the razor view and then update the blazor component?
You can submit a form to change the Model.MinimumPrice, because you can't change the Model
in view, it's from server side, so you should change it in server side too.
This is the demo:
blazor component:
<p>Min:#MinimumPrice</p>
#code {
[Parameter]
public int MinimumPrice { get; set; }
[Parameter]
public int MaximumPrice { get; set; }
}
Razor view:
<form method="post">
Change min to<input type="text" name="value"/>
<input type="submit" value="ok" class="btn btn-primary" />
</form>
<component type="typeof(S42111.Components.ProductList)" render-mode="Static" param-MinimumPrice="Model.MinimumPrice" param-MaximumPrice="10" />
server side:
[BindProperty]
public int MinimumPrice { get; set; }
public void OnGet()
{
MinimumPrice = 1;
}
public async Task<IActionResult> OnPostAsync(int value)
{
this.MinimumPrice = value;
return Page();
}
Result:

Related

Blazor C#: Pass parameter from child component to parent component

In a blazor C# app, I have a page called TopicVideo.razor with the following code:
<div id="video-input">
<input #bind="YouTubeVideoUrl" type="text" placeholder="Video URL" style="max-height: 30px;">
<button class="summarizeBtn" #onclick="InitiateYouTubeDownload" tabindex="5">OK</button>
</div>
</div>
#code {
private async Task InitiateYouTubeDownload(){
// process
}
I wanted to create a new component called OptionOne.razor, that replaces the div with id "video-input" in the page TopicVideo.razor. So I created this:
<div id="video-input">
<input #bind="YouTubeVideoUrl" type="text" placeholder="Video URL">
<button #onclick="InitiateYouTubeDownload" tabindex="5">OK</button>
</div>
#code {
[Parameter] public string? YouTubeVideoUrl { get; set; }
[Parameter] public EventCallback<MouseEventArgs> InitiateYouTubeDownload { get; set; }
}
and in TopicVideo, I replaced the div with id "video-input" with the following:
<OptionOne YouTubeVideoUrl="YouTubeVideoUrl" InitiateYouTubeDownload="InitiateYouTubeDownload"></OptionOne>
Issue is: It seems the OptionOne component is not passing YouTubeVideoUrl correctly. When I use the VS Code Debugger, hovering over YouTubeVideoUrl in the OptionOne page shows the correct URL, but hovering over it in the TopicVideo.razor page shows "null". How do I pass YouTubeVideoUrl from the child component OptionOne to the parent component TopicVideo?
Since you already have an event registered, you can just use that to pass the URL of the video back to the parent. You could make the following changes:
OptionOne.razor:
<div id="video-input">
<input #bind="this.YouTubeVideoUrl" type="text" placeholder="Video URL">
<button #onclick="this.StartYouTubeDownload" tabindex="5">OK</button>
</div>
#code {
[Parameter] public string? YouTubeVideoUrl { get; set; }
[Parameter] public EventCallback<string> InitiateYouTubeDownload { get; set; }
private async Task StartYouTubeDownload()
{
if (this.InitiateYouTubeDownload.HasDelegate)
{
await this.InitiateYouTubeDownload.InvokeAsync(this.YouTubeVideoUrl);
}
}
}
TopicVideo.razor:
<OptionOne YouTubeVideoUrl="#this.YouTubeVideoUrl" InitiateYouTubeDownload="this.InitiateYouTubeDownload"></OptionOne>
#code {
private string YouTubeVideoUrl;
private void InitiateYouTubeDownload(string url)
{
this.YouTubeVideoUrl = url;
}
}

Selected item in ASP.NET Core razor not being passed

I'm using ASP.NET Core Razor for the first time. I'm trying to simply set a bound property with the selected value of a dropdown but the property is always null when the form is posted. Also, how does one get the form posted when a selection is made? TIA
Razor page:
<td>
<form method="post">
<select asp-for="#Model.selectedReport" asp-items="#Model.Reports" class="form-control">
</select>
</form>
</td>
Code behind:
public class SubscribedReportsModel : PageModel
{
[BindProperty]
public List<SelectListItem> Workspaces { get; private set; }
[BindProperty]
public List<SelectListItem> Reports { get; private set; }
[BindProperty]
public string selectedReport { get; set; }
public async Task OnGetAsync()
{
await GetWorkspaces();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (selectedReport != null)
{
return Page();
}
return RedirectToPage("./Index");
}
}
I'm a bit rusty it appears. All I had to do was add onchange="this.form.submit()" to the control:
<td>
<form method="post">
<select asp-for="#Model.selectedReport" asp-items="#Model.Reports" class="form-control" onchange="this.form.submit()">
</select>
#*#Html.DropDownList("ddl_Reports", Model.Reports, new {#class="form-control"})*#
#*#Html.DropDownListFor(m => m.selectedReport, Model.Reports, "--Select Report--", new{#class="form-control"})*#
</form>
</td>

How do you bind a checkbox in .net core razor pages?

How do you bind a checkbox in .net core razor pages?
I'm currently having problems where the checkbox value isn't coming back when I submit the form (using post method).
Below is my code.
domain classes:
public class Restaurant
{
public int Id { get; set; }
[Required, StringLength(80)]
public string Name { get; set; }
public Meals MealsServed { get; set; }
}
public class Meals
{
public int Id { get; set; }
public bool Breakfast { get; set; }
public bool Lunch { get; set; }
public bool Dinner { get; set; }
}
from page model:
[BindProperty]
public Restaurant Restaurant{ get; set; }
public EditModel(IRestaurantData restaurantData, IHtmlHelper htmlHelper)
{
this.restaurantData = restaurantData;
this.htmlHelper = htmlHelper;
}
public IActionResult OnGet(int? restaurantId)
{
Restaurant = restaurantData.GetById(restaurantId.Value);
Restaurant.MealsServed.Breakfast = true;
return Page();
}
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
restaurantData.Update(Restaurant);
restaurantData.Commit();
TempData["Message"] = "Restaurant Saved";
return RedirectToPage("./Detail", new { restaurantId = Restaurant.Id });
}
from razor page:
<form method="post">
<input type="hidden" asp-for="Restaurant.Id" />
<div class="form-group">
<label asp-for="Restaurant.Name"></label>
<input asp-for="Restaurant.Name" class="form-control" />
<span class="text-danger" asp-validation-for="Restaurant.Name"></span>
</div>
<div class="form-group">
<input asp-for="Restaurant.MealsServed.Lunch" />
<label asp-for="Restaurant.MealsServed.Lunch"> </label>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
So, I figured out the problem. Everything was correct that I presented above.
After checking the checkbox for Lunch and saving the item, and then viewing the Restaurant item again, it would come back unchecked.
The issue was with the depth of the data that I was pulling from the database. It was only pulling the top level of data.
So, I had to change GetById method from:
public Restaurant GetById(int id)
{
return db.Restaurants.Find(id);
}
to:
public Restaurant GetById(int id)
{
return db.Restaurants.Where(r => r.Id == id).Include(r => r.MealsServed).FirstOrDefault();
}
explicitly telling it to pull the data for the object in the MealsServed property.

ASP.NET Core - Bind IEnumerable<T> to a ViewModel field on POST

I have a web application for registering teams to a competition, where each team can select a number of technologies that they will use for their project. The technologies are saved in a Label class.
I am using a view model to bind the information from the form to the action.
However, when I try to submit the form, it takes all other fields, except the list of technologies.
Label.cs
public class Label
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string ColorPalette { get; set; }
}
CreateTeamViewModel.cs
public class CreateTeamViewModel
{
[Required]
public string TeamName { get; set; }
public string ProjectName { get; set; }
public string ProjectDescription { get; set; }
[Required]
public string RepositoryLink { get; set; }
public List<Label> Labels = new List<Label>();
}
TeamsController.cs
public class TeamsController
{
private readonly ApplicationDbContext context;
public IActionResult Create()
{
ViewData["Labels"] = this.context.Labels.ToList();
return View();
}
[HttpPost]
public IActionResult Create(CreateTeamViewModel team)
{
List<Label> labels = team.Labels;
int count = labels.Count; // count = 0
return LocalRedirect("/");
}
}
Create.cshtml (the list of checkboxes)
#model Competition.Data.ViewModels.CreateTeamViewModel
#{
List<Label> labels = ViewData["Labels"] as List<Label>;
}
<form asp-action="Create">
<div class="form-check">
#for(int i = 0; i < labels.Count; i++)
{
<input asp-for="#Model.Labels[i].IsSelected" type="checkbox" />
<label asp-for="#Model.Labels[i].Name">
<span class="badge badge-#labels[i].ColorPalette">#labels[i].Name</span>
</label>
<input asp-for="#Model.Labels[i].Name" type="hidden" value="#labels[i].Name" />
<input asp-for="#Model.Labels[i].ColorPalette" type="hidden" value="#labels[i].ColorPalette" />
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
You need to bind to a list of int instead of a list of Label on your view model. Then, you'll need to use that list of selected ids to fill your list of labels on the Team entity you're persisting:
public class CreateTeamViewModel
{
[Required]
public string TeamName { get; set; }
public string ProjectName { get; set; }
public string ProjectDescription { get; set; }
[Required]
public string RepositoryLink { get; set; }
public List<int> SelectedLabels { get; set; } = new List<int>();
}
Then, you'll need to modify your form to bind your checkboxes to this list:
#foreach (var label in labels)
{
<input asp-for="SelectedLabels" id="Label#(label.Id)" value="#label.Id" type="checkbox" />
<label id="Label#(label.Id)">
<span class="badge badge-#label.ColorPalette">#label.Name</span>
</label>
}
Notice that I removed the hidden inputs. You should never post anything that the user should not be able to modify, as even hidden inputs can be tampered with.
After posting, server-side you'll end up with a list of label ids that were selected by the user. Simply query the associated labels out of your database and then assign that to the team you're creating:
team.Labels = await _context.Set<Label>().Where(x => model.SelectedLabels.Contains(x.Id)).ToListAsync();

MVC 4 Model Binding returns null

I'm having trouble with model binding in MVC. I have a class:
public class UserSurvey
{
public int Id { get; set; }
public virtual Survey Survey { get; set; }
}
Which is the model for a view:
#model SurveyR.Model.UserSurvey
<form id="surveyForm">
<div class="container survey">
#Html.HiddenFor(x=>x.Id)
#Html.EditorFor(x => x.Survey.Steps)
</div>
<input type="button" value="Submit" id="btnSubmit"/>
</form>
And then for the submit the controller takes a class:
public class SurveyResponseViewModel
{
public int Id { get; set; }
public Survey Survey { get; set; }
}
[HttpPost]
public ActionResult Submit(SurveyResponseViewModel surveyResponse)
{
...
}
When I debug the submit the surveyResponse.Survey object is populated as it should be but the surveyResponse.Id value is 0 when it should be 1.
I can see the Id=1 being passed back in the submit but the model binding doesn't seem to hook it up.
Any help would be greatly appreciated!
Kev
EDIT: The rendered html looks like this:
<form id="surveyForm">
<div class="container survey">
<input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="1" />
So yes the value appears there and is also passed in the submit if I look using dev tools.
EDIT 2: The Form data in dev tools definitely contains "Id:1".
Your Code seems to be fine.Try passing the id value explicitly as another parameter like below
[HttpPost]
public ActionResult Submit(SurveyResponseViewModel surveyResponse , int Id )
{
surveyResponse.Id = Id
}
I have tested. Its working fine.
public ActionResult test1()
{
var model = new UserSurvey();
model.Id = 10;
return View(model);
}
[HttpPost]
public ActionResult test1(SurveyResponseViewModel surveyResponse)
{
var x = surveyResponse.Id; // returns 10
return View(new UserSurvey());
}
public class SurveyResponseViewModel
{
public int Id { get; set; }
public Survey Survey { get; set; }
}
public class UserSurvey
{
public int Id { get; set; }
public virtual Survey Survey { get; set; }
}
public class Survey
{
public string Steps { get; set; }
}
#model TestWeb.Controllers.UserSurvey
#using (Html.BeginForm())
{
<div class="container survey">
#Html.HiddenFor(x=>x.Id)
#Html.EditorFor(x => x.Survey.Steps)
</div>
<input type="submit" value="Submit" id="btnSubmit"/>
}

Categories