Input 'number' for Doubles in Asp.Net? - c#

If in an Asp.Net Core application you have a variable type Int, this generates an input div for the model that only lets you write numbers, and lets you increase the number there with some buttons:
http://imgfz.com/i/HJuRvmS.png
<div class="form-group">
<label asp-for="CantidadperoDouble" class="control-label"></label>
<input asp-for="CantidadperoDouble" class="form-control" />
<span asp-validation-for="CantidadperoDouble" class="text-danger"></span>
</div>
However, if the original variable from your model is a Double, the input div that the code generates is the same which is used for the Strings, letting you write words there, although the model doesn't accept those values.
Is there a way to change that?
I would like my input div for Doubles to be the same as the used for Ints, but with the buttons that lets you increase the Integer part of the number, but still have the comma/dot at the end, letting you write the decimal part / fractional part, manually.

You could use JQuery to allow only numeric and one Decimal Point input in the Textbox.
Check the following code (from your description and the tag list, I assume you are create a Asp.net core Razor Application):
Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
Create.cshtml.cs
public class CreateModel : PageModel
{
[BindProperty]
public Product Product { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var data = Product;
return RedirectToPage("./Index");
}
}
Create.cshtml:
#page
#model RazorSample.Pages.CreateModel
<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="Product.Name" class="control-label"></label>
<input asp-for="Product.Name" class="form-control" />
<span asp-validation-for="Product.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.Price" class="control-label"></label>
<input asp-for="Product.Price" class="form-control" />
<span asp-validation-for="Product.Price" 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 () {
$("#Product_Price").on("keypress keyup blur", function (event) {
$(this).val($(this).val().replace(/[^0-9\.]/g, ''));
if ((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57)) {
event.preventDefault();
}
});
})
</script>
}
The output as below (it allows only numeric and one Decimal Point):

Related

Bind Image src to IFormFile asp.net core

I am starting to learn Asp.Net core. I have a model class which is saved to the database as follows,
public class BlogData
{
public int Id { get; set; }
public string BlogTitle { get; set; }
public string BlogContent { get; set; }
public DateTime PostedDateTime { get; set; }
public byte[] ImageData { get; set; }
}
Now I created an other class as follows,
public class BlogDataWithStringImage : BlogData
{
private IFormFile _actualImage;
public IFormFile ActualImage
{
get => _actualImage;
set
{
if (value != null)
{
using var fs1 = value.OpenReadStream();
using var ms1 = new MemoryStream();
fs1.CopyTo(ms1);
ImageData = ms1.ToArray();
}
_actualImage = value;
}
}
}
Problem: My intention is when some value gets bound to the ActualImage property, I want to convert it to byte Array. Problem is, when I keep a breakpoint on the set block of (ActualImage property in BlogDataWithStringImage class) the breakpoint never gets hit even when I choose the image on the View. Please help.
#model KGSBlog.Models.BlogDataWithStringImage
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BlogData</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="BlogTitle" class="control-label"></label>
<input asp-for="BlogTitle" class="form-control" />
<span asp-validation-for="BlogTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BlogContent" class="control-label"></label>
<input asp-for="BlogContent" class="form-control" />
<span asp-validation-for="BlogContent" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PostedDateTime" class="control-label"></label>
<input asp-for="PostedDateTime" class="form-control" />
<span asp-validation-for="PostedDateTime" class="text-danger"></span>
</div>
<div class="col-md-12">
<div class="form-group">
<label asp-for="ActualImage" class="control-label"></label>
<div class="custom-file">
<input asp-for="ActualImage" class="custom-file-input" id="customFile">
<label class="custom-file-label" for="customFile">Choose file</label>#*Choosing an image never sets the ActualImage property*#
</div>
<span asp-validation-for="ActualImage" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Ok I figured it out. I was refering to this post in case if it helps someone, Image upload asp.net core
I added the following script inside the Scripts section as in the above post and it works fine.
<script type="text/javascript">
$(".custom-file-input").on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
</script>

Populate DropDown List in MVC App that Consumes a Web API

