Blazor Razor Validation Message don't display from component library - razor

I have created a library of blazor components to be able to call the components from the app but the message validation doesn't show. At the moment, the validation is done in a InputText (it validates the format or the length of the Input) but the message or the style of the component is not shown.
The code of the component library:
CustomInputText
<input value="#Value" #oninput="OnValueChanged" placeholder=#placeholderText class="form-control i-component o-my-4" />
<ValidationMessage For="#(() => model)" />
#code {
[Parameter]
public string placeholderText { get; set; }
[Parameter]
public object model { get; set; }
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
private Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
I import the component from a nuget package to be able to use it in the App
The App code:
<CustomInputText placeholderText="Place Holder Test" model="filterPayroll.IPF" #bind-Value="filterPayroll.IPF"/>
When I put the ValidationMessage directly in the app it works correctly, but not in the library. For the two cases, the validation linked to the "filterPayroll" class is done correctly, the difference is that in one the message is displayed and the other does not.

You need to pass the For for the Validation Summary as an expression.
CustomInputText needs to look like this:
<input value="#Value" #oninput="OnValueChanged" placeholder=#placeholderText class="form-control i-component o-my-4" />
<ValidationMessage For="#For" />
#code {
[Parameter]
public string placeholderText { get; set; }
[Parameter] public Expression<Func<string>>? For { get; set; }
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
private Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
And your markup:
<CustomInputText #bind-Value="filterPayRoll.IDF" For="() => filterPayRoll.IDF" />

Related

Pass Value in Nested Parameter in Blazor Component

How do i pass value in nested parameter. suppose i have a custom control called mycomponent
mycomponent.Razor
<label>
</label>
#code
{
[Parameter]
public TestBase Test { get; set; } = new TestBase();
}
TestBase Class
public class TestBase
{
[Parameter]
public string Cap { get; set; }
[Parameter]
public string Cap5 { get; set; }="hai"
[Parameter]
public string Cap2 { get; set; }
[Parameter]
public string Cap3 { get; set; }
}
MyPage
<mycomponent Test.Cap="my value">
</mycomponent>
Test.Cap="my value" is not working
What is the right way to pass value in nested Parameter
You should be passing TestBase into MyComponent, not trying to set a value of TestBase in MyComponent. Setting a default value for Test in MyComponent just covers the situation where the Parameter is not provided.
<mycomponent Test="MyTest">
</mycomponent>
#code {
private TestBase MyTest = new TestBase() {Cap = "Test Value"};
SomeButtonClick Event()
{
MyTest.Value = "Another Value";
}
}
I suggest you read up about components - This is a good start or MS Docs.
Update
straight answer to your question is: No you can't do what you're trying to do.

How to create a InputText Component

