I am working on a project in C# with .NetCore Razor Pages and I have this page where a user makes an appointment by completing some fields.
I have this field that is a calendar and the user can select a date from it. Based on that date I want to populate a drop down with a list of hours that are available for an appointment.
I tried to use Handlers like OnPost() but that requires a submit button, but I only have the calendar input.
This is the code I use in my Razor Page for the calendar
<div class="form-group col-md-4">
<label asp-for="StartDate"></label>
<input asp-for="StartDate" class="form-control" />
<span class="text-danger" asp-validation-for="StartDate"></span>
</div>
In my model page I should have something like
public IActionResult OnPostStartDate()
{
\\code that brings from the database the available hours
}
Is there any other function that I can use so when the user chooses a date from the calendar the dropdown will be populated with the available hours?
EDIT
In my Razor Page
#section Scripts {
<script>
$("#StartDate").on("change", function () {
var time = $(this).val();
$("#select").empty();
$("#select").append("<option value=''>select </option>");
$.getJSON(`?handler=Time&time=${time}`, (data) => {
$.each(data, function (i, item) {
$("#select").append("<option value='" + "'>" + item.hour + "</option>");
});
});
});
</script>
}
<form class="form-style" method="post" id="createBTForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="StartDate" class="control-label"></label>
<input asp-for="StartDate" class="form-control" />
<span asp-validation-for="StartDate" class="text-danger"></span>
</div>
<select id="select" asp-for="Time" class="form-control"></select>
<div class="form-group button-position col-md4">
<input type="submit" id="placeRequest" name="placeRequest" value="Place Request" class="btn btn-primary" />
</div>
</form>
In my model Page
[Required(ErrorMessage = "Field cannot be empty!")]
[BindProperty]
[DataType(DataType.Date)]
[Display(Name = "Start Date:")]
public DateTime StartDate { get; set; }
//this is null after selecting an option from the dropdown
[BindProperty]
public string Time { get;set; }
//this is the method that should work when I press the Place Request submit button
public async Task<IActionResult> OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
//here I send the data to the database
return Redirect(Url.Page(indexPage));
}
//this is the method that puts the values on the dropdown (this works)
public async Task<IActionResult> OnGetTime(DateTime time)
{
//Here I set my model based on database data
var finalHours = leaveFromLocations.Where(f => !unavailable.Contains(f));
foreach (var h in finalHours)
{
model.Add(new Hours
{
Hour = h
});
}
return new JsonResult(model);
}
The problem is that after I send the json model to the dropdown and the values appear in the dropdown I can't take the option that is selected (in debug after choosing an option the Time property appears null)
You can write an onchange function and then use ajax to send data to backend.Below is a simple demo.
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Time.StartDate" class="control-label"></label>
<input asp-for="Time.StartDate" class="form-control" />
<span asp-validation-for="Time.StartDate" class="text-danger"></span>
</div>
<select id="select" asp-for="Time.Hour" class="form-control"></select>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$("#Time_StartDate").on("change", function () {
var time = $(this).val();
$("#select").empty();
$("#select").append("<option value=''>select </option>");
$.getJSON(`?handler=Time&time=${time}`, (data) => {
$.each(data, function (i, item) {
//"id" and "hours" is in your JsonResult(model),and remember The first letter of the attribute must be lowercase
$("#select").append("<option value='" + item.id + "'>" + item.hours + "</option>");
});
});
});
</script>
}
Backend:
public IActionResult OnGetTime(DateTime time)
{
//this is a fake data,you can query your data here.
var model = new List<Hour>
{
new Hour
{
Id=1,
Hours=1
},
new Hour
{
Id=2,
Hours=2
},
};
return new JsonResult(model);
}
Test result:
Related
I have a process to import Excel-data. That works fine. But for starting the process I have to select some values. "Year from", "Year to" and the parameter isTest.
I want to change the range of the import-values. I have tried several solutions. But nothing works.
After changing the values and by clicking on the submit-button, I want to see the new values.
What do I have to change in the code?
This is the ActionResult:
[HttpPost]
public IActionResult Importeren(int jaarVan, int jaarTm, bool isTest)
{
//minValue = 1981; maxValue = 2004; isTest = true;
_importProces.Start(jaarVan, jaarTm, isTest);
return View();
}
This is the html-page:
#using (Html.BeginForm("Importeren", "Home", new { jaarVan = ViewBag.JaarVan, jaarTm = ViewBag.JaarTm, isTest = ViewBag.IsTest }, FormMethod.Post))
{
<div class="form-group">
<div class="col-md-2 col-xl-2">
#Html.Label("Jaar vanaf:")
</div>
<div class="col-md-2 col-xl-2">
#Html.Hidden("#JaarVan")
<span id="lblJaarVan">#ViewBag.JaarVan</span><input type="range" id="jaarVan" value="#ViewBag.JaarVan" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1"
onshow="showJaarVan(this.value)" oninput="showJaarVan(this.value)" onchange="showJaarVan(this.value)" />
</div>
<div class="col-md-2 col-xl-2">
#Html.Label("Jaar tot/met:")
</div>
<div class="col-md-2 col-xl-2">
#Html.Hidden("#JaarTm")
<span id="lblJaarTm">#ViewBag.JaarTm</span><input type="range" id="jaarTm" value="#ViewBag.JaarTm" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1"
onshow="showJaarTm(this.value)" oninput="showJaarTm(this.value)" onchange="showJaarTm(this.value)" />
</div>
<div class="col-md-2 col-xl-2">
#Html.Label("Is test:")
#Html.Hidden("#IsTest")
<input type="checkbox" id="isTest" value="#ViewBag.IsTest" checked="#ViewBag.IsTest" />
</div>
#*<progress*#
<p> <input class="text-primary" type="submit" value="Import Exceldata" name="Opslaan" /></p>
</div>
}
<script>
//$(document).ready(function () {
// $('#frmsubmit').submit(function () {
// var jaarVan = document.getElementById("#jaarVanRange").value;
// var jaarTm = document.getElementById("#jaarTmRange").value;
// var isTest = document.getElementById("#isTest").value;
// })
//});
function showJaarVan(newVal) {
document.getElementById("lblJaarVan").innerHTML = newVal;
ViewViewBag.JaarVan = newVal;
}
function showJaarTm(newVal) {
document.getElementById("lblJaarTm").innerHTML = newVal;
ViewViewBag.JaarTm = newVal;
}
</script>
I believe the model binding for MVC form posts relies on the name attribute of your inputs, try adding names to your inputs to match the parameters in your action:
<input type="range" id="jaarTm" name="jaarTm" value="#ViewBag.JaarTm" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1" onshow="showJaarTm(this.value)" oninput="showJaarTm(this.value)" onchange="showJaarTm(this.value)" />
I also don't think you need to include the route values object in the BeginForm() call because form data has higher precedence in model binding. That object is also evaluated before the post and hold the original years and not the new ones that you set in your range inputs.
The goal here is to get input="file" Required validation attribute to work before clicking away. Now when I select file to upload I have to click away for require validation to work. In result I have to double click submit.
index.cshtml.cs
[Required(ErrorMessage = "File is required.")]
[BindProperty]
[Display(Name = "File")]
public IFormFile Upload { get; set; }
index.cs
#page
#model IndexModel
#{
ViewData["Title"] = "Data uploader";
}
#if (Model.Success)
{
<div class="alert alert-success alert-dismissible">
×
Failas įkeltas.
</div>
}
#if (Model.Error)
{
<div class="alert alert-danger alert-dismissible">
×
#Model.ErrorMessage
</div>
}
<div class="panel-body py-4 bg-light">
<div class="container col-lg-8 floated_elements">
<div class="h4 text-center py-3">Data update</div>
<form method="post" class="needs-validation" enctype="multipart/form-data" novalidate>
<div class="form-group row mb-3">
<label asp-for="Upload" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="custom-file">
<input type="file" asp-for="Upload" class="custom-file-input" accept=".zip" />
<label class="custom-file-label">Select file</label>
<span asp-validation-for="Upload"></span>
</div>
</div>
</div>
<button class="btn btn-primary float-right" type="submit">Submit</button>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$(document).ready(function () {
if ($(".custom-file span").hasClass('field-validation-error')) {
if (!$(".custom-file-input").hasClass('is-invalid')) {
$(".custom-file-input").addClass("is-invalid")
}
}
}
);
window.setTimeout(function () {
$(".alert-success").fadeTo(500, 0).slideUp(500, function () {
$(this).remove();
});
}, 2000);
$(document).ready(function () {
$('.custom-file-input').on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).next('.custom-file-label').html(fileName);
});
});
</script>
}
Clicking button without populating any of the fields violates the [Required] attributes on the model. The ModelState is invalid. The validation error messages are displayed to view. If you not submit, the validation will not work.
Solution 1
Creat client-side JavaScript Form Validation.
Solution 2
There is a simply solution for you case. Add $("#update").click(); to trigger validation.
Codes of Js
<script>
$(document).ready(function () {
$("#update").click();
if ($(".custom-file span").hasClass('field-validation-error')) {
if (!$(".custom-file-input").hasClass('is-invalid')) {
$(".custom-file-input").addClass("is-invalid")
}
}
}
To have a better user experience, I optimized the code of input change event.
$('.custom-file-input').on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).next('.custom-file-label').html(fileName);
$(".custom-file-input").removeClass("is-invalid");
if (fileName == '')
$(".custom-file-input").addClass("is-invalid")
});
I'm new to asp.net core and trying to learn… I've a razor page with some controls. (Asp.Net Core 2.2) and i wanna fill text-boxes or other controls by selecting an item from a combobox… When i used GET method, the data contains html tags… If i use POST no value comes back. Need help to understand if my way is wrong or not
the cshtml as follows
<script type="text/javascript">
$(function () {
$("#txtTcKimlikNo").change(function () {
$.ajax({
url: '#Url.Action("fillOgrenciData")',
type: "POST",
data: { "code": $(this).val() },
"success": function (data) {
if (data != null) {
alert(data);
$("#name").val(data.Okulno);
alert($("#name").val());
alert("ok");
}
}
});
});
});
</script>
<section class="well">
<h2 class="ra-well-title">Öğrenci Bilgileri</h2>
<div class="form-group">
<label class="control-label col-sm-4" for="name">Adı Soyadı</label>
<div class="col-sm-8 col-md-6">
<input id="name" class="k-textbox" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4" for="birthday">Doğum Tarihi</label>
<div class="col-sm-8 col-md-6">
#(Html.Kendo().DatePicker().Name("date").Value("10/09/1979"))
</div>
</div>
</div>
<div class="form-horizontal form-widgets col-sm-6">
<div class="form-group">
<label class="control-label col-sm-4" for="txtTcKimlikNo">Tc Kimlik No</label>
<div class="col-sm-8 col-md-6">
#(Html.Kendo().ComboBox()
.Name("txtTcKimlikNo")
.DataTextField("Tckimlikno")
.DataValueField("Ogrid")
.BindTo(Model.ogrenciler)
.Filter("Contains")
.Height(300)
.Template("<span class=\"k-state-default\">#: data.Adisoyadi #<p>#: data.Okulno #</p></span>"))
</div>
</div>
</div>
and the cshtml.cs is as follows
public void OnGet()
{
Data = _db.Okul.ToList();
EgOgyillar = _db.PrEgogyillar.ToList();
ogrenciler = _db.Ogrenci.ToList();
}
public JsonResult ReadOkul()
{
return new JsonResult(_db.Okul.ToList());
}
[HttpPost]
public ActionResult fillOgrenciData(string code)
{
var query = from c in _db.Ogrenci
where c.Tckimlikno == Convert.ToInt64(code)
select c;
return new JsonResult(query);
}
But i can't fill the name textbox with data.Okulno. It always comes undefined. By the way i didn't use models in app. If it's need to used, i will restart to write the app.
I am working on a project in MVC which has multiple forms on one page but the forms are separated as different views and are called as #Html.actions from the main view. Some of these forms are complex and have text-boxes and multiple select lists in them. I am trying to make this page be completely dynamic because there is such a large amount of code to reload if the page is refreshed by a post or page change. Part of the main view looks like this:
<div class="row" style="height:500px">
<div class="col-lg-3">
<h3>Groups</h3>
<ul class="nav nav-pills nav-stacked">
<li><a data-toggle="pill" href="#CreGroup"> Create Group</a></li>
<li><a data-toggle="pill" href="#ModGroup"> Modify Group</a></li>
<li><a data-toggle="pill" href="#DelGroup"> Delete Group</a></li>
<li><a data-toggle="pill" href="#ARGS"> Add or Remove Users/Group from Schedule</a></li>
</ul>
</div>
<div class="container col-lg-8 col-lg-offset-1" style="height:95%;border-radius:70px;border-color:darkblue; border:1px solid">
<div class="tab-content col-lg-offset-1">
<div id="CreGroup" class="tab-pane active">
#Html.Action("CreateGroup", "Home")
</div>
<div id="ModGroup" class="tab-pane">
#Html.Action("ModifyGroup", "Home")
</div>
<div id="DelGroup" class="tab-pane">
#Html.Action("DeleteGroup", "Home")
</div>
<div id="ARGS" class="tab-pane">
#Html.Action("ARGS", "Home")
</div>
</div>
</div>
</div>
I would like to focus on my CreateGroup view/form. This form has group name, permission, and type as text boxes. It also has a multi select list with users to be put in the group in the form. The controller for the Create Group view looks like this:
[HttpGet]
public ActionResult CreateGroup()
{
List<string> u = new List<string>();
object[] users = data.getDataFrmDB("Select username From `users`;");
if (users != null)
{
foreach (object[] user in users)
{
u.Add((string)user[0]);
}
}
ViewBag.GUsers = new MultiSelectList(u, "Username");
return View();
}
Right now the view looks like this: (and a picture of it rendered is below)
##CreateGroup##
<div class="container" style="text-align:center">
<div class="row">
#using (Html.BeginForm(null, null, FormMethod.Post))
{
<h3>Create Group</h3>
<div class="col-lg-7">
<div class="row">
<div class="col-lg-6" style="text-align:left">
<br/>
<label>Group Name:</label> <br /><br />
<label>Group Type:</label><br /><br />
<label>Group Permissions:</label><br /><br />
<label>Server Name:</label><br /><br />
<label>Server Email:</label><br /><br />
</div>
<div class="col-lg-6" style="text-align:right;">
<br/>
#Html.TextBox("Gname", "Group Name", new { maxlength = 50 })<br /><br />
#Html.TextBox("Gtype", "Group Type", new { maxlength = 50 })<br /><br />
#Html.TextBox("Gpermission", "Permissions", new { maxlength = 50 })<br /><br />
#Html.TextBox("Gserver", "Server Name", new { maxlength = 50 })<br /><br />
#Html.TextBox("Gemail", "Server Email", new { maxlength = 50 })<br /><br />
<br />
</div>
</div>
</div>
<div class="col-lg-4 col-lg-offset-1">
<label> Select Users for Group: </label>
#Html.ListBox("GUsers", ViewBag.Users as MultiSelectList,
new { #class = "chzn-select", #style = "width:150px; height:250px" })
</div>
}
</div>
<div class="row">
<div class="col-lg-1 col-lg-offset-8">
<button class="btn btn-primary dropdown-toggle" id="Button1" type="button" onclick="group()"> Create Group(s)</button>
</div>
</div>
Right now I am using ajax to handle the forms with only textboxes but it does not seem to work when there is a list box. So how would I post all of these values dynamically to the controller without reloading the page? Thank you in advance.
I figured it out. You have to handle it like a regular json post with only text boxes but tack on the list of selected objects to the array being posted.
Here is the Json post:
var GnameInput = $("#Gname");
var GserverInput = $("#Gserver");
var GemailInput = $("#Gemail");
var GtypeInput = $("#Gtype");
var GpermInput = $("#GPerm");
var Gusers = $("#GUsers");
function group() {
var myList = []
var Gname = GnameInput.val();
var Gserver = GserverInput.val();
var Gemail = GemailInput.val();
var Gtype = GtypeInput.val();
var Gperm = GpermInput.val();
//add all the values to the start of the array
myList.push(Gname);
myList.push(Gserver);
myList.push(Gemail);
myList.push(Gtype);
myList.push(Gperm);
//add all the selected values after
$("#GUsers > option:selected").each(function () {
myList.push($(this).val());
});
jQuery.ajax({
type: 'post',
dataType: 'json',
contentType: "application/json; charset=utf-8",
url: 'CreateG',
data: JSON.stringify(myList),
success: function (data) {
//remove all of the objects from the list after post
$('#GUsers option:contains("' + item + '")').remove();
//confirmation message off success
$('#msgCG').html("sucessfully created Group: "+Gname);
},
failure: function (errMsg) {
$('#msgCG').html(data.msg);
}
});
return false;// if it's a link to prevent post
}
Here is the controller:
[HttpPost]
public JsonResult CreateG(List<string> group)
{
if (group == null)
{
///break if the post failed
}
//assign values base on where they were placed in array and remove after
string name = group[0];
group.RemoveAt(0);
string server = group[0];
group.RemoveAt(0);
string email = group[0];
group.RemoveAt(0);
string type = group[0];
group.RemoveAt(0);
string perm = group[0];
group.RemoveAt(0);
//iterate through the remainder of users
foreach (string user in group)
{
//do whatever
}
return Json(group);
}
net mvc 5 application and for this I use bootstrap because it looks fine.
I don't want to use for an input and a searchbutton the
#using (Html.BeginForm("...
Can I control the html tags without this from my controller. For example here is my index.cshtml
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<div class="row">
<h2>Suche</h2>
<div id="custom-search-input">
<div class="input-group col-md-12">
<input type="text" class=" search-query form-control" placeholder="Search" />
<span class="input-group-btn">
<button class="btn btn-danger" type="button">
<span class=" glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</div>
</div>
</div>
I want if I click on the Searchbutton I get a message with the text from the inputfield.
Here is the Controller:
public ActionResult Search(string value)
{
//listofUsers is a arraylist of all users that found^^
return View(listofUsers);
}
How I can do this? :)
Add a div to show the result:
<div id="custom-search-input">
<div class="input-group col-md-12">
<input type="text" class=" search-query form-control" placeholder="Search" />
<span class="input-group-btn">
<button class="btn btn-danger" type="button">
<span class=" glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</div>
<div class="custom-search-result"></div>
Then in a script tag or a linked js file:
$(document).ready(function () {
$('.custom-search-input').each(function () {
var sinput = $(this);
var svalue = sinput.find('input');
var sresult = sinput.next('.custom-search-result');
sinput.find('button').click(function () {
$.ajax({
url: '/ControllerName/Search?value=' + svalue.val(),
type: 'GET'
}).done(function (result) {
sresult.html(result);
});
});
});
});
This is a basic example with no error handling.
First I highly recommend reading Philip Walton (Google) - Decoupling your HTML, CSS and Javascript, it's extremely good.
Here how I would use MVC to it's full potential.
Model:
// Extensible Programming
// Using a string limits additional features
// Future proofing via a class that takes 2 minutes to create
public class GlobalSearch
{
public string SearchTerms { get; set; }
}
View:
#Model GlobalSearch
<div class="container">
<div class="row">
<h2>Suche</h2>
<div id="custom-search-input">
#using (Html.BeginForm("Search"))
{
<div class="input-group col-md-12">
#Html.TextBoxFor(m => m.SearchTerms, new {
#class="search-query form-control",
placeholder="Search" })
<span class="input-group-btn">
<button class="btn btn-danger" type="button">
<span class=" glyphicon glyphicon-search js-form-submit"></span>
</button>
</span>
</div>
}
</div>
</div>
</div>
Controller:
// Strongly Typed Class is Returned
public ActionResult Search(GlobalSearch search)
{
return View(listofUsers);
}
The following script will require this fantastic script called form2js which correctly converts any strongly-typed forms generated by MVC (arrays, lists etc) into Json that will be ModelBinded correctly.
$(document).ready(function() {
('.js-form-submit').on('click', function() {
var $form = $(this).closest('form');
var json = form2js($form);
var ajaxSettings = {
url: $form.attr('action'),
type: $form.attr('method'),
data: json,
contentType: "application/json",
}
$.ajax(ajaxSettings)
.done()
.always()
.fail();
});
});
Of course this could be easily abstract into it's own javascript class/namespace that returns the promise and reusable on any form that simply has a button with the class js-form-submit instead of continually rewriting $.ajax over and over again each time for different forms.