I have a simple MVC app that is consuming a web api via REST. The controller in the MVC app makes http calls to the web api to populate views within the MVC app with razor syntax.
I am trying to figure out how to populate a drop down list on one of the 'create' actions. I'm currently just using the scaffolded page:
#model ComicBookInventory.Shared.ComicBookWithAuthorsAndCharactersViewModel
#{
ViewData["Title"] = "CreateComicBook";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>CreateComicBook</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="CreateComicBook">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsRead" /> #Html.DisplayNameFor(model => model.IsRead)
</label>
</div>
<div class="form-group">
<label asp-for="DateRead" class="control-label"></label>
<input asp-for="DateRead" class="form-control" />
<span asp-validation-for="DateRead" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CoverUrl" class="control-label"></label>
<input asp-for="CoverUrl" class="form-control" />
<span asp-validation-for="CoverUrl" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Which gets populated from this action in the controller:
public async Task<IActionResult> CreateComicBook(ComicBookWithAuthorsAndCharactersViewModel model)
{
string uri = $"https://localhost:5001/api/comicbook/add-book/";
HttpClient client = _httpClientFactory.CreateClient(
name: "ComicBookInventory.Api");
var postTask = await client.PostAsJsonAsync<ComicBookWithAuthorsAndCharactersViewModel>(uri, model);
if (postTask.IsSuccessStatusCode)
{
return RedirectToAction("GetAllComics");
}
else
{
return View(model);
}
}
Here is the view model definition:
namespace ComicBookInventory.Shared
{
public class ComicBookWithAuthorsAndCharactersViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool IsRead { get; set; }
public DateTime? DateRead { get; set; }
public int? Rating { get; set; }
public string Genre { get; set; }
public string? CoverUrl { get; set; }
/// <summary>
/// Navigation properties
/// </summary>
/// a book can have many authors
public ICollection<string>? AuthorNames { get; set; }
public ICollection<string>? CharacterNames { get; set; }
}
}
My question is, I want to add a drop down checklist to the view, so that when I am creating a comic book, I can select Authors that currently exist in the database. The same for characters.
Here is the entire code base in case anyone is interested: https://github.com/rnemeth90/ComicBookInventoryApp
I normally try and figure things out on my own (I'm relatively new to EF Core, and have very little experience with many-to-many relationships in EF core). I have tried various things and struggled with this for most of my weekend. I feel like this should be relatively simple but cannot figure it out. Please help.
In your Code, I noticed that you used #Html.DropDownList to realzie the selector element, and using form submit to handle the data. So I did a test in my side:
#{
List<SelectListItem> listItems= new List<SelectListItem>();
listItems.Add(new SelectListItem
{
Text = "Exemplo1",
Value = "Exemplo1_v"
});
listItems.Add(new SelectListItem
{
Text = "Exemplo2",
Value = "Exemplo2_v",
Selected = true
});
listItems.Add(new SelectListItem
{
Text = "Exemplo3",
Value = "Exemplo3_v"
});
}
<form asp-action="Create">
<div class="form-group">
AuthorNames
<div class="col-md-10">
#Html.DropDownListFor(model => model.Author, listItems, "-- Select author --")
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
My model contains a public string? Author { get; set; } property, and when I click the submit button, the select value will be submitted, so when you want to pass the FullName to your controller, you need to set it as the Value of the SelectListItem.

ASP.NET Core Razor Page binding properties and preserve data in model

