Showing Error Value Cannot be null. While Adding Task from Gantt Chart for ASP.NET MVC with dhtmlxGantt.
An exception of type ‘System.ArgumentNullException’ occurred in mscorlib.dll but was not handled in user code.
Additional Information :Value cannot be null
LINK : dhtmlx Link for Gantt Chart
Heres My Code :
public static List<GanttRequest> Parse(FormCollection form, string ganttMode)
{
// save current culture and change it to InvariantCulture for data parsing
var currentCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
var dataActions = new List<GanttRequest>();
var prefixes = form["ids"].Split(',');
foreach (var prefix in prefixes)
{
var request = new GanttRequest();
// lambda expression for form data parsing
Func<string, string> parse = x => form[String.Format("{0}_{1}", prefix, x)];
request.Mode = (GanttMode)Enum.Parse(typeof(GanttMode), ganttMode, true);
request.Action = (GanttAction)Enum.Parse(typeof(GanttAction), parse("!nativeeditor_status"), true);
request.SourceId = Int64.Parse(parse("id"));
// parse gantt task
if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Tasks)
{
//--HERE SHOWING ERROR VALUE CANNOT BE NULL--//
request.UpdatedTask = new GanttTasks()
{
GanttTaskId = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0,
Text = parse("text"),
StartDate = DateTime.Parse(parse("start_date")),
Duration = Int32.Parse(parse("duration")),
Progress = Decimal.Parse(parse("progress")),
ParentId = (parse("parent") != "0") ? Int32.Parse(parse("parent")) : (int?)null,
SortOrder = (parse("order") != null) ? Int32.Parse(parse("order")) : 0,
Type = parse("type")
};
}
// parse gantt link
else if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Links)
{
request.UpdatedLink = new GanttLinks()
{
GanttLinkId = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0,
SourceTaskId = Int32.Parse(parse("source")),
TargetTaskId = Int32.Parse(parse("target")),
Type = parse("type")
};
}
dataActions.Add(request);
}
// return current culture back
Thread.CurrentThread.CurrentCulture = currentCulture;
return dataActions;
}
}
I Referred to link provided above and done as stated. But while adding value its shows value cannot be null.
Try setting a default value for task.progress on the client side,
JS:
gantt.attachEvent("onTaskCreated", function(task){
task.progress = 0;
return true;
});
Client doesn't set the default value for progress property of a newly created task, so when you insert task on a backend progress value is null.
And since backend code doesn't validate the value, probably the error fires on this line:
Progress = Decimal.Parse(parse("progress")),
Setting a default value on the client, as shown above, or checking for null on the server should fix the issue
I'm attempting to build a speed test app for our customers connecting to our e-labs. I want to test their download speed in Mbps.
The logic I came up with is; upon click event, record the startTime, make an ajax call to a FileResult controller to return a 2.67 mb jpg file back to the client. Upon 'success', record the endTime, subtract the two time stamps, then call a different controller to finish off some logic and record the results to the db where I then return the view to show the results.
I'm hosting on an Azure Db server in region where I live. My results are 1 Mbps, which seems slow compared to speedtest.net where I receive 15 mbps selecting a server in the same region.
I'm wondering if this approach is botched? I'm still working through the basics so the try catch's etc aren't implemented.
Script in my Page:
<script>
$(document).ready(function () {
$("#downloadFile").click(function () {
var start = Date.now();
var end = null;
var totalSeconds = 0.00;
$.ajax({
url: "/Home/DownloadTest",
success: function (data) {
end = Date.now();
//alert(start + " " + end);
totalSeconds = (end - start) / 1000;
window.location.href = "/Home/DownloadResults?totalSeconds="+totalSeconds;
}
});
});
});
</script>
FileResult Controller
//Download File
public FileResult DownloadTest()
{
string directoryPath = Server.MapPath("~/TestFile/2point67mb.jpg");
string fileName = "DownloadTest.jpg";
return File(directoryPath, "image/jpeg", fileName);
}
View Controller
//Download Results
public ActionResult DownloadResults(string totalSeconds)
{
double totalSecs = Convert.ToDouble(totalSeconds);
SpeedTest Test = new SpeedTest();
Services.IPAddress ip = new Services.IPAddress();
var clientIP = ip.GetIPAddress();
string[] IPAddresses = clientIP.Split(':');
Test.Address = IPAddresses[0];
double fileSize = 2.67; //Size of File in MB.
double speed = 0.00;
speed = Math.Round(fileSize / totalSecs);
Test.ResponseTime = string.Format("{0} Mbps", speed);
Test.Status = "Success";
Test.UserId = User.Identity.GetUserId();
Test.TestDate = DateTime.Now;
db.SpeedTest.Add(Test);
db.SaveChanges();
return View(Test);
}
I have a kendo datetimepicker control and when a user manually types in an incorrect format missing the colon in the time, the validation does not catch this and in the MVC controller, the models property has a null date/time.
My client side validation is able to parse 21/01/2015 1230 but by the time it reaches the model in the controller server side its null, as it cannot map and parse the datetime.
Some console.log output of the value input and kendo.parseDate's effort.
21/01/2015 0000
Wed Jan 21 2015 00:00:00 GMT+0000 (GMT Standard Time)
Here's my client-side validation below.
So how can I force the validation to work client-side?
$("#accidentForm").kendoValidator({
rules: {
date: function (input) {
if (input.is("[name=Accident.IncidentDate]")) {
console.log(input.val());
var d = kendo.parseDate(input.val());
console.log(d);
return d instanceof Date;
}
return true;
}
},
messages: {
customRuleDateTimePick: "Incident date time format incorrect."
}
});
// attach a validator to the container and get a reference
var validatable = $("#accidentForm").kendoValidator().data("kendoValidator");
$("#btnSave").click(function () {
//validate the input elements and check if there are any errors
if (validatable.validate() === false) {
// get the errors and write them out to the "errors" html container
var errors = validatable.errors();
$(errors).each(function () {
$("#errors").html(this);
});
return false;
}
return true;
});
OK specifying a format and culture parameter on the kendo.parseDate seems to help and stops the post to the server until a valid date AND time is input.
$("#accidentForm").kendoValidator({
rules: {
date: function (input) {
if (input.is("[name=Accident.IncidentDate]")) {
var d = kendo.parseDate(input.val(), ["dd/MM/yyyy HH:mm"], 'en-GB');
return d instanceof Date;
}
return true;
}
},
messages: {
date: "Incident date time format incorrect."
}
});
Another alternative, simpler I think, was to extend the validator methods for date types. This then picked up the MVC error message attributes, so I get to use the right resource.resx file rather than hard code my error text.
<script type="text/javascript">
$(function () {
$.validator.methods.date = function (value, element) {
// Custom validation required for DateTimePicker, so use kendo.parseDate as it works better than jquery unobtrusive validation.
return this.optional(element) || kendo.parseDate(value, ["dd/MM/yyyy HH:mm"], 'en-GB') !== null;
}
});
</script>
As of now (Dojo 1.9.2) I haven't been able to find a Dojo autocomplete widget that would satisfy all of the following (typical) requirements:
Only executes a query to the server when a predefined number of characters have been entered (without this, big datasets should not be queried)
Does not require a full REST service on the server, only a URL which can be parametrized with a search term and simply returns JSON objects containing an ID and a label to display (so the data-query to the database can be limited just to the required data fields, not loading full data-entities and use only one field thereafter)
Has a configurable time-delay between the key-releases and the start of the server-query (without this excessive number of queries are fired against the server)
Capable of recognizing when there is no need for a new server-query (since the previously executed query is more generic than the current one would be).
Dropdown-stlye (has GUI elements indicating that this is a selector field)
I have created a draft solution (see below), please advise if you have a simpler, better solution to the above requirements with Dojo > 1.9.
The AutoComplete widget as a Dojo AMD module (placed into /gefc/dijit/AutoComplete.js according to AMD rules):
//
// AutoComplete style widget which works together with an ItemFileReadStore
//
// It will re-query the server whenever necessary.
//
define([
"dojo/_base/declare",
"dijit/form/FilteringSelect"
],
function(declare, _FilteringSelect) {
return declare(
[_FilteringSelect], {
// minimum number of input characters to trigger search
minKeyCount: 2,
// the term for which we have queried the server for the last time
lastServerQueryTerm: null,
// The query URL which will be set on the store when a server query
// is needed
queryURL: null,
//------------------------------------------------------------------------
postCreate: function() {
this.inherited(arguments);
// Setting defaults
if (this.searchDelay == null)
this.searchDelay = 500;
if (this.searchAttr == null)
this.searchAttr = "label";
if (this.autoComplete == null)
this.autoComplete = true;
if (this.minKeyCount == null)
this.minKeyCount = 2;
},
escapeRegExp: function (str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
replaceAll: function (find, replace, str) {
return str.replace(new RegExp(this.escapeRegExp(find), 'g'), replace);
},
startsWith: function (longStr, shortStr) {
return (longStr.match("^" + shortStr) == shortStr)
},
// override search method, count the input length
_startSearch: function (/*String*/ key) {
// If there is not enough text entered, we won't start querying
if (!key || key.length < this.minKeyCount) {
this.closeDropDown();
return;
}
// Deciding if the server needs to be queried
var serverQueryNeeded = false;
if (this.lastServerQueryTerm == null)
serverQueryNeeded = true;
else if (!this.startsWith(key, this.lastServerQueryTerm)) {
// the key does not start with the server queryterm
serverQueryNeeded = true;
}
if (serverQueryNeeded) {
// Creating a query url templated with the autocomplete term
var url = this.replaceAll('${autoCompleteTerm}', key, this.queryURL);
this.store.url = url
// We need to close the store in order to allow the FilteringSelect
// to re-open it with the new query term
this.store.close();
this.lastServerQueryTerm = key;
}
// Calling the super start search
this.inherited(arguments);
}
}
);
});
Notes:
I included some string functions to make it standalone, these should go to their proper places in your JS library.
The JavaScript embedded into the page which uses teh AutoComplete widget:
require([
"dojo/ready",
"dojo/data/ItemFileReadStore",
"gefc/dijit/AutoComplete",
"dojo/parser"
],
function(ready, ItemFileReadStore, AutoComplete) {
ready(function() {
// The initially displayed data (current value, possibly null)
// This makes it possible that the widget does not fire a query against
// the server immediately after initialization for getting a label for
// its current value
var dt = null;
<g:if test="${tenantInstance.technicalContact != null}">
dt = {identifier:"id", items:[
{id: "${tenantInstance.technicalContact.id}",
label:"${tenantInstance.technicalContact.name}"
}
]};
</g:if>
// If there is no current value, this will have no data
var partnerStore = new ItemFileReadStore(
{ data: dt,
urlPreventCache: true,
clearOnClose: true
}
);
var partnerSelect = new AutoComplete({
id: "technicalContactAC",
name: "technicalContact.id",
value: "${tenantInstance?.technicalContact?.id}",
displayValue: "${tenantInstance?.technicalContact?.name}",
queryURL: '<g:createLink controller="partner"
action="listForAutoComplete"
absolute="true"/>?term=\$\{autoCompleteTerm\}',
store: partnerStore,
searchAttr: "label",
autoComplete: true
},
"technicalContactAC"
);
})
})
Notes:
This is not standalone JavaScript, but generated with Grails on the server side, thus you see <g:if... and other server-side markup in the code). Replace those sections with your own markup.
<g:createLink will result in something like this after server-side page generation: /Limes/partner/listForAutoComplete?term=${autoCompleteTerm}
As of dojo 1.9, I would start by recommending that you replace your ItemFileReadStore by a store from the dojo/store package.
Then, I think dijit/form/FilteringSelect already has the features you need.
Given your requirement to avoid a server round-trip at the initial page startup, I would setup 2 different stores :
a dojo/store/Memory that would handle your initial data.
a dojo/store/JsonRest that queries your controller on subsequent requests.
Then, to avoid querying the server at each keystroke, set the FilteringSelect's intermediateChanges property to false, and implement your logic in the onChange extension point.
For the requirement of triggering the server call after a delay, implement that in the onChange as well. In the following example I did a simple setTimeout, but you should consider writing a better debounce method. See this blog post and the utility functions of dgrid.
I would do this in your GSP page :
require(["dojo/store/Memory", "dojo/store/JsonRest", "dijit/form/FilteringSelect", "dojo/_base/lang"],
function(Memory, JsonRest, FilteringSelect, lang) {
var initialPartnerStore = undefined;
<g:if test="${tenantInstance.technicalContact != null}">
dt = {identifier:"id", items:[
{id: "${tenantInstance.technicalContact.id}",
label:"${tenantInstance.technicalContact.name}"
}
]};
initialPartnerStore = new Memory({
data : dt
});
</g:if>
var partnerStore = new JsonRest({
target : '<g:createLink controller="partner" action="listForAutoComplete" absolute="true"/>',
});
var queryDelay = 500;
var select = new FilteringSelect({
id: "technicalContactAC",
name: "technicalContact.id",
value: "${tenantInstance?.technicalContact?.id}",
displayValue: "${tenantInstance?.technicalContact?.name}",
store: initialPartnerStore ? initialPartnerStore : partnerStore,
query : { term : ${autoCompleteTerm} },
searchAttr: "label",
autoComplete: true,
intermediateChanges : false,
onChange : function(newValue) {
// Change to the JsonRest store to query the server
if (this.store !== partnerStore) {
this.set("store", partnerStore);
}
// Only query after your desired delay
setTimeout(lang.hitch(this, function(){
this.set('query', { term : newValue }
}), queryDelay);
}
}).startup();
});
This code is untested, but you get the idea...
I am using ASP.NET MVC 4 with Web API.
I want to be able to download a csv file on click on a button.
Below is my jquery call to the api 'exportfruit'
function downloadFile(){
var data = {
StartDate: this.model.get('StartDate'),
Name: this.model.get('Name')
};
var form = document.createElement('form');
form.action = 'api/fruitapi/exportFruit';
form.method = 'POST';
form.style.display = 'none';
for (i in data) {
if (data[i] != "") {
var inputElement = document.createElement('textarea');
inputElement.name = i;
inputElement.value = data[i];
form.appendChild(inputElement);
}
}
document.body.appendChild(form);
form.submit();
}
and my web api action is as below
[ActionName("ExportFruit")]
public HttpResponseMessage PostExportFruit(SomeModel model)
{
// for now i am just testing the value returned from model.
string csv = "some data from db";
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StringContent(csv);
//a text file is actually an octet-stream (pdf, etc)
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
//we used attachment to force download
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "testfile.csv";
return result;
}
now the problem I am facing is that I am passing date as 'dd/mm/yyyy' but in the web api action it converts date into 'mm/dd/yyyy'
So for example,
if I have a date like
1/2/2012 this is converted to 2/1/2012
if 22/10/2012 (todays date) is converted to 01/01/0001
How do I fix this ?
I had similar problem when passing json data which I fixed by using this But I have no idea on how to go about on this one.
Please help me on this, as there is hardly any content available on internet for this.
You can change StartDate's type, Datetime to String.
Then, post to action, ParseExact string to datetime
DateTime startDate = DateTime.ParseExact(model.StartDate, "dd/MM/yyyy", null);
DateTime endDate = DateTime.ParseExact(model.EndDate, "dd/MM/yyyy", null);