Blazor problem connecting the UI with the code - asp.net-core

I'm using Blazor Server application in Visual Studio 2019. In the .razor page I have:
<div class="container">
<div class="row">
<div class="col-md">
<label for="ConnectionStringEdit" id="Label1">Connection String for destination</label>
</div>
<div class="col-md-7">
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" text=#ConnectDestination spellcheck="false" style="width: 585px; height: 26px;" class="form-control">
</div>
<div class="col-md-auto">
<input type="submit" id="btnConnect" name="btnConnect" value="Connect" class="btn btn-primary" #onclick="Connect1">
</div>
</div>
</div>
now in the code part I have
#code {
private string ConnectDestination { get; set; } = "";
private void Connect1()
{
if (ConnectDestination.Length > 0)
{
// do something
}
}
}
When I insert something in the Input and I press the button, ConnectDestination doesn't take the value of the Input Control. So this last If condition is never true. How do I get the inserted value of the Input control named ConnectionStringEdit?
Thanks

It should be #bind-value="#ConnectDestination"
you could also use the short directive #bind instead:
#bind="#ConnectDestination"
Note: All the input element's types are bound through the value attribute of the element.
Note: Both #bind-value and #bind are compiler directive instructing the compiler to emit code, behind the scene, that enables two way data-binding between a variable and an Html tag. The compiler create a two-way data binding by binding a variable to the value attribute of the element, something equivalent to this:
value="#ConnectDestination", which creates a one direction binding from the variable to the bound element. The compiler also creates an event call back which enables binding from the element to the variable, something equivalent to this:
#onchange="#((args) => ConnectDestination = args.Value?.ToString())"
This means that you could do that yourself, if you wish to have more control over the binding. You'll usually do something like this:
value="#ConnectDestination" #onchange="OnChange"
And define the call back method like this:
private void OnChange(ChangeEventArgs args)
{
// Note that it is your responsibility to update the
// ConnectDestination variable:
ConnectDestination = args.Value?.ToString());
}
Note: This is wrong:
<input type="submit" id="btnConnect" name="btnConnect" value="Connect" class="btn btn-primary" #onclick="Connect1">
The type attribute of the input element should be set to button:
<input type="button"
Blazor App is an SPA... meaning no submit. The only place you use the "submit" button is when you use the EditForm component, and even then the "submit" action is intercepted and canceled by the Blazor.

You can try
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" #bind=#ConnectDestination spellcheck="false" style="width: 585px; height: 26px;" class="form-control">
or
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" value="#ConnectDestination"
#onchange="#((ChangeEventArgs __e) => ConnectDestination = __e?.Value?.ToString())" spellcheck="false" style="width: 585px; height: 26px;" class="form-control">

Related

ASP.NET Core 2.2. Razor Pages - How to populate a form control based on another field

I have a form control "ConnectorType" which I turned into a dropdown list with pre-defined values (just 3qty currently)
When the user selects and item from this dropdown list, depending on the value selected I then want to populate another text box form control underneath.
To better explain, please see image below:
Example, if TCP Server IN is selected then the form control underneath (textbox)should automatically say "Inbound"
Ideally this text box should also have an attribute/configuration that prevents the user from entering their own text, grayed out perhaps. Once the create form is submitted, the textbox that contains this value "Inbound" will then be added to the SQL Table using Enitity Framework.
The solution requires that this field dynamically changes each time a new item is selected from the list.
Current code for the drop down list:
Page Model Class:
public IEnumerable<SelectListItem> ConnectorTypeList { get; private set; } // temp
public IActionResult OnGet()
{
// prepare the list in here
ConnectorTypeList = new SelectListItem[]
{
new SelectListItem ("TCP Server IN", "TCP Server IN"),
new SelectListItem ("TCP Server OUT", "TCP Server OUT"),
new SelectListItem ("SMTP Server IN", "SMTP Server IN")
};
return Page();
}
Page View:
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" class="form-control" asp-items="#Model.ConnectorTypeList"></select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" class="form-control" />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
Note the current form-control I'm wanting to modify is the "ConnectorModel.DataFlow" in the above page view code. At the moment it's just a simple textbox that the user can enter their own choice of text.
I'm going round in circles having read up on page handlers etc. It seems there is a onchange event but unsure how to implement this and somehow link it back to the page model class, run a method then postback the result. I'm not looking for a JQuery script as it seems this should not be required in the newer framework, not sure I just don't want a complicated long solution given I will be using a lot of these throughout the app. Thanks in advance...
The easiest way is to use onchange() on your <select> tag and assign data to input using js.(Add id attribute for <select> and <input> before)
If you would like to prevent the user from entering their own text, just use readonly attribute for you input.
<input asp-for="DataFlow" id="dataFlow" class="form-control" readonly/>
The Sample Page View:
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" id="connectorTypeList" class="form-control" asp-items="#Model.ConnectorTypeList" onchange="assignData()">
<option>Select ConnectorType</option>
</select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" id="dataFlow" class="form-control" readonly />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts{
<script>
function assignData() {
var contentType = $("#connectorTypeList").val();
if (contentType == "TCP Server IN") {
$("#dataFlow").val("Inbound");
}
}
</script>
}