I have a question about model binding in razor pages .net core 3.1. Lets say I have a model such as this:
public Boolean bEmailMessages { get; set; }
public string sUserEmail { get; set; }
public Boolean bIcal { get; set; }
public Boolean bEventNotice { get; set; }
public string EPRLogin { get; set; }
public string EPRPWD { get; set; }
public string EPRLaunch { get; set; }
On one of my razor pages I use this model for only the top 4 items as inputs that are bound to those items. In my constructor I am initializing this model with data from the database. On my HTTPPOST I get the model back, but the last 3 items are null (EPRLogin, EPRPWD, and EPRLaunch) because I have another page where the user updates those items.
Is there a way to preserve the data in that model and only have the binding update the data from the page? The reason I ask, is because I have a function that I get the model from and I send it to save it to the database.
The only way I can think of doing this would be to get my model within the save method, change only the items I got back from the page model and then send my modified model to be saved.
I hope I explained this well enough...
As you said, I think the best way is to update only the first four items. Below is a work demo.
You can add an Id into your model.
Then in your pagemodel(Assuming Id=1 is the initial value):
public IActionResult OnGet()
{
Simple = _context.Simples.Find(1);
return Page();
}
public IActionResult OnPost(Simple simple)
{
_context.Simples.Attach(simple);
_context.Entry(simple).Property(x => x.bEmailMessages).IsModified = true;
_context.Entry(simple).Property(x => x.bEventNotice).IsModified = true;
_context.Entry(simple).Property(x => x.bIcal).IsModified = true;
_context.Entry(simple).Property(x => x.sUserEmail).IsModified = true;
_context.SaveChanges();
if (!ModelState.IsValid)
{
return Page();
}
return RedirectToPage("./Index");
}
}
And your page:
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Simple.Id" />
<div class="form-group">
<label asp-for="Simple.bEmailMessages" class="control-label"></label>
<input asp-for="Simple.bEmailMessages" />
<span asp-validation-for="Simple.bEmailMessages" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Simple.bEventNotice" class="control-label"></label>
<input asp-for="Simple.bEventNotice" />
<span asp-validation-for="Simple.bEventNotice" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Simple.bIcal" class="control-label"></label>
<input asp-for="Simple.bIcal" />
<span asp-validation-for="Simple.bIcal" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Simple.sUserEmail" class="control-label"></label>
<input asp-for="Simple.sUserEmail" class="form-control" />
<span asp-validation-for="Simple.sUserEmail" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
Result:

The value is not set after POST if use asp-for attribute in MVC Core 3.1

