I'm trying to bind the Bootstrap 3 Datetimepicker to to my ASP.NET Core model using the MVC tag helper like this:
<div class='input-group date' id='datetimepicker1'>
<input asp-for="Observation.ObservationDateTime" type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
I am hitting a problem initialising the control. The following does not work:
1) in the Razor section scripts like this:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker();
});
</script>
}
Doing this initialises the datetimepicker, but without the model value. How do I get it to work but with the model value as the initial value?
Include your date field in the view like this:
#Html.EditorFor(model => model.MyDate, new { htmlAttributes = new { #class = "my-date" } })
And initialize it in your JavaScript like this (I am using jQuery)
$(document).ready(function () {
// get date from MyDate input field
var date = $(".my-date").val();
// use current date as default, if input is empty
if (!date) {
date = new Date();
}
$('.my-date').datetimepicker({
format: 'YYYY/MM/DD',
date: date
});
});
Please note that I am using 'my-date' class as input selector, you may want to select it differently... and obviously you need to include the the bootstrap library...
As a side note, it is best practice to put your script in an external file: why should I avoid inline Scripts
Related
I am building an Asp.Net Core web application using Razor.
The intended audience for this app will be using it on tablets.
Part of the application consists of several pages/forms that will require user signatures.
We could retrieve an image of a user's signature and display that on demand in the web page.
Is it possible to be more interactive and allow users to "sign" the form/page within the browser? Are there any 3rd party control libraries that would support this functionality?
I pretty sure this can be done on native applications, but can I achieve this through Asp.Net Core?
I found signature_pad in github, and it works for me.
You can take a look at the screenshots of my test steps first, and I will add the test code at the bottom.
Test Code
1. signature.cshtml
#*
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
*#
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad#2.3.2/dist/signature_pad.min.js"></script>
<form method="POST">
<p>
<canvas width="500" height="400" id="signature"
style="border:1px solid black"></canvas><br>
<button type="button" id="accept"
class="btn btn-primary">
Accept signature
</button>
<button type="submit" id="save"
class="btn btn-primary">
Save
</button><br>
<img width="500" height="400" id="savetarget"
style="border:1px solid black"><br>
<input id="SignatureDataUrl" type="text">
</p>
</form>
<script>
$(function () {
var canvas = document.querySelector('#signature');
var pad = new SignaturePad(canvas);
$('#accept').click(function () {
var data = pad.toDataURL();
$('#savetarget').attr('src', data);
$('#SignatureDataUrl').val(data);
pad.off();
});
$('#save').click(function () {
$.ajax({
url: "/ForTest/get_signature",
type: "POST",
data: { base64png:$('#SignatureDataUrl').val()},
success: function (data) {
console.log("success");
},
error: function (hata, ajaxoptions, throwerror) {
alert("failed");
}
});
});
});
</script>
2. C# code
[HttpPost]
public string get_signature(string base64png) {
var dataUri = base64png;//"data:image/png;base64,iVBORw0K...";
var encodedImage = dataUri.Split(',')[1];
var decodedImage = Convert.FromBase64String(encodedImage);
System.IO.File.WriteAllBytes("signature_pic/"+DateTime.Now.ToString("yyyyMMddHHmmss")+"signature.png", decodedImage);
return "ok";
}
Tips
If you want test my code, you need create signature_pic folder like me.
When my form is submitted I wish to get an input value:
<input type="text" id="name">
I know I can use form input bindings to update the values to a variable, but how can I just do this on submit. I currently have:
<form v-on:submit.prevent="getFormValues">
But how can I get the value inside of the getFormValues method?
Also, side question, is there any benefit to doing it on submit rather than updating variable when user enters the data via binding?
The form submit action emits a submit event, which provides you with the event target, among other things.
The submit event's target is an HTMLFormElement, which has an elements property. See this MDN link for how to iterate over, or access specific elements by name or index.
If you add a name property to your input, you can access the field like this in your form submit handler:
<form #submit.prevent="getFormValues">
<input type="text" name="name">
</form>
new Vue({
el: '#app',
data: {
name: ''
},
methods: {
getFormValues (submitEvent) {
this.name = submitEvent.target.elements.name.value
}
}
}
As to why you'd want to do this: HTML forms already provide helpful logic like disabling the submit action when a form is not valid, which I prefer not to re-implement in Javascript. So, if I find myself generating a list of items that require a small amount of input before performing an action (like selecting the number of items you'd like to add to a cart), I can put a form in each item, use the native form validation, and then grab the value off of the target form coming in from the submit action.
You should use model binding, especially here as mentioned by Schlangguru in his response.
However, there are other techniques that you can use, like normal Javascript or references. But I really don't see why you would want to do that instead of model binding, it makes no sense to me:
<div id="app">
<form>
<input type="text" ref="my_input">
<button #click.prevent="getFormValues()">Get values</button>
</form>
Output: {{ output }}
</div>
As you see, I put ref="my_input" to get the input DOM element:
new Vue({
el: '#app',
data: {
output: ''
},
methods: {
getFormValues () {
this.output = this.$refs.my_input.value
}
}
})
I made a small jsFiddle if you want to try it out: https://jsfiddle.net/sh70oe4n/
But once again, my response is far from something you could call "good practice"
You have to define a model for your input.
<input type="text" id="name" v-model="name">
Then you you can access the value with
this.name inside your getFormValues method.
This is at least how they do it in the official TodoMVC example: https://v2.vuejs.org/v2/examples/todomvc.html (See v-model="newTodo" in HTML and addTodo() in JS)
Please see below for sample solution, I combined the use of v-model and "submitEvent" i.e. <input type="submit" value="Submit">. Used submitEvent to benefit from the built in form validation.
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<form #submit.prevent="getFormValues">
<div class="form-group">
<input type="email" class="form-control form-control-user"
v-model="exampleInputEmail"
placeholder="Enter Email Address...">
</div>
<div class="form-group">
<input type="password" class="form-control"
v-model="exampleInputPassword" placeholder="Password"> </div>
<input type="submit" value="Submit">
</form>
</div>
<script>
const vm = new Vue({
el: '#app',
methods: {
getFormValues (submitEvent) {
alert("Email: "+this.exampleInputEmail+" "+"Password: "+this.exampleInputPassword);
}
}
});
</script>
</body>
</html>
The other answers suggest assembling your json POST body from input or model values, one by one. This is fine, but you also have the option of grabbing the whole FormData of your form and whopping it off to the server in one hit. The following working example uses Vue 3 with Axios, typescript, the composition API and setup, but the same trick will work anywhere.
I like this method because there's less handling. If you're old skool, you can specify the endpoint and the encoding type directly on the form tag.
You'll note that we grab the form from the submit event, so there's no ref, and no document.getElementById(), the horror.
I've left the console.log() there to show that you need the spread operator to see what's inside your FormData before you send it.
<template>
<form #submit.prevent="formOnSubmit">
<input type="file" name="aGrid" />
<input type="text" name="aMessage" />
<input type="submit" />
</form>
</template>
<script setup lang="ts">
import axiosClient from '../../stores/http-common';
const formOnSubmit = (event: SubmitEvent) => {
const formData = new FormData(event.target as HTMLFormElement);
console.log({...formData});
axiosClient.post(`api/my-endpoint`, formData, {
headers: {
"Content-Type": "multipart/form-data",
}
})
}
</script>
In my ViewModel for my MVC4 application I have some code to get names from an ajax call and populate a simple control within my page, which is using Bootstrap 3. As you can see below I have a hard-coded array which works perfectly. With the ajax call, I see the data in the UI but it does not update my control and I have NO idea why. I have verified the data exists and I have also tried setting self.Names = ko.observableArray within the ajax call. Is there a simple reason why? As I said I see the data within my form in both scenarios but I am not seeing the update I expect.
$(document).ready(function () {
function ViewModel() {
//Make the self as 'this' reference
var self = this;
//Declare observable which will be bind with UI
self.Name = ko.observable("");
var Names = {
Name: self.Name
};
self.Name = ko.observable();
//self.Names = ko.observableArray([{ Name: "Brian" }, { Name: "Jesse" }, { Name: "James" }]);
self.Names = ko.observableArray(); // Contains the list of Names
// Initialize the view-model
$.ajax({
url: '#Url.Action("GetNames", "Home")',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
self.Names(data); //Put the response in ObservableArray
}
});
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
});
Here is the Response from the body via the ajax call:
[{"Id":1,"Name":"Brian"},{"Id":2,"Name":"Jesse"},{"Id":3,"Name":"James"}]
My HTML
<p>Current selection is <span data-bind="text:Name"></span></p>
<div class="container">
<div class="col-sm-7 well">
<form class="form-inline" action="#" method="get">
<div class="input-group col-sm-8">
<input class="form-control" placeholder="Work Section" name="q" type="text">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Select <span class="caret"></span></button>
<ul class="dropdown-menu" data-bind="foreach: Names">
<li class="dropdown">
</li>
</ul>
<input name="category" class="category" type="hidden">
</div>
</div>
Probably because the data coming in is either not structured the same as your bindings or observables are not set/updated properly. If they aren't observed then they wont update.
I'm not 100% sure on this, but I think you have to use the observable array functions in order for observers (UI elements bound to the array or its contents) to actually get updated. Basing this on a section from the observable array documentation on the knockout site:
2.For functions that modify the contents of the array, such as push and splice, KO’s methods automatically trigger the dependency tracking
mechanism so that all registered listeners are notified of the change,
and your UI is automatically updated.
Depending on the json, one way you might fix this is clear the observable array and reload it with data elements converted to observables:
self.Names.removeAll();
var newName = null;
for (var idx = 0; idx < data.length; idx++) {
newName = data[idx];
newName.Id = ko.observable(newName.Id);
newName.Name = ko.observable(newName.Name);
self.Names.push(newName);
}
On your html. the click function is using the Name property of the selected array element as the parameter. you don't want this, you want the latest value. So change this:
click: function() {$root.Name(Name);}
to this
//notice the new parenthesis after Name.
click: function() {$root.Name(Name());}
I need to use datepicker on an EditorFor field.
My code for the view is:
<div class="editor-label">
#Html.Label("birthDate","birthDate")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.birthDate, new { #class = "datepicker"})
#Html.ValidationMessageFor(model => model.birthDate)
</div>
The script:
<script type="text/javascript">
$(document).ready(
function () {
$('.datepicker').datepicker({
changeMonth: true,
changeYear: true,
minDate: "-99Y",
dateFormat: "dd/mm/yyyy"
});
});
I can't make it work. I have the following error: Microsoft JScript runtime error: Object doesn't support this property or method 'datepicker'.
I have already loaded jQuery before I try to call the function.
#Html.EditorFor(model => model.birthDate, new { #class = "datepicker"})
Will not work, because "EditorFor" will not accept (pass along to the DOM) a Class.
So, you need to use "TextBoxFor" which will accept a Class or better yet, create an EditorTemplate to handle any field that is a DateTime and add the class there. This link details how to make the Template.
Once you have the Class working (can be verified in the source code), the rest is just referencing the jquery code. I use
#Styles.Render("~/Content/themes/base/css")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
and activating it with the script you already have. Watch that the name '.datepicker' is Exactly the same as the Class.
EditorFor accept a class for me
#Html.EditorFor(model => model.Birthday, new { htmlAttributes = new { #class = "datepicker",#Name="birthday",#PlaceHolder = "mm/dd/yyyy" } })
Html markup
class="datepicker text-box single-line"
The Class is work.
I had a simmilar issue, solved by using the HTML5 functionality, that has support for datetime, in your example I would suggest trying somthing like this:
<div class="editor-label">
#Html.Label("birthDate","birthDate")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.birthDate, new { #class = "datepicker", type="datetime-local"})
#Html.ValidationMessageFor(model => model.birthDate)
</div>
the only thing that I addes is a type ="datetime-local"
I am trying to use knockout js in my project so I tried the simple Hello World example but i couldnt get it to work. I created a new MVC4 project and just copy do a simple binding below is my code
<script src="~/Scripts/knockout-2.1.0.js" type="text/javascript"></script>
<script type="text/javascript">
// Here's my data model
var viewModel = function (first, last) {
this.firstName = ko.observable(first);
this.lastName = ko.observable(last);
//this.fullName = ko.computed(function () {
// Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
//return this.firstName() + " " + this.lastName();
//}, this);
};
$(document).ready(function() {
ko.applyBindings(new viewModel("Planet", "Earth")); // This makes Knockout get to work
});
</script>
<div class="liveExample">
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
#*<h2>Hello, <span data-bind='text: fullName'> </span>!</h2>*#
</div>
Basically it will just display the value of the model on a textbox.
I already referenced the knockout.js in my project but it does not work
I also added the knockout js in my BundleConfig.cs
bundles.Add(new ScriptBundle("~/bundles/knockout").Include("~/Scripts/knockout-2.1.0.js"));
I didnt work
If you are using MVC, use the scripts section to declare your JS. This will move the declarations to the bottom of the HTML page, letting the HTML render first. Here's my version of your code that worked first time out of the box:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div class="liveExample">
<p>First name:
<input data-bind="value: firstName" /></p>
<p>Last name:
<input data-bind="value: lastName" /></p>
#*<h2>Hello, <span data-bind='text: fullName'> </span>!</h2>*#
</div>
#section scripts {
<script src="~/Scripts/knockout-2.2.1.js"></script>
<script type="text/javascript">
var viewModel = function (firstName, lastName) {
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
};
$(function () {
ko.applyBindings(new viewModel("Planet", "Earth"));
});
</script>
}
try putting knockout in the of your document. Without any error messages the only thing I can say is I ran into a similar problem and that was the fix for me.
My example was driving me crazy because it worked in fiddle but not in MVC, I mentioned it to a designer friend of mine and he said it made since to him, basically that knockout needed to be fully downloaded before the page began to render.
Hope this helps