How to pass a dynamicy changed model to Partial view?

I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .

ASP.NET Core - Data values not bound when trying to use a partial view to lay out form controls

I'm trying to create a partial view to save on the amount of boilerplate code I need when creating forms. A simple version of what I have is as follows...
#model FormRowViewModel
<div class="form-group row">
<label for="#Model.PropertyName" class="col-lg-2 col-form-label">#Model.Label</label>
<div class="col-lg-10 input-group">
<input type="text" asp-for="#Model.PropertyName" class="form-control">
</div>
</div>
...where FormRowViewModel looks like this...
public class FormRowViewModel {
public FormRowViewModel(string propertyName, string label) {
PropertyName = propertyName;
Label = label;
}
public string PropertyName { get; set; }
public string Label { get; set; }
}
The idea is to use it in a view like this...
#model ContactViewModel
<form method="post" asp-controller="Home" asp-action="Index">
<div asp-validation-summary="All" class="text-danger"></div>
#await Html.PartialAsync("_FormRow", new FormRowViewModel("UserName", "Your name"))
#await Html.PartialAsync("_FormRow", new FormRowViewModel("Email", "Email"))
#await Html.PartialAsync("_FormRow", new FormRowViewModel("Telephone", "Telephone"))
<div class="form-group row">
<div class="offset-sm-2 col-lg-10">
<button type="submit" class="btn btn-primarySubmit</button>
</div>
</div>
</form>
This works, in that it creates the HTML (almost) as expected, but has two problems...
1) The generated HTML includes value attributes that set the content of the textboxes to the property names...
<input type="text" class="form-control" id="PropertyName"
name="PropertyName" value="UserName">
2) Whatever I put in the textboxes, when the form is posted back to the server, the view model properties are all empty strings. Even the property names that were added don't come through.
In case it helps, here is the controller action that handles the view...
public IActionResult Index() =>
View();
[HttpPost]
public IActionResult Index(ContactViewModel vm) {
if (!ModelState.IsValid) {
return View(vm);
}
// Next line added so I can see when it worked
return RedirectToAction(nameof(Privacy));
}
Anyone know what I'm doing wrong? Thanks
Your whole approach here is incorrect. It seems what you're looking for is editor templates. Essentially, you need to create partial views in Views\Shared\EditorTemplates that correspond with types or members of the DataType enum, and add your custom HTML there. For example, you can create a String.cshtml view:
#model string
<div class="form-group row">
<label asp-for="#Model" class="col-lg-2 col-form-label"></label>
<div class="col-lg-10 input-group">
<input asp-for="#Model" class="form-control">
</div>
</div>
Then, for any string property:
#Html.EditorFor(x => x.MyStringProp)
And your custom template will be used, with the proper name binding.
Alternatively, you can create custom taghelpers, but the methodology for that is a bit more complicated, since you'll need to handle the HTML generation in code. If you're interested in that approach, though, look at the source for the built-in tag helpers and create your own based on that.

