In Blazor handle Blazor component InputNumber property readonly via code - input

<InputNumber readonly
id="ProductShares"
class="form-control"
placeholder="Product Shares"
oninput="#Calculation"
#bind-Value="product.ProductShares" />
I want to update InputNumber to readonly in code. That is on event oninput I am calling Calculation method and I want to programmatically update readonly property.
public async void Calculation(ChangeEventArgs e)
{
// Something like
if(condition)
{
ProductShares.readonly = true; // ProductShares is the id of the InputNumber
}
else
{
ProductShares.readonly = false;
}
}

You can set bool value to readonly attribute like this
This is your html
<InputNumber readonly="#ReadOnly"
id="ProductShares"
class="form-control"
placeholder="Product Shares"
oninput="#Calculation"
#bind-Value="product.ProductShares" />
This is your code behind
#code{
private bool ReadOnly=true;
}

Related

Create dynamically form with field dependencies in Blazor

I'm creating a library for generating form in Blazor. The logic is quite straightforward: based on a JSON, the library creates the form, using my custom component. Here a piece of the code of the Razor component called SurveyGenerator
<EditForm EditContext="#editContext" OnValidSubmit="#ValidFormSubmitted">
<table class="table">
<tbody>
#foreach (var element in Form.Elements)
{
<tr>
<td>
#switch (element.Type)
{
case "Textbox":
var el = element as PSC.Survey.Shared.Textbox;
if (!ElementValues.ContainsKey(el.Name))
ElementValues.Add(el.Name, el.Text);
<Textbox Name="#el.Name"
Placeholder="#el.PlaceHolder"
Value="#el.Text"
Title="#el.Title"
Description="#el.Description"
IsRequired="#el.IsRequired"
IsVisible="#el.IsVisible"
/>
break;
default:
<p>Unknow control</p>
break;
}
</td>
</tr>
}
<tr>
<td colspan="2">
<button class="btn btn-primary"
#onclick="Submit">Submit</button>
</td>
</tr>
</tbody>
</table>
</EditForm>
#code {
internal static readonly Dictionary<string, object> ElementValues =
new Dictionary<string, object>();
private EditContext? editContext;
}
The component Textbox is a simple Razor component
#if(IsVisible) {
<input type="text" name="#Name" placeholder="#PlaceHolder"
#bind-value="#Value" class="form-control #CssInternal #CssClass">
}
#code {
[CascadingParameter]
private EditContext EditContext { get; set; }
private bool IsVisible;
[Parameter]
public string Value
{
get { return _value; }
set
{
if (_value != value)
{
_value = value;
SurveyGenerator.ElementValues[Name] = Value;
}
}
}
private string _value;
protected override Task OnInitializedAsync()
{
if (EditContext != null)
{
EditContext.OnFieldChanged += FieldChanged;
EditContext.OnValidationRequested += ValidationRequested;
}
return Task.CompletedTask;
}
private void FieldChanged(object sender, FieldChangedEventArgs e)
=> this.Validate(e.FieldIdentifier.FieldName);
private void ValidationRequested(object sender,
ValidationRequestedEventArgs e)
=> this.Validate();
private void Validate(string fieldname = null)
{
// validation code
}
}
So, when a component changes the property Value, it immediately adds the value also in the variable ElementValues in the SurveyGenerator. When the user presses the submit button, the validation starts for all the components and it works.
Now, the problem I'm facing is that something some components have to be displayed only if another field is selected or has a particular value. Unfortunately, the EditForm doesn't pass this information to the components. I though the event OnFieldChanged should raise every time there is a change in any component but it is not work in this way.
For this reason, I'm looking a way to notify all the components that a value changed, like an observable collection. When the component receives the notification, it can check if it has to display itself or not, using the IsVisible property.
Do you have any ideas or suggestions or code to share with me?

How can I pass a variable from one razor page to another?

I am making a web app using razor pages and I need to pass a string variable that's in my signup.cshtml to signin.cshtml. How would I do this?
If you want to pass a string variable from signup.cshtml to signin.cshtml.cs.Here is a demo:
signup.cshtml:
<form asp-page="signin" asp-page-handler="Test" method="post">
<input name="TestString" />
<input type="submit" value="submit"/>
</form>
signin.cshtml.cs:
public void OnPostTest(string TestString)
{
}
result:
If you want to pass a string variable from signup.cshtml handler to signin.cshtml handler.Here is a demo:
signup.cshtml.cs:
public IActionResult OnGet()
{
var TestString= "test";
return RedirectToPage("signin","Test", new { TestString = TestString });
}
signin.cshtml.cs:
public void OnGetTest(string TestString)
{
}
result:

how can I refer to a component created via DynamicComponent in Blazor?

I'm rendering components using DinamicComponent and I need to call a function found in the child component.
I can't find the equivalent of using #ref for the DinamicComponents so that I can reference to call the function.
This is the parent component
<div class="tab-content">
#foreach (VerticalTabComponent.TabModel oneTabItem in VerticalTabsList)
{
<div class="tab-pane fade show #(oneTabItem.TabIndex == SelectedTabIndex ? "active" : "")" #key=#($"VTabDivDynamic_{TabPrefix}_{oneTabItem.TabIndex.ToString()}")>
<DynamicComponent
Type=#System.Type.GetType(oneTabItem.TabComponent)
Parameters=#oneTabItem.TabParameters>
</DynamicComponent>
</div>
}
</div>
This is the code in Blazor Component Tab
public partial class TabComponent
{
[Parameter]
public EventCallback<string> InsertUpdateCallback { get; set; }
protected override async Task OnInitializedAsync()
{
await CallAnyfunctionAsync();
}
private async Task<bool> LoadDataGrid()
{
//this is the function I need to call from parent
}
}
How can I call the Load Grid function from the parent component?
There is an easy solution. Not sure if that is new but the ref-attribut does exist for the DynamicComponent! You can use it like this:
<DynamicComponent Type="typeof(MyComponent)" Parameters="#MyParameters" #ref="dc" />
and in Code-Behind:
private DynamicComponent? dc;
private MyComponent? MyComponentRef
{
get
{
return (MyComponent?)dc?.Instance;
}
}
Normally in Blazor we use #Ref to get a reference to a component, but as you've seen this won't work with a DynamicComponent.
A workaround for this would be to add a [Parameter] to the component called something like Register which is an action with the generic type set as the component type. You can then add code to handle OnParametersSet to call this method in the component.
You can then add a Register parameter in your TabParameters which gets updated with a reference.
Example code below would be added to the SurveyPrompt component:
/// <summary>
/// will be called when params set
/// </summary>
[Parameter] public Action<SurveyPrompt> Register { get; set; }
protected override void OnParametersSet()
{
if (Register != null)
{
// register this component
Register(this);
}
}
You add a Register parameter with an Action<type> value. Here's an example:
SurveyPrompt sp1 = null;
void Register1(SurveyPrompt survey)
{
sp1 = survey;
Console.WriteLine("SP1 has title " + sp1.Title);
}
protected override void OnInitialized()
{
Action<SurveyPrompt> p1 = Register1;
params1 = new Dictionary<string, object>()
{
{ "Title", "Survey Title Here" },
{ "Register", p1 }
};
}
IDictionary<string, object> params1;

Asp.Net Razor Pages OnClick not firing

I have this Button in my Index.cshmtl
<button value="Search" name="Search" submit="Search" class="searchbutton" onclick="btnSearch_Click">Search</button>
this Button sends a post when clicked.
In my Index.cshtml.cs I have this boolean variable and function
private bool SearchButton;
protected void btnSearch_Click(object sender, EventArgs e)
{
SearchButton = true;
}
So my problem is that, if I click the Button the code will never enter the btnSearch_Click function
You can use a named handler method in Razor Pages to emulate a server-side event handler in Web Forms.
Here's a content page:
#page
#model RazorPagesDemo.Pages.HandlerDemoModel
#{
}
<p>#Model.Message</p>
<form asp-page-handler="Search" method="post">
<input type="text" asp-for="SearchTerm" />
<button class="btn btn-default">Search</button>
</form>
And here's the PageModel:
public class HandlerDemoModel : PageModel
{
[BindProperty]
public string SearchTerm { get; set; }
public string Message { get; set; }
public void OnPostSearch()
{
Message = $"You searched for {SearchTerm}";
}
}
OnPostSearch is the equivalent to your btnSearch_Click method. You use the asp-page-handler attribute to wire your form up to execute that method. You can read more about Razor Pages handler methods here: https://www.learnrazorpages.com/razor-pages/handler-methods