I'm trying to create a Input component that can:
Clear its value when press Escape.
On mousewheel change its value.
I'm creating a Input component cause i dont wanna put the same code in all Inputs elements and i need to administrate all from the same site. I have 6 days with dis issue. I'm reading and searching information that i could use but nothing help me at 100%.
#inherits Microsoft.AspNetCore.Components.Forms.InputBase<string>
#using Microsoft.AspNetCore.Components.Forms
#if (ReadOnly)
{
<InputText type="text"
tabindex="#TabIndex"
class="#Class"
id="#Id"
style="#Style"
maxlength="#MaxLength"
min="#Min"
max="#Max"
placeholder="#Placeholder"
readonly />
}
else
{
<InputText type="text"
maxlength="#MaxLength"
min="#Min"
max="#Max"
class="#Class"
tabindex="#TabIndex"
id="#Id"
style="#Style"
placeholder="#Placeholder"
#attributes="#AdditionalAttributes"
#bind-Value="#Value"
#bind-Value:event="oninput"
#onkeydown="#(e => { if (e.Code == "Escape") Value = Clear(); StateHasChanged(); })"
#onmousewheel="CaptureScroll"
/>
}
#code{
[Parameter]
public string Type { get; set; }
[Parameter]
public string Class { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string Id { get; set; }
[Parameter]
public string Style { get; set; }
[Parameter]
public bool ReadOnly { get; set; }
[Parameter]
public int MaxLength { get; set; }
[Parameter]
public int Min { get; set; }
[Parameter]
public int Max { get; set; }
[Parameter]
public int TabIndex { get; set; }
}
When i need to use it i just call it:
<HyperInput Type="text" Class="form-control" Id="inputName" TabIndex="1" #bind-Value="model.ModelName" />.
When i run the application trying to make this work i have this error:
Error: System.InvalidOperationException: Shared_Components.Forms.HyperInput requires a cascading parameter of type EditContext. For example, you can use Shared_Components.Forms.HyperInput inside an EditForm.
at Microsoft.AspNetCore.Components.Forms.InputBase1.SetParametersAsync(ParameterView parameters) at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange1 oldTree, ArrayRange`1 newTree)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
I tried wrap HyperInput with a EditForm and not work.
I wish that someone could help me, i need it.
EDIT:
Now, i'm trying with a input element instead InputText and without inherit InputBase. I only added:
private string _binder;
[Parameter]
public string Binder
{
get => _binder;
set => Set(value);
}
[ParameterAttribute] public EventCallback<string> BinderChanged { get; set; }
public async void Set(string value)
{
_binder = value;
await BinderChanged.InvokeAsync(value);
}
The only thing that i can do with this component is delete with Backspace.
NOTE: Instead of use #bind-Value i'm using #bind-Binder.
You can use classic DOM events, like onkeyup and onmousewheel to make your HyperInput work in your favor. I'd strongly recommend to inherent from InputText since it makes your component much more powerful. The version I post here should you get started. It is far away from being completed.
Usage by other components
To show the usage within an EditContext and the two-way binding capabilities, I created a simple model, a display element, and a reset button
<EditForm Model="_model">
<HyperInput #bind-Value="_model.Name" Options="#(new String[]{ "ABC", "DEF","TEST","SOMETHING"})" class="form-control" />
</EditForm>
<span>#_model.Name</span>\
<button #onclick="#(() => _model.Name = "My Testname")" >Reset</button>
#code
{
public class TestModel
{
public String Name { get; set; } = "My test name";
}
private TestModel _model = new TestModel();
}
HyperInput
#inherits InputText
<input type="text" #bind-value="Value" #onkeyup="ClearInput" #onmousewheel="OnMouseWheelUp" #attributes=AdditionalAttributes />
#code {
[Parameter]
public IReadOnlyList<String> Options { get; set; }
private Int32 _index = 0;
public void ClearInput(KeyboardEventArgs args)
{
if(args.Key == "Escape")
{
//CurrentValue triggers the update process
base.CurrentValue = String.Empty;
}
}
public void OnMouseWheelUp(WheelEventArgs args)
{
Int32 nextIndex;
if(args.DeltaY > 0)
{
nextIndex = _index + 1;
}
else
{
nextIndex = _index - 1;
}
SuggestInput(nextIndex);
}
private void SuggestInput(Int32 requestedIndex)
{
if(Options == null || Options.Count == 0) { return; }
if(requestedIndex < 0 && Options.Count > 0)
{
_index = 0;
CurrentValue = Options[0];
}
else if(requestedIndex < Options.Count)
{
_index = requestedIndex;
CurrentValue = Options[requestedIndex];
}
}
}
If the escape button is pressed, CurrentValue is set to an empty string and the text box is empty. CurrentValue will trigger the update process. For the mouse wheel, there is a list of options represented by the parameter Options and a local field responsible for remembering the current position in the list.
The WheelEventArgs has a property DeltaY. If you scroll up, this value is negative. If you scroll down, the value is positive.
I make a simple "limiter" logic. If you reach the boundaries of the list (up or down), nothing happens anymore. But of course, you could change it to circular buffer or anything else you have a mind.

REST API returns "bad array" instead of JSON object

I'm building REST API server in .NET core. I'm testing my code via Postman software. I have a problem with Include() method that enables me to attach navigation property data. I'm trying to get data in [HttpGet] action and objects that are being returned are wrong.
My code :
MODELS
Session model
public class Session
{
[Key]
public int IDSession { get; set; }
[Required]
public DateTime LogInTime { get; set; }
public DateTime LogOutTime { get; set; }
[Required]
public int IDUser { get; set; }
public User User { get; set; }
[Required]
public int IDMachine { get; set; }
public Machine Machine { get; set; }
}
User model
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public string AvatarPath { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public User CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public List<UserGroup> UsersGroups { get; set; }
public List<Alarm> ExecutedAlarms { get; set; }
public List<Alarm> ResetedAlarms { get; set; }
public List<AccessCard> Cards { get; set; }
public List<AccessCard> UserCardsAdded { get; set; }
public List<User> UsersAdded { get; set; }
public List<Session> Sessions { get; set; }
public List<EventsLog> Events { get; set; }
public List<Reference> References { get; set; }
public List<UserPermission> UsersPermissions { get; set; }
}
Session controller
[Produces("application/json")]
[Route("api/Sessions")]
public class SessionsController : Controller
{
private readonly DBContext _context;
#region CONSTRUCTOR
public SessionsController(DBContext context)
{
_context = context;
}
#endregion
#region HTTP GET
// GET: api/sessions
[HttpGet]
public async Task<IActionResult> GetSessions()
{
var sessions = await _context.Sessions.Include(s => s.User). ToListAsync();
if (sessions.Any())
{
return new ObjectResult(sessions);
}
else
{
return NotFound();
}
}
// GET: api/sessions/1
[HttpGet("{id}", Name = "GetSessionByID")]
public async Task<IActionResult> GetSessionByID(Int32 id)
{
var session = await _context.Sessions.Include(s => s.User).FirstOrDefaultAsync(s => s.IDSession == id);
if (session == null)
{
return NotFound();
}
else
{
return new ObjectResult(session);
}
}
#endregion
}
The idea is that User model contains List<Session> collection that he/she created. I want to be able to return users with its sessions
Of course Session model contains a single User because every session is related with a specific, single User.
Now, when I need to get all sessions objects in SessionController with GetSessions() or GetSessionsByID() I use POSTMAN [HttpGet] action like this : http://localhost:8080/api/sessions which returns me wrong data :
A session contains a user and in turn a single user is related with its sessions. It looks like it tries to return me Session object properly, includes User object but then it tries to include all sessions for that User. That's not what I want. It looks like some kind of a loop. Sessions shoud be returned with its User objects and that's it. How can I achieve that ? Am I doing some logical mistake in my models ?
Thanks !
I met also this issue recently. So, I've fixed it by adding this script in the Startup.cs file and ConfigureServices method :
services.AddMvc().AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
So, you suffix services.AddMvc() by this code who means that you have to make JSON.Net to ignore cycles finded to the nested object request. And of course having Newtonsoft.Json package installed to your project and referenced in each concerned file
For much clearer information, see this link at Related Data and Serialization section :
https://learn.microsoft.com/en-us/ef/core/querying/related-data
Hope this is helpfull for you

Populate DropDown from database in an edit view using MVC4

I am new to MVC and trying to populate a dropdown list in the "create" view which is generated from a view model, but it returns with an error saying object reference is not an instance of an object. below is my code :
Controller Code:
public ActionResult Create()
{
return View(new AddRecipeViewModel());
}
Model Code:
public class DifficultyLevel
{
[Key]
public int Id { get; set; }
public string Difficulty { get; set; }
}
public class AddRecipeViewModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipeReview> Reviews { get; set; }
public virtual IEnumerable<DifficultyLevel> Difficulty { get; set; }
}
View:
<div>
<select>
#foreach (var item in Model.Difficulty)
{
<option>#item.Difficulty</option>
}
</select>
</div>
Is there an easy workaround this ? as I will be adding more drop downs in this as I go along.
Thanks,
Vishal
not sure if you need to use virtual in your view models.. that's usually only for the entity models. but anyway, try adding a constructor to AddRecipeViewModel and set the collections equal to empty lists so they won't be null.
public class AddRecipeViewModel
{
public AddRecipeViewModel()
{
Reviews = new List<RecipeReview>();
Difficulty = new List<DifficultyLevel>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipeReview> Reviews { get; set; }
public virtual IEnumerable<DifficultyLevel> Difficulty { get; set; }
}

MVC Radio Button List Grid

I've got an issue trying to read the selected values from the radio button list in my [HttpPost] Controller. Any help would be greatly appreciated.
My models are the following:
public partial class RoleModel
{
public Guid RoleId { get; set; }
public string Description { get; set; }
public List<RoleModuleAccessRight> RoleModuleAccessRights { get; set; }
}
public class RoleModuleAccessRight
{
public string ModuleName { get; set; }
public int ModuleId { get; set; }
public bool HasFullControl { get; set; }
public bool HasReadOnly { get; set; }
public bool HasNoAccess { get; set; }
}
My Controllers:
[HttpGet]
public ActionResult Edit(Guid id)
{
RoleModel role = BusinessLayer.UserManager.GetRoleModel(id);
role.RoleModuleAccessRights = BusinessLayer.UserManager.GetModulesForRoleId(role.RoleId);
return PartialView("_Edit", role);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(RoleModel model)
{
if (ModelState.IsValid)
{
BusinessLayer.UserManager.UpdateAccessRights(model);
string url = Url.Action("List", "Roles");
return Json(new { success = true, url = url });
}
return PartialView("_Edit", model);
}
My View code:
#foreach (RoleModuleAccessRight item in Model.RoleModuleAccessRights)
{
#Html.HiddenFor(model => item.ModuleId)
string radioName = string.Format("RoleModuleAccessRights_{0}", item.ModuleId.ToString());
<tr>
<td>#item.ModuleName</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasFullControl, item.HasFullControl)
</div>
</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasReadOnly, item.HasReadOnly)
</div>
</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasNoAccess, item.HasNoAccess)
</div>
</td>
</tr>
}
The issue im having is that when i post the form im not able to grab the information in my [HttpPost] Controller. It returns "null"
The mark up generated is the following:
Looking at your code, your ids are not unique which is going to break things, also using a dedicated template will simplify your problem. See this example https://stackoverflow.com/a/7668325
Another more generic article: Multiple radio button groups in MVC 4 Razor