Can I debounce a checkbox input in Aurelia?

I'm trying to use the debounce binding behaviour on a list of checkboxes, but it doesn't seem to be working the way I expect (I'm not sure if you can even debounce a checkbox):
<label repeat.for="v of values">
<input type="checkbox" value.bind="v" checked.bind="checkedVal & debounce:1000"> Checkbox value "${v}"
</label>
clicking on any of the checkboxes results in the checkedVal array updating immediately, whereas it works as I expect for a normal input:
<input type="text" value.bind="textVal & debounce:1000"/>
Can I debounce a checkbox input?
Here's the full code, with a GistRun here.
app.html:
<template>
<h1>Checkbox bind debounce</h1>
<form>
<label for="text">text input with debounce:1000 </label>
<input type="text" value.bind="textVal & debounce:1000"/>
<div repeat.for="v of values">
<br/>
<label>
<input type="checkbox" value.bind="v" checked.bind="checkedVal & debounce:1000"> Checkbox value "${v}"
</label>
</div>
</form>
<br/>
<p>Text value: ${textVal}</p>
<p>Checked values:</p>
<p repeat.for="v of checkedVal">${v}</p>
</template>
app.js:
export class App {
values = [1, 2, 3];
checkedVal = [];
}
Thanks!
At this time, it's not supported. The debounce binding behavior controls the rate at which the checkedVal property is assigned. In a checked binding, the property isn't assigned, the array instance referenced by the property is mutated with push and splice which circumvents the debouncing in the binding expression.

asp-for tag adds required field validation on checkbox in asp.net core

I have asp.net core application and im trying to add simple checkbox without any validation. Checkbox is bound to boolean property on model. Below is the code
Model
public class MyModel
{
public bool IsEmployee { get; set; }
}
cshtml
<form>
<div>
<label asp-for="IsEmployee">Is Employee</label>
<input type="checkbox" asp-for="IsEmployee"/>
</div>
<button id="btnSave" class="btn btn-primary" type="button">Save</button>
</form>
<script src="~/js/test.js"></script>
javascript
$(function () {
var kendoValidator = $('form').kendoValidator().data("kendoValidator");
$('#btnSave').click(function () {
if (kendoValidator.validate()) {
alert('true');
}
else {
alert('false');
}
})
})
I am using asp-for tag helper on input element. Note that IsEmployee property DOES NOT have [Required] attribute. But because of asp-for tag helper the rendered html has data-val-required and data-val attributes on input element. It also adds one more hiddden input element with same name.
Below is rendered html.
(also note that i think it only happens when input type is checkbox. for textboxes its working fine)
<form novalidate="novalidate" data-role="validator">
<div>
<label for="IsEmployee">Is Employee</label>
<input name="IsEmployee" id="IsEmployee" type="checkbox" value="true" data-val-required="The IsEmployee field is required." data-val="true">
</div>
<button class="btn btn-primary" id="btnSave" type="button">Save</button>
<input name="IsEmployee" type="hidden" value="false">
</form>
I am using kendovalidator as well which adds data-role="validator" on form element.
Issues
There are 2 issues here
1> As soon as i click on check the box error message appears as The IsEmployee field is required.
2>kendoValidator.validate() method always returns false regardless of checkbox is selected or not.
Demo here JSFiddle
Update 2
We cannot bind nullable bool to checkbox. I am using asp.net core. I am not sure what the equivalent syntax in asp.net core for the suggestion here which is valid for classic asp.net
Add data-validate="false" to the checkbox input. The kendoValidator will ignore all inputs with that attribute set to false.
<input type="checkbox" asp-for="IsEmployee" data-validate="false" />
If you don't wan't the default generated html you have 2 choices.
Don't use it ! You are not forced to use the tag helpers, they are there for when you do need other html attributes generated. In this case just use < input name="IsEmployee" ...>
Change the way asp-for behaves for your checkbox. You can do this be either creating your own IHtmlGenerater or by extending the DefaultHtmlGenerator and overriding GenerateCheckBox and possibly GenerateInput and then registering it with something like services.TryAddSingleton();
Hope this helpes you.