How to bind input of type time with blazor

Hello i have 2 variables of type int that i would like to bind to the min and max values of an input of type time.
How can i do this?
I do not know what to place in the bind field since there are 2 different variables.
Also there is the min and max attributes.
<input type="time" min="#model.min" max="#model.max" bind=?/>
What should i put in the bind ?
Update
On a more thoroughly analysis i decided i will need 2 variables of type Timespan and i will bind these to 2 inputs of type time.
Previous solution was not working for me with .net Core 3.1 so I'll add an updated one:
Use Blazor :
<EditForm Model=#model OnValidSubmit="Submit">
<InputText type="time" #bind-Value="TimeProxy" />
</EditForm>
Code changes are necessary as well.
#code {
// This field is required as you can not use property in out statement
private TimeSpan LocalTime = TimeSpan.FromHours(0);
private string TimeProxy {
get => model.Time.ToString();
set => TimeSpan.TryParse(value,out LocalTime);
}
private void Submit() {
model.Time = LocalTime;
// following submit logic...
}
}
You cannot bind a TimeSpan directly to an input in Blazor, but you can use a property to convert it to/from a string.
<input type="time" min="#model.min" max="#model.max" bind="#TimeProxy" />
and
#functions
{
string TimeProxy { get => model.Time.ToString(); set => TimeSpan.TryParse(value,out model.Time); }
}
.NET 6.0.8 now has nice functionality for this in InputDate, that supports the TimeOnly type (among others). I found out as i was studying the .NET source code and about to create my own (due to ccStars' answer)
<InputDate Type="InputDateType.Time" #bind-Value="#model.TimeVar" />
Where TimeVar has type TimeOnly?. It's nice that it handles nullable types.
You can also do the following:
<input type="time" #bind="SomeTime" #bind:format="HH:mm"/>
#code {
public DateTime SomeTime = new DateTime();
private TimeSpan _time = SomeTime.TimeOfDay;
}
Please not this doesn't bind to the TimeSpan directly!
I wrote a little component for this which utilizes databinding and works with the proper data types.
usage:
<LabeledTime #bind-Value="shutdownDelay"></LabeledTime>
component:
<label class="form-label" for="#LabelId">
#ChildContent
</label>
<input id="#LabelId" type="time" value="#ValueInternal.ToString("hh\\:mm")" step="60" #onchange="InternalValueChanged"/>
#code {
private long LabelId = DateTime.Now.Ticks;
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public TimeSpan Value { get; set; }
[Parameter]
public EventCallback<TimeSpan> ValueChanged { get; set; }
private TimeSpan ValueInternal { get; set; }
protected override void OnParametersSet()
{
ValueInternal = Value;
base.OnParametersSet();
}
private void InternalValueChanged(ChangeEventArgs obj)
{
if (!TimeSpan.TryParseExact(obj.Value.ToString(), "hh\\:mm\\:ss", null, out var result))
return;
ValueInternal = result;
Value = result;
ValueChanged.InvokeAsync(result);
}
}
Hopefully this helps, I was also trying to bind a Timespan a input of type="time" I managed to achieve it with the help from the following site
https://www.meziantou.net/creating-a-inputselect-component-for-enumerations-in-blazor.htm
This is the code I created to achieve this
InputTime.cs
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Rendering;
using System;
using System.Globalization;
namespace Example
{
public sealed class InputTime : InputBase<TimeSpan>
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddAttribute(1, "type", "time");
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddAttribute(3, "class", CssClass);
builder.AddAttribute(4, "value", BindConverter.FormatValue(CurrentValueAsString));
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string>(this, value => CurrentValueAsString = value, CurrentValueAsString, null));
builder.CloseElement(); // close the select element
}
protected override bool TryParseValueFromString(string value, out TimeSpan result, out string validationErrorMessage)
{
validationErrorMessage = null;
return BindConverter.TryConvertTo<TimeSpan>(value, CultureInfo.CurrentCulture, out result);
}
}
}
Razor Page
<div class="form-group">
<label>Time From</label>
<InputTime #bind-Value="MyModel.TimeFrom" class="form-control"></InputTime>
<ValidationMessage For="() => MyModel.TimeFrom"></ValidationMessage>
</div>