Strange popup behavior - asp.net-core

I have a modal popup controller that I dynamically render on the page defined as follows
<MatDialog #bind-IsOpen="#Open">
<MatDialogTitle>#Title</MatDialogTitle>
<MatDialogContent>
#Content
</MatDialogContent>
<MatDialogActions>
<MatButton OnClick="#Yes">Yes</MatButton>
<MatButton OnClick="#(e => { Open = false; })">No</MatButton>
</MatDialogActions>
</MatDialog>
#code {
[Parameter] public bool Open { get; set; }
[Parameter] public string Title { get; set; }
[Parameter] public string Content { get; set; }
[Parameter] public EventCallback<string> YesCallback { get; set; }
void Yes()
{
Open = false;
YesCallback.InvokeAsync("deleted");
}
}
It just meant to close the popup and call the passed in callback function. Issue is if YesCallback.InvokeAsync("deleted"); is called dialog never closes. The callback gets executed but the dialog stays open. If YesCallback.InvokeAsync("deleted"); is removed the dialog closes fine, but, obviously no callback is called.
There are no errors.

Related

Localizing data annotations in .razor pages [duplicate]

Asp.net core server side localization is well documented and working for me. But how do you localize DataAnnotations on DTO models on the client side of Blazor webassembly?
On server side I've added the code below and DataAnnotations are localized. Everything is working as expected.
...
services
.AddRazorPages() .AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization(
options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
return factory.Create(typeof(CommonStrings));
};
});
...
But how do I do the same thing on Blazor client side (webassembly)?
For example I have this model which is on client side:
public class ApplicationUserDTO
{
public string Id { get; set; }
[Required(ErrorMessage ="Field {0} is required")]
[Display(Name ="First name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last name")]
public string LastName { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
}
I want to post it to backend via <EditForm> component, and before I do that do the validation on client side.
I also want to localize it like i would on aspnet.core server - Error/validation messages and display names...
I tried with LocalizedValidator component:
public class MessageValidatorBase<TValue> : ComponentBase, IDisposable
{
private FieldIdentifier _fieldIdentifier;
private EventHandler<ValidationStateChangedEventArgs> _stateChangedHandler
=> (sender, args) => StateHasChanged();
[CascadingParameter]
private EditContext EditContext { get; set; }
[Parameter]
public Expression<Func<TValue>> For { get; set; }
[Parameter]
public string Class { get; set; }
protected IEnumerable<string> ValidationMessages =>
EditContext.GetValidationMessages(_fieldIdentifier);
protected override void OnInitialized()
{
_fieldIdentifier = FieldIdentifier.Create(For);
EditContext.OnValidationStateChanged += _stateChangedHandler;
}
public void Dispose()
{
EditContext.OnValidationStateChanged -= _stateChangedHandler;
}
}
and then created component:
#typeparam TValue
#inherits MessageValidatorBase<TValue>
#inject StringLocalizationService _localizer
#foreach (var message in ValidationMessages)
{
<div class="#Class">
#_localizer[message]
</div>
}
but the problem is I get already expanded string here. For example if I have error message like this "The field {0} is required" I get "The field First name is required" which will not be localized since I don't have the resource with that key and I don't intend to translate the same error message for every property name...
[EDIT]
I just want to know if there is something trivial I didn't do instead of implementing it completely on my own
WebAssembly example.
Example property
[MaxLength(5, ErrorMessageResourceName = "LengthError", ErrorMessageResourceType = typeof(Resources.App))]
public string Prefix { get; set; }
Create a folder in your client called Resources.
Add a `.resx' file for each language plus a default (no language).
Make sure your set the access Modifier to Public
Example output in French.

devexpress blazor popup component not opening after closing

I created a popup component and added it to my page. but after turning it on and off it won't turn back on. I couldn't understand this problem
Component
<DxPopup
#bind-Visible="#WarningPopupVisible"
ShowFooter="true"
HeaderText="Warning"
BodyText="Content content content content"
>
<FooterContentTemplate>
<DxButton CssClass="popup-button" RenderStyle="ButtonRenderStyle.Primary" Text="Tamam" Click="#(() => WarningPopupVisible = false)"/>
</FooterContentTemplate>
</DxPopup>
#code {
[Parameter]
public bool WarningPopupVisible { get; set; }
}
/productspage
<Warning WarningPopupVisible="WarningPopupVisible"/>
<DxButton Text="Open Modal" CssClass="w-25" Click="#(() => WarningPopupVisible = true)" />
#code {
public bool WarningPopupVisible { get; set; }
}
it looks like you try to open an Popup from another class?
u need to reference to "WarningPopupVisible" dont initialize
"WarningPopupVisible" in /productpage again just use something like popup.WarningPopUpVisible = false;
and set the [Parameter] bool like this:
[Parameter]
public bool WarningPopupVisible { get; set; } = false;

Can child content be added to MarkupString with RenderFragment?

I have code like this;
#((MarkupString)$"<{item.Tagname}> {ChildContent} </{item.TagName}>")
ChildContent in the middle is a RenderFragment object, but it outputs as follows;
"Microsoft.AspNetCore.Components.RenderFragment"
Is there a way to make it work normally this way?
I'm guessing you're trying to do something like this.
The example uses a RenderTreeBuilder to construct the markup. It's the eqivalent to writing assembler for components, but as the markup gets complex it's the only sensible way!
If you use this method one word of advice: the sequence numbers need to be coded manually, do not use an iterator.
#this.content
#code {
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public string TagName { get; set; } = "div";
private RenderFragment content => builder =>
{
builder.OpenElement(0, TagName);
builder.AddContent(1, ChildContent);
builder.CloseComponent();
};
}
Or as a complete component:
public class ComponentClass : ComponentBase
{
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public string TagName { get; set; } = "div";
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, TagName);
builder.AddContent(1, ChildContent);
builder.CloseComponent();
}
}

How to set input box value to a string in Blazor WebAssembly?

I am using Blazor WebAssmebly. I have an input box and I simply want to reset it to no text after the user types in text and hits the Enter key:
<input id="txtWord" name="txtWord" placeholder="Enter your text" #onchange="#onChange" #onkeyup="#Enter" />
private void onChange(Microsoft.AspNetCore.Components.ChangeEventArgs args)
{
value = (string)args.Value;
}
public void Enter(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
if (value.Trim() != "")
{
doSomething();
}
}
}
So I set the variable 'value' to the input text, but then I want to clear the text box. How do I do that?
It looks like as if you're not binding your input to the value variable. Your code should be something like this:
<input id="txtWord" name="txtWord" placeholder="Enter your text"
value ="#value" #onchange="onChange" #onkeyup="Enter" />
#code
{
private string value;
}
Note that by adding the value attribute to the input element I create a two-way databinding, from the variable to the control, and from the control to the variable. When you use the #onchange directive, you create a one-way data binding.
In order to reset the input element, you can do the following:
if (value.Trim() != "")
{
// I guess it is here that you want to reset the input
// element. Assigning empty string to the `value` variable
// will cause the control to re-render with the new value;
// that is empty string...
value = "";
doSomething();
}
This will handle "enter" and "Submit" button press. I use this in a SignalR library I am developing. The default css classes are for Bootstrap.
SendBox.razor
<EditForm Model="#SendBoxViewModel" OnSubmit="#Send">
<div class="#DivClass">
<input #ref="#inputBox"
#bind-value="SendBoxViewModel.InputMessage"
#bind-value:event="oninput"
type="text"
aria-label="#Placeholder"
placeholder="#Placeholder"
class="#InputClass"
aria-describedby="button-send"
disabled=#Disabled>
<button class="#ButtonClass"
type="submit"
id="button-send"
disabled=#Disabled>
#Label
</button>
</div>
</EditForm>
SendBox.razor.cs
public partial class SendBox : ComponentBase
{
private ElementReference inputBox;
[Parameter]
public string Label { get; set; } = "Send";
[Parameter]
public string Placeholder { get; set; } = "Type a new message here.";
[Parameter]
public string DivClass { get; set; } = "input-group";
[Parameter]
public string InputClass { get; set; } = "form-control";
[Parameter]
public string ButtonClass { get; set; } = "btn btn-outline-primary";
[Parameter]
public bool Disabled { get; set; }
[Parameter]
public EventCallback<string> OnSend { get; set; }
public SendBoxViewModel SendBoxViewModel { get; set; } = new SendBoxViewModel();
private bool MessageInputInvalid => string.IsNullOrWhiteSpace(SendBoxViewModel.InputMessage);
private async Task Send()
{
if (!MessageInputInvalid)
{
await OnSend.InvokeAsync(SendBoxViewModel.InputMessage);
SendBoxViewModel.InputMessage = string.Empty;
await inputBox.FocusAsync();
}
}
}
SendBoxViewModel.cs
public class SendBoxViewModel
{
[MinLength(length: 1)]
[MaxLength(length: 1024)]
[Required(AllowEmptyStrings = false)]
public string? InputMessage { get; set; }
}

Blazor's InputSelect Component not updating form validations errors on it's value change

I am using Blazor's InputSelect Component on a field called LocationId.
[Range(1, int.MaxValue, ErrorMessage = "Please Select Location")]
public int LocationId { get; set; }
On my razor component, where form validations are taking place, I am calling a child component like this:
<div class="form-group">
<label>Location</label>
<ValidationMessage For="#(() => StudentData.LocationId)" />
<SelectCommon RowType="Location" RowData="Locations" #bind-MyPhrase="#StudentData.LocationId">
<SelectOption>
<option selected disabled value="0">Choose a Location</option>
</SelectOption>
<OptionValue Context="p">
<option value="#p.Id">#p.City, #p.State</option>
</OptionValue>
</SelectCommon>
</div>
In the child component there is the InputSelect component whose code is:
#typeparam RowType
<InputSelect class="form-control" #bind-Value="HandleChange">
#if (SelectOption != null)
{
#SelectOption
}
#foreach (RowType item in RowData)
{
#OptionValue(item);
}
</InputSelect>
#code {
[Parameter]
public RenderFragment SelectOption { get; set; }
[Parameter]
public RenderFragment<RowType> OptionValue { get; set; }
[Parameter]
public IEnumerable<RowType> RowData { get; set; }
[Parameter]
public int MyPhrase { get; set; }
[Parameter]
public EventCallback<int> MyPhraseChanged { get; set; }
public int HandleChange
{
get { return MyPhrase; }
set
{
MyPhrase = value;
MyPhraseChanged.InvokeAsync(MyPhrase);
}
}
}
The work of #bind-Value="HandleChange" is to create blazor chain binding thing which is working perfectly. The parent component has this attribute #bind-MyPhrase="#StudentData.LocationId" which send the value of the model to the child for binding.
The problem is happening when i change the value of the select but the validation messages are not updated. However when I click the button which submits the form the validation messages updates. You can see the below gif which is showning this thing.
I have also notices that If id do not go with the chain binding approach and keep my InputSelect directly inside the EditForm component then this problem does not happen. It happens only in the parent-child way of coding (chain binding).
How can i correct this things?
Thanks to #dani-herrera
ApplicationUserDropDown.razor (child component):
<TelerikComboBox id="userbox" #bind-Value="#Value"
Data="#Data" TItem="UserListVM"
TValue="Guid?" Placeholder="Select User"
ValueField="Id" TextField="FullName">
ApplicationUserDropDown.razor.cs (child component):
public partial class ApplicationUserDropDown
{
#region Value two way binding
[CascadingParameter] EditContext EditContext { get; set; } = default!;
[Parameter] public Expression<Func<Guid?>> ValueExpression { get; set; }
private Guid? _valueId;
[Parameter]
public Guid? Value
{
get => _valueId;
set
{
if (_valueId == value) return;
_valueId = value;
ValueChanged.InvokeAsync(value);
var fieldIdentifier = FieldIdentifier.Create(ValueExpression);
EditContext.NotifyFieldChanged(fieldIdentifier);
}
}
[Parameter]
public EventCallback<Guid?> ValueChanged { get; set; }
#endregion Value two way binding
razor page using child component:
<ApplicationUserDropDown #bind-Value="Account.OwnerId"></ApplicationUserDropDown>
<ValidationMessage For="#(() => Account.OwnerId)">Owner is Required</ValidationMessage>