I have simple view model:
public class GogViewModel
{
[Required]
public string Name { get; set; }
public bool IsBeta { get; set; }
public string Result { get; set; }
}
and controller:
[HttpGet]
public IActionResult Index()
{
return View(new GogViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(GogViewModel model)
{
if (ModelState.IsValid)
{
model.Result = DoSomething();
}
return View(model);
}
and View:
#model GogViewModel
<div>
<form method="post">
#Html.AntiForgeryToken()
<div class="form-group row">
<label class="col-sm-2 col-form-label" asp-for="Name">Release name</label>
<div class="col-sm-10">
<input type="text" class="form-control" asp-for="Name" placeholder="paste release name here" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" asp-for="Result">Result</label>
<div class="col-sm-9">
<input type="text" class="form-control-plaintext" readonly="readonly" asp-for="Result" />
<p>#Model.Result</p> <!-- it works here means the value is printed -->
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-outline-info copy float-right" data-target="Result"><i class="far fa-copy"></i></button>
</div>
<div class="clearfix"></div>
</div>
<input type="submit" class="btn btn-success" value="Fix it" />
</form>
</div>
Somehow the result is not output after POST request.
Why ? Where's my mistake ?
It will work in the following way:
Set private setter to Result property => public string Result {get; private set;}
Add method which set value for Result: internal void SetResult(string result) { Result = result; }
In controller, call SetResult like: model.SetResult(...);
and will display in readonly input. I don't know why it works and I don't understand why the value is cleared when put it on input.
I used input with readonly attribute in order to copy to clipboard the output value with javascript.
maybe you need to add something in the input tag like value="#Model.Result"?

Put Form contents into list for MVC

I have a page containing one main form and multiple smaller duplicate forms. I am trying to post them both using one button with JQuery, and handle them in the order of: main form first then smaller forms second. However, when I attempt to access the smaller forms, I am getting data from only the last one. I was thinking that the best way to do it would be to put the smaller forms into a list, pass that to my handler, and then do a foreach loop through the list. How would I put the form information into a list before my handler uses it?
Here is my model:
public class SubmitModel
{
[BindProperty]
public Timelineinfo Timelineinfo { get; set; } //main form
[BindProperty]
public Media Medias { get; set; }//small form
}
My controller:
[HttpPost]
[Route("/Submit/Submit")]
[ValidateAntiForgeryToken] //Main form handler
public async Task<IActionResult> Submit(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in main form then submit main form to Database,
}
return View("Pages/Submit.cshtml", model);
}
[Route("/Submit/MediaAdd")]
[ValidateAntiForgeryToken] //Add media form handler
public async Task<IActionResult> MediaAdd(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in small form then submit small form to Database
}
return View("Pages/Submit.cshtml", model);
}
And a truncated version of my small form (main form looks similar but with with Submit instead of MediaAdd:
#using (Html.BeginForm("MediaAdd", "Timeline", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="container">
<div class="form-row">
<label asp-for="Medias.Blurb" class="control-label">Blurb<span class="text-danger ml-1">*</span> <span class="text-muted ml-1">Explain what is in the media</span></label>
<input asp-for="Medias.Blurb" class="form-control" />
<span asp-validation-for="Medias.Blurb" class="text-danger"></span>
</div>
<div class="form-row">
<div class="ml-3 col-4">
<label class="form-check-label">Is there any blood or gore in the video?<span class="text-danger ml-1">*</span></label>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias.Gore" class="form-check-input" value="0">
<label class="form-check-label">No</label>
</div>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias.Gore" class="form-check-input" value="1" />
<label class="form-check-label">Yes</label>
<span asp-validation-for="Medias.Gore" class="text-danger"></span>
</div>
</div>
</div>
</div>
}
Finally, how I am currently submitting my forms:
$('#submitButton').click(function () {
$('form').submit();
});
I made an example based on your requirement, you may refer to it:
Model:
public class SubmitModel
{
[BindProperty]
public Timelineinfo Timelineinfo { get; set; }
[BindProperty]
public List<Media> Medias { get; set; }
}
public class Timelineinfo
{
public int TimeId { get; set; }
public string Description { get; set; }
}
public class Media
{
public string Blurb { get; set; }
public string Gore { get; set; }
}
Main form View(Index):
#model SubmitModel
<button id="AddMedia" type="button" class="btn btn-primary">Add Media</button>
#using (Html.BeginForm("MediaAdd", "Timeline", FormMethod.Post))
{
<div class="container">
<div class="form-row">
<label asp-for="Timelineinfo.TimeId" class="control-label">TimeId</label>
<input asp-for="Timelineinfo.TimeId" class="form-control" />
</div>
<div class="form-row">
<label asp-for="Timelineinfo.Description" class="control-label">Description</label>
<input asp-for="Timelineinfo.Description" class="form-control" />
</div>
</div>
<div id="subformsArea">
</div>
<div>
<input type="submit" value="submit" class="btn btn-primary" />
</div>
}
#section scripts{
<script>
var count = 0;
$("#AddMedia").on('click', function () {
$.ajax({
url: '/Timeline/Media',
method: 'get',
data: {data : count},
success: function (result) {
$("#subformsArea").append(result);
count++;
}
})
})
</script>
}
Subform View(Partial View):
#model SubmitModel
#{
int i = ViewBag.Count;
}
<div class="container">
<div class="form-row">
<label asp-for="Medias[i].Blurb" class="control-label">Blurb<span class="text-danger ml-1">*</span> <span class="text-muted ml-1">Explain what is in the media</span></label>
<input asp-for="Medias[i].Blurb" class="form-control" />
<span asp-validation-for="Medias[i].Blurb" class="text-danger"></span>
</div>
<div class="form-row">
<div class="ml-3 col-4">
<label class="form-check-label">Is there any blood or gore in the video?<span class="text-danger ml-1">*</span></label>
<br />
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias[i].Gore" class="form-check-input" value="0">
<label class="form-check-label">No</label>
</div>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias[i].Gore" class="form-check-input" value="1" />
<label class="form-check-label">Yes</label>
<span asp-validation-for="Medias[i].Gore" class="text-danger"></span>
</div>
</div>
</div>
</div>
Controller:
public class TimelineController : Controller
{
public IActionResult Index()
{
return View();
}
[Route("/Submit/MediaAdd")]
[ValidateAntiForgeryToken] //Add media form handler
public IActionResult MediaAdd(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in small form then submit small form to Database
}
return View("Pages/Submit.cshtml", model);
}
public IActionResult Media(int data)
{
ViewBag.Count = data;
return PartialView();
}
}
Result:
This is how I would do it. The simpler the better:
<form action='myAction' method='postOrGet'>
//subform 1
<input name='inputA' subform='s1'.../>
//subform 2
<input name='inputX' subform='s2'.../>
...
<button type='submit'>submit</button>
</form>
Then you can easily separate submitted data based on the "subform" attribute (s1, s2. etc)

Categories

Resources