I have a blazor page with some input boxes and a button to submit the data. There is then code to validate and upload the data to a database after passing validation.
#page "/register"
<h2>
Create account
</h2>
<EditForm Model="_model" OnSubmit="#Submit">
<div>
<div class="form-group">
<label>Display name</label>
<InputText #bind-Value="_handle" class="form-control" />
</div>
<div class="form-group">
<label>Email</label>
<InputText #bind-Value="_email" type="email" class="form-control" />
</div>
<div class="form-group">
<label>Password</label>
<InputText #bind-Value="_password" type="password" class="form-control" />
</div>
<div class="form-group">
<label>Re-enter password</label>
<InputText #bind-Value="_repeatPassword" type="password" class="form-control" />
</div>
<div class="d-grid">
<button type="button" class="btn btn-primary" #onclick="Submit">Create your account</button>
</div>
</div>
</EditForm>
<div>
Sign in
</div>
#code {
private object _model = new object();
private string _firstname;
private string _lastname;
private string _handle;
private string _email;
private string _password;
private string _repeatPassword;
// -- validate inputs -- //
// -- Add to Database -- //
var user = new User
{
Id = Guid.NewGuid(),
Username = _email.ToLowerInvariant(),
Handle = _handle,
Password = password,
PasswordSalt = passwordSalt,
Firstname = _firstname,
Lastname = _lastname,
CreatedOn = DateTime.UtcNow,
};
context.Users.Add(user);
await context.SaveChangesAsync(default);
}
Recently I decided to set up unit testing for my razor pages and I am currently stuck on how to verify that a user is actually being created with the given data. I will say I know that my code is working because I can see the correct data being added to the database but I don't want to test potential changes in the future by looking at the database. I would like to be able to test it by the unit test. Here is what I have so far, I think I am close but not quite sure how to fill the input boxes and then hit submit to see the user is created. Is this possible? My unit test code is below.
namespace ZachUnitTests.ZachUnitTests.Pages;
public class RegisterTests
{
[Test]
public void VerifyUserData()
{
// Arrange
using var ctx = new Bunit.TestContext();
// Act
var cut = ctx.RenderComponent<Register>();
var inputs = cut.FindAll(".form-group");
var input1 = inputs.Where(x => x.ParentElement.TextContent.Contains("Display name")).FirstOrDefault();
input1.Input("Zachrules123"); //doesn't work because there is no #oninput .. How do I fill this box?
var input2 = inputs.Where(x => x.ParentElement.TextContent.Contains("Email")).FirstOrDefault();
input2.Input("Zachrules123#gmail.com"); //doesn't work because there is no #oninput .. How do I fill this box?
var input3 = inputs.Where(x => x.ParentElement.TextContent.Contains("Password")).FirstOrDefault();
input3.Input("password"); //doesn't work because there is no #oninput .. How do I fill this box?
var input4 = inputs.Where(x => x.ParentElement.TextContent.Contains("Re-enter password")).FirstOrDefault();
input4.Input("password"); //doesn't work because there is no #oninput .. How do I fill this box?
cut.Find("button").Click();
//Assert
//User is created
}
}
The error message from bUnit tells you what other event handler are actually bound to the object. You can also check cut.Markup and see what event handler are bound. But it is likely onchange, so use the Change() method instead of Input().
Related
My project has a few tiers here. Customer -> Project
The Project Table has a value for the CustomerID.
I'm getting a list of CustomerNames using my CustomersModel.
Simple example of what I'm trying to do here. Choose a customer from a list and then type in a new Project Name and submit that to a table. Binding the CustomerID causes the "Chose a Customer" option to not show up in the select drop down. If I put CustomerName is the bound value it will show as the placeholder for the drop down.
But I need to pass the CustomerID to the Projects Table with the new Project Name.
I'm not sure what I'm missing here.
--
Component Code
<EditForm Model="#newProject" OnValidSubmit="#InsertLocation">
<DataAnnotationsValidator />
<div class="form-group">
<div class="row">
<div class="col-md-5">
<label class="form-label">Customer Name</label>
<InputSelect class="form-control" id="CustomerName" #bind-Value="newProject.CustomerID">
#if (customer is null)
{
}
else
{
<option selected disabled value="-1"> Choose a Customer</option>
#foreach (var customer in customers)
{
<option>#customer.CustomerName</option>
}
}
</InputSelect>
#*<ValidationMessage For="(() => newProject.CustomerID)" />*#
</div>
<div class="col-md-5">
<label class="form-label">Location Name</label>
<InputText class="form-control" id="ProjectName" #bind-Value="newProject.ProjectName" placeholder="Location Name" />
<ValidationMessage For="(() => newProject.ProjectName)" />
</div>
</div>
#code from the component
private ProjectsViewModel newProject = new ProjectsViewModel();
private async Task InsertLocation()
{
//assign the new project data to the project model in the back end
ProjectsModel project = new ProjectsModel
{
//TODO: make these match what is in the model and update insert query
ProjectName = newProject.ProjectName,
CustomerID = newProject.CustomerID
};
await projectsData.InsertLocation(project); //send data to db
newProject = new ProjectsViewModel();
projects = await projectsData.ReadProjects();
OpenAddLocationModal = false;
}
And finally sending to the stored procedure from the projectData file.
public async Task InsertLocation(IProjectsModel project)
{
var p = new
{
project.CustomerID,
project.ProjectName
};
await _db.SaveData("dbo.spProject_Create", p, "DefaultConnection");
}
SQL Procedure
#ProjectName nvarchar(255),
#CustomerID int
as
begin
insert into dbo.Projects(CustomerID, ProjectName)
values (#CustomerID, #ProjectName);
end
Thanks for any help.
I am using Blazor framework with ASP.NET Core 3.0.
I have a Input Box and I added a Label HTML next to it. When user enters, say 5, in the Input Box, the Label should show calculated value of 5/2. That is, value of Input Box divide by 2.
In the Blazor framework, I am not understanding how to add jQuery.
Code is something like this:
<div class="col-sm-4">
<div class="justify-content-center mb-2">
<input type="text" class="form-control form-control-sm border border-secondary" #bind="myModel.CarWeight[index]" />
</div>
</div>
#if (myModel.AppType == "LC" || myModel.AppType == "LN")
{
decimal calcRes = Convert.ToDecimal(myModel.CarWeight[index]) / 2;
<div class="col-sm-4">
<div class="justify-content-center mb-2">
<label class="col-form-label"><b>calcRes</b></label>
</div>
</div>
}
Please note this line: myModel.CarWeight[index]
When the page is loaded, it creates an entry Form of 5 rows and 2 columns. 5 input boxes. When user fills any of the input box, I want the corresponding label to show the calculation.
You can update bind value on specific event
#bind:event="oninput"
See https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-5.0
You get this very simple code
#page "/"
<input type="number" #bind="inputVal" #bind:event="oninput" />
<label>#(Convert.ToDouble(inputVal)/2)</label>
#code{
string inputVal = "0";
}
Maybe somebody will come up with better idea to achieve what you want, but currently you could try something like this with C# code (.NET 5.0.103):
#page "/SampleComponent"
<input type="number" #bind="Operations[0].Input" #oninput="#( (input) => Calculate(input, 0))"/>
<label>#Operations[0].Result</label>
<input type="number" #bind="Operations[1].Input" #oninput="#( (input) => Calculate(input, 1))"/>
<label>#Operations[1].Result</label>
#code {
public List<Operation> Operations = new List<Operation>();
protected override void OnInitialized()
{
base.OnInitialized();
Operations.Add(new Operation{Input = 0, Result = 0});
Operations.Add(new Operation{Input = 0, Result = 0});
}
public void Calculate(ChangeEventArgs input, int id)
{
float result;
if(float.TryParse((string)input.Value, out result))
{
Operations[id].Result = result/2;
}
else
{
Operations[id].Result = 0;
}
}
public class Operation
{
public float Input { get; set; }
public float Result { get; set; }
}
}
Suppose I want to use an EditForm, but I want the value binding to trigger every time the user types into the control instead of just on blur.
Suppose, for the sake of an example, that I want an InputNumber<int> that does this? I've tried using different means that are floating around such as bind-Value:event="oninput" with no success. I was finally able to get more or less what I wanted by copying the AspNetCore source code for InputNumber and overriding/rewriting a few things.
Here is my InputNumber<int> which accomplishes what I'm hoping for:
public class MyInputNumber: InputNumber<int>
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddAttribute(1, "step", "any");
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddAttribute(3, "type", "number");
builder.AddAttribute(4, "class", CssClass);
builder.AddAttribute(5, "value", FormatValue(CurrentValueAsString));
builder.AddAttribute(6, "oninput", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.CloseElement();
}
// Copied from AspNetCore - src/Components/Components/src/BindConverter.cs
private static string FormatValue(string value, CultureInfo culture = null) => FormatStringValueCore(value, culture);
// Copied from AspNetCore - src/Components/Components/src/BindConverter.cs
private static string FormatStringValueCore(string value, CultureInfo culture)
{
return value;
}
}
note the "oninput" in the sequence 6 item was changed from "onchange" in the base InputNumber's BuildRenderTree method. I'd like to know how to:
see the output of BuildRenderTree, so that I can know how to do this with Razor and/or
just kind of know in general what sort of Razor syntax would be equivalent to doing this in the future.
I've gathered from comments in the AspNetCore code that this is definitely not the preferred way of doing this sort of thing, with Razor being the preferred approach. I've tested that this works in .NET Core 3 Preview 7 ASP.NET Core Hosted Blazor by subscribing to the EditContext's OnFieldChangedEvent, and can see that with this approach I get the different behavior that I'm looking for. Hopefully there is a better way.
Update
Including some more information about the problem
#using BlazorAugust2019.Client.Components;
#inherits BlazorFormsCode
#page "/blazorforms"
<EditForm EditContext="EditContext">
<div class="form-group row">
<label for="date" class="col-sm-2 col-form-label text-sm-right">Date: </label>
<div class="col-sm-4">
<KlaInputDate Id="date" Class="form-control" #bind-Value="Model.Date"></KlaInputDate>
</div>
</div>
<div class="form-group row">
<label for="summary" class="col-sm-2 col-form-label text-sm-right">Summary: </label>
<div class="col-sm-4">
<KlaInputText Id="summary" Class="form-control" #bind-Value="Model.Summary"></KlaInputText>
</div>
</div>
<div class="form-group row">
<label for="temperaturec" class="col-sm-2 col-form-label text-sm-right">Temperature °C</label>
<div class="col-sm-4">
<KlaInputNumber Id="temperaturec" Class="form-control" #bind-Value="Model.TemperatureC"></KlaInputNumber>
</div>
</div>
<div class="form-group row">
<label for="temperaturef" class="col-sm-2 col-form-label text-sm-right">Temperature °F</label>
<div class="col-sm-4">
<input type="text" id="temperaturef" class="form-control" value="#Model.TemperatureF" readonly />
</div>
</div>
</EditForm>
using BlazorAugust2019.Shared;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
namespace BlazorAugust2019.Client.Pages.BlazorForms
{
public class BlazorFormsCode : ComponentBase
{
public EditContext EditContext;
public WeatherForecast Model;
public BlazorFormsCode()
{
Model = new WeatherForecast()
{
Date = DateTime.Now,
Summary = "Test",
TemperatureC = 21
};
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
Console.WriteLine($"EditContext_OnFieldChanged - {e.FieldIdentifier.FieldName}");
}
}
}
What I'm looking for is the "EditContext_OnFieldChanged" event to fire when I type into the inputs. The current example works, just looking for a better way.
For anyone wondering, you can subclass InputText to change how it renders. For example, to make it use the oninput event, create MyInputText.razor containing:
#inherits Microsoft.AspNetCore.Components.Forms.InputText
<input #attributes="#AdditionalAttributes" class="#CssClass" #bind="#CurrentValueAsString" #bind:event="oninput" />
Now when you use <MyInputText #bind-Value="#someval" /> it will behave just like InputText except it updates on each keystroke.
SteveSanderson
Inheriting from a component and changing it so that it responds to the input event is now covered in the official documentation for .NET Core 3.1:
InputText based on the input event
Use the InputText component to create a custom component that uses the input event instead of the change event.
Create a component with the following markup, and use the component just as InputText is used:
razor:
#inherits InputText
<input
#attributes="AdditionalAttributes"
class="#CssClass"
value="#CurrentValue"
#oninput="EventCallback.Factory.CreateBinder<string>(
this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
The documentation also gives an example of how to inherit a generic component:
#using System.Globalization
#typeparam TValue
#inherits InputBase<TValue>
So if you then combine the two of those together you can create a component that changes the behaviour of the InputNumber component as follows:
#typeparam TNumber
#inherits InputNumber<TNumber>
<input
type="number"
step="any"
#attributes="AdditionalAttributes"
class="#CssClass"
value="#CurrentValue"
#oninput="EventCallback.Factory.CreateBinder<string>(
this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
Including the type="number" part will give the same behaviour as the existing InputNumber (only allowing numeric character entry, using the up and down arrows to increment/decrement etc.)
If you put the code above in a file called say InputNumberUpdateOnInput.razor in the Shared folder of the Blazor project that component can then be used in the same way you'd use a normal InputNumber, for example:
<InputNumberUpdateOnInput class="form-control text-right" #bind-Value="#invoiceLine.Qty" />
If you want to control the number of decimal places the component will allow you'd need to make the step value a parameter that you pass into the component. There's more on step in this answer.
Try this:
<input type="text" id="example1" #bind-value="#value" #bind-value:event="oninput" />
More...
Your approach is not recommended. You should subclass your components using Razor.
The following is a sample that should work. It can redirect you how to get a solution.
A solution to wrap an InputText:
NewInputText.razor
<div class="form-group">
<label class="col-form-label">#Label</label>
<InputText Class="form-control" Value="#Value" ValueChanged="#ValueChanged" ValueExpression="#ValueExpression"></InputText>
</div>
#functions {
[Parameter] string Label { get; set; }
[Parameter] string Value { get; set; }
[Parameter] EventCallback<string> ValueChanged { get; set; }
[Parameter] Expression<Func<string>> ValueExpression { get; set; }
}
Index.razor
<span>Name of the category: #category.Name</span>
<EditForm Model="#category">
<NewInputText #bind-Value="#category.Name"/>
</EditForm>
You may also inherit from InputBase directly like so:
#inherits InputBase<string>
<input type="number" bind="#CurrentValue" id="#Id" class="#CssClass" />
Hope this helps...
I found that one can do that if he/she avoids using #bind and instead, bind manually like this:
<InputText Value=#MyValue #oninput=#HandleInputChange ValueExpression=#(() => MyValue) />
#{
// This will change for every character which is entered/removed from the input
Public string MyValue { get; set; }
void HandleInputChange(ChangeEventArgs args)
{
MyValue = args.Value.ToString();
}
}
I still don't really understand why this works. I think it is because of the AdditionalAttributes parameter. It passes the oninput attribute to the html <input> element which updates your value, so you are not relying anymore on the ValueChanged callback to update your value.
Alright, I've figured this out after poking around the source code looking particularly at properties CurrentValueAsString and CurrentValue. This is the solution I've come up with for making a text input that properly fires field changed events oninput:
public class InputTextCode : InputBase<string>
{
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}
#inherits InputTextCode;
<input type="text" id="#Id" class="#Class" #bind-value="CurrentValueAsString" #bind-value:event="oninput" />
it would be really nice if this could be an easily configurable option on Input components out of the box, but at least there is a way to do it that doesn't make my skin crawl.
Good day, i have dynamically created form elements with jquery that looks like this:
<label for="fname" class="fname_label col-sm-2 control-label">First Name</label>
<div class="col-sm-4">
<input type="text" class="fname_input form-control" id="fname" placeholder="First Name" name="firstnames">
</div>
<label for="uemail" class="uemail_label col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input type="text" class="uemail_input form-control" id="uemail" placeholder="Email" name="emailaddresses">
A user can create multiple elements like this with same name and ids. I mean if a user should click on add more, the same elements are created with the same name and id with jquery. A user can create multiple elements say 10 or more. My question is how can i post or insert the values of the dynamically create elements to the database. i am using c# in MVC. Thank You.
Had a bit of time and put this together. I created a JavaScript namespace to hold my functions, data etc; kept the jQuery part separate for the event (submit and add rows) management. You could easily add capability to delete new entry groups (row) as well, just need ONE to stay as I used .clone() to get that new row.
Sample markup using some bootstrap stuff (not required for the functional part). Note I used jQuery for the ajax stuff, you would not have to but it made the sample a bit smaller perhaps.
<div class="container">
<form id="myform">
<div class="inputs-holder">
<fieldset class="entry-group">
<legend class="col-form-legend col-xm-2">
one input
</legend>
<div class="form-group row">
<label class="col-xs-2 col-form-label col-form-label-sm">Name</label>
<div class="col-xs-7">
<input required="true" class="form-control form-control-xs name-field" type="text" />
</div>
</div>
<div class="form-group row">
<label class="col-xs-2 col-form-label col-form-label-sm">Email</label>
<div class="col-xs-7">
<input required="true" class="form-control form-control-xs email-field" type="email" placeholder="enter email" value="testme#example.com" />
</div>
</div>
</fieldset>
</div>
<div class="form-group row">
<div class="offset-xs-2 col-xs-5">
<button id="submitme" type="submit" class="btn btn-primary btn-xs">Submit Me</button>
</div>
<div class="offset-xs-2 col-xs-5">
<button id="addnewgroup" type="button" class="btn btn-xs">Add new group</button>
</div>
</div>
</form>
</div>
<div id="results">
Entries
</div>
Some script to process and push data via ajax to server:
/* Latest compiled and minified JavaScript included as External Resource */
var myApp = myApp || {};
myApp.arrayObj = {
// some stuff clipped out not used here...
// use to lookup duplicates
lookup: function(myArray, searchTerm, property, firstOnly) {
var found = [];
var i = myArray.length;
while (i--) {
if (myArray[i][property] === searchTerm) {
found.push(myArray[i]);
if (firstOnly) break; //if only the first
}
}
return found;
},
// could be used to validate duplicates for example
lookupAll: function(myArray, property, searchTerm) {
return this.lookup(myArray, searchTerm, property, false);
}
};
myApp.data = {
entries: [],
saveUrl: "/Home/SaveEmails" this COULD be from server/MVC
};
myApp.func = {
addEmailRow: function(myArray, item, allowdups, uniquekey) {
// matches the POCO object class names
var entry = {
"name": item.name,
"email": item.email
};
if (allowdups || (!allowdups && !myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length)) {
myArray.push(entry);
} else if (allowdups && myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length) {
myApp.data.entries[uniquekey] = item[uniquekey];
} else if (allowdups && !myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length) {
myArray.push(entry);
}
},
// just something to show what is entered/being submitted
showEntries: function(list) {
$.each(list, function(index, value) {
$('#results').append("<div>" + value.name + " " + value.email + "</div>");
});
},
// the important part
saveEntries: function(list) {
var entries = JSON.stringify({
'Entries': list
});
$.ajax({
contentType: 'application/json;',
dataType: 'json',
type: 'POST',
url: myApp.data.saveUrl,
data: entries
}).done(function() {
$('#results').html('"SaveEmails()" successfully called.');
})
.fail(function(response) {
$('#results').html(response);
});
}
};
$(document).ready(function() {
// add new "group" row
$('#addnewgroup').on('click', function() {
var holder = $('.inputs-holder');
// clone that first row
var newRow = holder.find('.entry-group').eq(0).clone();
// clear any values entered and append it
newRow.find('input').val("");
newRow.appendTo(holder);
});
// a bit verbose for clarity here
$('#myform').on('submit', function(e) {
e.preventDefault();
e.stopPropagation();
// clear entries
myApp.data.entries = [];
var allowdups = false,
uniquekey = "name";
var holder = $('.inputs-holder');
// get entries
holder.find('.entry-group').each(function() {
var email = $(this).find('.email-field').val();
var name = $(this).find('.name-field').val();
var item = {
"email": email,
"name": name
};
myApp.func.addEmailRow(myApp.data.entries, item, allowdups, uniquekey);
});
$('#results').html("<div>Results:</div>");
myApp.func.showEntries(myApp.data.entries);
myApp.func.saveEntries(myApp.data.entries);
// supress default submit form
return false;
});
});
Now the server side:
/* MVC for this: */
// POCO object: - reference this whereever you put it.
public class EmailEntry
{
public String name { get; set; }
public String email { get; set; }
}
// controller/ method: used IEnumerable instead of List as simpler
public class HomeController : Controller
{
[HttpPost]
public void SaveEmails(IEnumerable<EmailEntry> entries)
{
// do stuff with email entries here...validate/save for example
}
}
ALL this is untested and my contain small errors but I believe it to be pretty bug free.
I have an html begin form in mvc
#using (Html.BeginForm("Search", "Reports",FormMethod.Post, new { enctype = "multipart/form-data", #class = "form-inline" }))
{
<div class="form-group">
<input type="text" class="form-control input-sm" placeholder="Value" name="SearchValue">
<input type="text" class="form-control input-sm second-value" placeholder="Value" style="display:none;" name="SearchValue1">
<button type="button" class="btn btn-default btn-Add">+</button>
</div>
<div id="othersearch"></div>
<input type="submit" value="Search" class="btn btn-primary" />
}
I want to post this form item in one controller
public ActionResult Search(FormCollection collection)
{
string searchvalue = collection.Get("SearchValue");
return View();
}
my problem is that some times second text box is not visible.. that time i dont want to collect the values.And when i press button add generate the same type of input field in the form with same name (i can add many input box). Then how can i collect all these in my controller . please help me..
You can have all the text boxes with same name "SeachValue" in your case.
string searchvalue = collection.Get("SearchValue");
This will return all text box values as comma saperated string which you can split and use further.
Check out the screen shot
the html
and the results
You can get value of all textboxes having same name using following code:
var results = ((String[])formcollection.GetValue("mytxt").RawValue).ToList();
foreach (var item in results)
{
//string name = item;
}
When ever you add a element dynamically make sure you also set it a name. so when you add a new input element it must be
<input type="text" name="NewTextBox" class="form-control input-sm" placeholder="Value" name="searchvalue">
So like this no matter how many text boxes you add, All will have same name. Once you post the form. In your controller do this.
[HTTPPOST]
public ActionResult Search(MyModel newModel,string[] NewTextBox)
{
// here as you had dynamic textbox with name = NewTextBox you
//will get all its value binded to the above string[]
}
OR
You can retrive them using Request.form["NewTextBox"] as
[HTTPPOST]
public ActionResult Search(MyModel newModel)
{
var values = Request.Form[NewTextBox];
}
But I would recommend you the first approach where you use the MVC Model Binder to take care of all the things. You will just have array of values to play with.
NOTE: Always make sure you get the names right, and use the name right while playing with MVC. As all the binding depends on the naming itself.