I'm trying to invoke a javascript function in a Blazor component, but with no success.
The error is:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not find 'FullCalendarInterop.init' ('FullCalendarInterop' was undefined).
Error: Could not find 'FullCalendarInterop.init' ('FullCalendarInterop' was undefined).
at https://localhost:7065/_framework/blazor.webassembly.js:1:328
at Array.forEach ()
at a.findFunction (https://localhost:7065/_framework/blazor.webassembly.js:1:296)
at _ (https://localhost:7065/_framework/blazor.webassembly.js:1:2437)
at https://localhost:7065/_framework/blazor.webassembly.js:1:3325
at new Promise ()
Component structure:
Razor page:
#using Microsoft.JSInterop
#inject IJSRuntime JSRuntime
protected override async Task OnAfterRenderAsync(bool firstRender)
{
var options = new CalendarOptions
{
Id = Id,
DefaultView = View,
CalendarEvents = Events.Where(r => r.Status == CalendarEventStatus.Active).ToList()
};
var dotNetObjectReference = DotNetObjectReference.Create(this);
var fullCalendarInterop = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Shared/InputCalendar.razor.js");
await fullCalendarInterop.InvokeVoidAsync("FullCalendarInterop.init", options, dotNetObjectReference);
// I already just tried^
// await JSRuntime.InvokeVoidAsync("FullCalendarInterop.init", options, DotNetObjectReference.Create(this));
await base.OnAfterRenderAsync(firstRender);}
Javascript
var FullCalendarInterop = function () {
return {
//main function to initiate the module
init: function (options, dotNetReference) {
var calendarEl = document.getElementById(options.id);
var calendar = new window.FullCalendar.Calendar(calendarEl, {
plugins: ['interaction', 'dayGrid', 'timeGrid', 'list'],
isRTL: window.KTUtil.isRTL(),
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
},
height: 800,
contentHeight: 750,
aspectRatio: 3, // see: https://fullcalendar.io/docs/aspectRatio
views: {
dayGridMonth: { buttonText: 'month' },
timeGridWeek: { buttonText: 'week' },
timeGridDay: { buttonText: 'day' },
listWeek: { buttonText: 'list' }
},
defaultView: options.defaultView,
defaultDate: options.defaultDate,
editable: true,
eventLimit: true, // allow "more" link when too many events
navLinks: true,
events: options.calendarEvents,
eventRender: function (info) {
var event = $(info.event);
var element = $(info.el);
var view = $(info.view);
if (info.event.extendedProps && info.event.extendedProps.description) {
if (element.hasClass('fc-day-grid-event')) {
element.data('content', info.event.extendedProps.description);
element.data('placement', 'top');
window.KTApp.initPopover(element);
} else if (element.hasClass('fc-time-grid-event')) {
element.find('.fc-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
} else if (element.find('.fc-list-item-title').lenght !== 0) {
element.find('.fc-list-item-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
}
}
element.find(".fc-bg").css("pointer-events", "none");
element.find(".fc-list-item-title.fc-widget-content").prepend(
"<span style='position: absolute; right: 5px;'>" +
"<button class='btn btn-icon btn-xs btn-circle btn-light' " +
"style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
"<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
element.find(".fc-content").append("<span style='position: absolute; top: 5px; right: 5px;'>" +
"<button class='btn btn-icon btn-xs btn-circle btn-light' " +
"style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
"<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
element.find("#calendar_del_" + event.id).click(function () {
var eventId = event[0]._def.defId;
var eventIdentifier = event[0]._def.extendedProps.identifier;
//Remove popover
removeContent(".popover.fade.show.bs-popover-top");
//$(".popover.fade.show.bs-popover-top").remove();
dotNetReference.invokeMethodAsync('CalendarEventDeleted', eventIdentifier);
//$("#candidate_calendar").fullCalendar('removeEvents', eventId);
});
},
viewSkeletonRender: function (info) {
var view = $(info.view);
var defaultView = view[0].type !== null ? view[0].type : "";
dotNetReference.invokeMethodAsync('SetDefaultView', defaultView);
//alert(defaultView);
}
});
calendar.render();
}
};
}
Modify your .JS file like so:
var FullCalendarInterop = function () {
return {
//main function to initiate the module
init: function (options, dotNetReference) {
... Trimmed for brevity ...
calendar.render();
}
};
}();
export { FullCalendarInterop };
I added (); to the end of your FullCalendarInterop function and then added the export. That should work for you.
As it says its a null reference error as you are not initializing JSmodule,
IJSObjectReference fullCalendarInterop;
fullCalendarInterop = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Shared/InputCalendar.razor.js");
Related
I am having issue in displaying a widget inside a modal on ASP.NET MVC platform. Currently, the widget is showing at the back of my modal.
#Html.TextBoxFor(Function(model) model.Clinic, New With {.Style = "width:300px", .id = "txtClinic"})
Widget
$.widget('custom.mcautocomplete', $.ui.autocomplete, {
_create: function () {
this._super();
this.widget().menu("option", "items", "> :not(.ui-widget-header)");
},
_renderMenu: function (ul, items) {
var self = this,
thead;
if (this.options.showHeader) {
table = $('<div class="ui-widget-header" style="width:100%"></div>');
$.each(this.options.columns, function (index, item) {
table.append('<span style="padding:0 4px;float:left;width:' + item.width + ';">' + item.name + '</span>');
});
table.append('<div style="clear: both;"></div>');
ul.append(table);
}
$.each(items, function (index, item) {
self._renderItem(ul, item);
});
},
_renderItem: function (ul, item) {
var t = '',
result = '';
var term = this.term;
$.each(this.options.columns, function (index, column) {
var value = item[column.valueField ? column.valueField : index];
t += '<span style="padding:0px 4px;float:left;width:' + column.width + ';height:13px;">' + value + '</span>'
});
var $a = '<a class="mcacAnchor">' + t + '<div style="clear: both;"></div></a>';
result = $('<li style="border-bottom: solid 1px #cccccc"></li>')
.data('ui-autocomplete-item', item)
.append($a)
.appendTo(ul);
return result;
}
});
Ajax
$("#txtClinic").mcautocomplete({
showHeader: true,
columns: [{
name: 'Code',
width: '120px',
valueField: 'ClinicCode'
}, {
name: 'Clinic Name',
width: '370px',
valueField: 'Clinic'
}
],
minLength: 3,
source: function (request, response) {
$.ajax({
cache: false,
url: '/User/GetClinicAllDetails',
type: "POST",
dataType: "json",
data: { query: request.term },
success: function (data) {
response($.map(data, function (item) {
return {
ClinicCode: item.ClinicCode,
Clinic: item.Clinic
}
}));
},
error: function (response) {
swal("", response.message, "error");
}
});
}
});
All the codes are listed in my Modal View, any ideas what could went wrong?
For anyone who is having the same issue as me.
Simply add the following line to the widget portion:
$(".ui-autocomplete.ui-front.ui-menu.ui-widget.ui-widget-content").addClass("zindex9999");
In your style:
.zindex9999{z-index:9999 !important;}
The reason of doing so is because when a modal is opened, the position is set to absolute and it neglect all the existing div, by setting z-index to 9999, it actually overwrites the absolute index as the absolute index is around 9998 (I'm not sure)
I am using google maps. And I can see the google map. But I get every this error:
SCRIPT5009: 'google' is undefined
My script is this:
var map;
if (typeof initMap == 'function') {
initMap();
}
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: { lat: 52.001321, lng: 4.374577 }
});
var geocoder = new google.maps.Geocoder();
document.getElementById('submit').addEventListener('click', function () {
geocodeAddress(geocoder, map);
});
}//end function initMap
$("document").ready(function () {
map = new google.maps.Map(document.getElementById('map'),
mapOptions);
});
//function loadScript() {
// var script = document.createElement('script');
// script.type = 'text/javascript';
// script.src = 'https://maps.googleapis.com/maps/api/js?v=3&sensor=false&callback=initMap';
// document.body.appendChild(script);
//}
//addLoadEvent(loadScript);
function toggleBounce() {
if (marker.getAnimation() !== null) {
marker.setAnimation(null);
} else {
marker.setAnimation(google.maps.Animation.BOUNCE);
}
}
var marker;
var infowindow;
function geocodeAddress(geocoder, resultsMap) {
if (typeof infowindow != 'undefined') {
infowindow.close();
}
if (typeof marker != 'undefined') {
marker.setMap(null);
}
var address = document.getElementById('address').value;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resultsMap.setCenter(results[0].geometry.location);
marker = new google.maps.Marker({
map: resultsMap,
draggable: true,
animation: google.maps.Animation.DROP,
position: results[0].geometry.location,
title: "Drag me!"
});
//document.getElementById(marker.getPosition().lat().toFixed(6)).className += "geolocation_long";
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
infowindow = new google.maps.InfoWindow({
content: '<p>Marker Location:'
+ 'lat: ' + marker.getPosition().lat().toFixed(6)
+ ', '
+ 'lng: ' + marker.getPosition().lng().toFixed(6)
+ '</p>'
});
document.getElementById("form_inp17").value = marker.getPosition().lng().toFixed(6);
document.getElementById("form_inp18").value = marker.getPosition().lat().toFixed(6);
google.maps.event.addListener(marker, 'dragend', function (event) {
if (typeof infowindow != 'undefined') {
infowindow.close();
}
infowindow = new google.maps.InfoWindow({
content: '<p>Marker Location:'
+ 'lat: ' + event.latLng.lat().toFixed(6)
+ ', '
+ 'lng: ' + event.latLng.lng().toFixed(6)
+ '</p>'
});
document.getElementById("form_inp17").value = event.latLng.lng().toFixed(6)
document.getElementById("form_inp18").value = event.latLng.lat().toFixed(6)
infowindow.open(map, marker);
});
infowindow.open(map, marker);
google.maps.event.addListener(marker, 'click', function (event) {
if (typeof infowindow != 'undefined') {
infowindow.open(map, marker);
}
});
});
}
And my html page where I refer to the google maps is like this:
#{
Layout = "~/Areas/Form/Views/Shared/_RenderLayout.cshtml";
}
#section SFSHeadSection {
#Scripts.Render("~/bundles/modernizr")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/xforms")
#Scripts.Render("~/bundles/xforms_" + ViewBag.Locale)
#if (ViewBag.Includes != null){
#Scripts.Render(ViewBag.Includes);
}
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&callback=initMap" async defer type="text/javascript"></script>
<script type="text/javascript">
$(function () {
#Html.Raw(ViewBag.onloadscript)
});
</script>
#Styles.Render("~/Content/jquery-ui.css")
#Styles.Render("~/css/Geo-picker.css")
<style type="text/css">
#Html.Raw(ViewBag.style)
</style>
}
<div>
#Html.Raw(ViewBag.FormHtml)
</div>
Thank you
and yes, I already looked at this thread:
http://stackoverflow.com/questions/11940872/script5009-google-is-undefined
In upload async file :
for example if i uploading 3 file Debug.WriteLine(info.FullName); and _db.Files.Add(New FileDto{FileName=info.Name}); repeated 50 time.means 50 record insert in database.
Why and how to avoid it?
what is my problem?
public Task<IEnumerable<FileDesc>> Post()
{
string folderName = "uploads";
string PATH = HttpContext.Current.Server.MapPath("~/" + folderName);
string rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);
if (Request.Content.IsMimeMultipartContent())
{
var streamProvider = new CustomMultipartFormDataStreamProvider(PATH);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<FileDesc>>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
Debug.WriteLine(info.FullName);//************Repeated******
_db.Files.Add(New FileDto{FileName=info.Name});//************Repeated******
return new FileDesc(info.Name, rootUrl + "/" + folderName + "/" + info.Name, info.Length / 1024);
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
[Update]
CustomMultipartFormDataStreamProvider :
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public CustomMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
var name = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? headers.ContentDisposition.FileName : "NoName";
return name.Replace("\"", string.Empty);
}
}
[Update]
$('#fileupload').fileupload({
dataType: "json",
url: "/media/upload",
limitConcurrentUploads: 1,
sequentialUploads: true,
progressInterval: 100,
maxChunkSize: 10000,
add: function (e, data) {
$('#filelistholder').removeClass('hide');
data.context = $('<div />').text(data.files[0].name).appendTo('#filelistholder');
$('<div class="progress progress-striped active"><div style="width: 0%;" class="progress-bar progress-bar-warning"></div></div>').appendTo(data.context);
$('#btnUploadAll').click(function () {
data.submit();
});
},
done: function (e, data) {
data.context.text(data.files[0].name + '... Completed');
$('<div class="progress progress-striped active"><div style="width: 100%;" class="progress-bar progress-bar-success"></div></div>').appendTo(data.context);
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#overallbar').css('width', progress + '%');
},
progress: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
data.context.find('.progress-bar').css('width', progress + '%');
}
});
Your Debug.WriteLine is in the projection function of streamProvider.FileData.Select, so it is executed as many times as there are items in streamProvider.FileData.
I am using google charts for my asp.net web application. I have successfully got the chart working in my application. But I want to know how to add animation, change the color of the column chart from code. In javascript I know it can be done using below option -
var options = {
width: 400,
height: 240,
title: 'Sample Data',
colors: ['#e0440e', '#f6c7b6'],
is3D:true
};
I tried calling the same in c# using string builder but chart itself doesnot load. Here is my code-
private void BindCourseChart()
{
//to bind course chart
DataTable dt = new DataTable();
try
{
dt = GetData_Course();
str.Append(#"<script type=text/javascript> google.load( *visualization*, *1*, {packages:[*corechart*],callback:drawChart});
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Title');
data.addColumn('number', 'Users enrolled');
**//Here I am adding role=style for customizing**
data.addColumn({type: 'string', role: 'style'});
data.addRows(" + dt.Rows.Count + ");");
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
str.Append("data.setValue( " + i + "," + 0 + "," + "'" + dt.Rows[i] ["Title"].ToString() + "');");
str.Append("data.setValue(" + i + "," + 1 + "," + dt.Rows[i]["MenteeCount"].ToString() + ") ;");
}
str.Append(" var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));");
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:#0092CB,");
str.Append("hAxis: {title: 'Course Title', titleTextStyle: {color: 'green'}}");
str.Append("}); }");
str.Append("</script>");
lt_course.Text = str.ToString().TrimEnd(',').Replace('*', '"');
}
Can anyone help me in this regard how to add colors,or animation to column charts .
Thanks in advance
If interested, I made a Google Chart class awhile back that I've used in a few projects:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Google
{
/// <summary>
/// Summary description for GoogleChart
/// </summary>
public class GoogleChart
{
// Fields
private string data = "";
private string javascript;
// Properties
public string elementId { get; set; }
public int height { get; set; }
public string title { get; set; }
public int width { get; set; }
// ChartTypes
public enum ChartType
{
BarChart,
PieChart,
LineChart,
ColumnChart,
AreaChart,
BubbleChart,
CandlestickChart,
ComboChart,
GeoChart,
ScatterChart,
SteppedAreaChart,
TableChart
}
// Methods
public GoogleChart()
{
this.title = "Google Chart";
this.width = 730;
this.height = 300;
this.elementId = "chart_div";
}
public void addColumn(string type, string columnName)
{
string data = this.data;
this.data = data + "data.addColumn('" + type + "', '" + columnName + "');";
}
public void addRow(string value)
{
this.data = this.data + "data.addRow([" + value + "]);";
}
public string generateChart(ChartType chart)
{
this.javascript = "<script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script>";
this.javascript = this.javascript + "<script type=\"text/javascript\">";
this.javascript = this.javascript + "google.load('visualization', '1.0', { 'packages': ['corechart']});";
this.javascript = this.javascript + "google.setOnLoadCallback(drawChart);";
this.javascript = this.javascript + "function drawChart() {";
this.javascript = this.javascript + "var data = new google.visualization.DataTable();";
this.javascript = this.javascript + this.data;
this.javascript = this.javascript + "var options = {";
this.javascript = this.javascript + "'title': '" + this.title + "',";
object javascript = this.javascript;
this.javascript = string.Concat(new object[] { javascript, "'width': ", this.width, ", 'height': ", this.height, "};" });
string str = this.javascript;
this.javascript = str + "var chart = new google.visualization." + chart.ToString() + "(document.getElementById('" + this.elementId + "'));";
this.javascript = this.javascript + "chart.draw(data, options);";
this.javascript = this.javascript + "} </script>";
return this.javascript;
}
}
}
You can then use it by doing the following:
private void GenerateQuickStats()
{
GoogleChart chart = new GoogleChart();
chart.title = "Quick Stats";
chart.width = 250;
chart.height = 200;
chart.addColumn("string", "Year");
chart.addColumn("number", "Value");
chart.addColumn("number", "Profit");
chart.addRow("'2014', 2000, 1000");
// asp literal
ltChart.Text = chart.generateChart(GoogleChart.ChartType.ColumnChart);
}
Since David addressed the color issue, I'll tackle the animations. Animating the chart on first draw is a bit complicated; you have to draw it once with a zero'd out data set, and then draw it again with the real data set. Here's some javascript (that you can add to your string builder, replacing the chart creation and drawing lines) that will animate the chart:
var view = new google.visualization.DataView(data);
view.setColumns([0, {
sourceColumn: 1,
calc: function () {return 0;}
}, 2]);
var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
chart.draw(data, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
});
chart.draw(view, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
[Edit - attempt at C# example]
private void BindCourseChart()
{
//to bind course chart
DataTable dt = new DataTable();
try
{
dt = GetData_Course();
str.Append(#"
<script type=text/javascript>
google.load( *visualization*, *1*, {packages:[*corechart*], callback:drawChart});
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Title');
data.addColumn('number', 'Users enrolled');
**//Here I am adding role=style for customizing**
data.addColumn({type: 'string', role: 'style'});
data.addRows(" + dt.Rows.Count + ");
");
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
str.Append("data.setValue( " + i + "," + 0 + "," + "'" + dt.Rows[i] ["Title"].ToString() + "');");
str.Append("data.setValue(" + i + "," + 1 + "," + dt.Rows[i]["MenteeCount"].ToString() + ") ;");
}
str.Append(#"
var view = new google.visualization.DataView(data);
view.setColumns([0, {
sourceColumn: 1,
calc: function () {return 0;}
}, 2]);
var chart = new google.visualization.ColumnChart(document.getElementById('course_div'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
chart.draw(data, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
});
chart.draw(view, {
width: 650,
height: 300,
title: 'Course Enrollment',
color: '#0092CB',
hAxis: {
title: 'Course Title',
titleTextStyle: {
color: 'green'
}
},
animation: {
duration: 1000
}
});
}
</script>
");
lt_course.Text = str.ToString().TrimEnd(',').Replace('*', '"');
}
I believe you are missing quotation marks around your color...
At the end of this line:
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:#0092CB,");
you need apostrophes around the hex color:
str.Append(" chart.draw(data, {width: 650, height: 300, title: 'Course Enrollment', color:'#0092CB',");
If that doesn't work, compare the javascript that your app generates with your manually created code.
I'm trying to get the FileDescription asp:textbox to save into a db when the user clicks upload but it's coming back blank. what am I doing wrong?
this is in my upload.aspx file
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1"
ThrobberID="myThrobber" OnUploadComplete="AjaxFileUpload1_UploadComplete"
ContextKeys=""
AllowedFileTypes="jpg,jpeg,doc,xls"
MaximumNumberOfFiles="1"
runat="server"/>
</div>
File Description<asp:TextBox ID="FileDescription" Width="200" runat="server" ></asp:TextBox>
and this is in my upload.cs file
protected void AjaxFileUpload1_UploadComplete(object sender, AjaxControlToolkit.AjaxFileUploadEventArgs e)
{
string sFileDescription = FileDescription.Text;
string filePath = "~/" + e.FileName;
AjaxFileUpload1.SaveAs(filePath);
}
Let's add individual descriptions for each uploaded file. To do this you need to download AjaxControlToolkit sources from Codeplex (here is a link to download: Latest toolkit sources and modify three files:
AjaxFileUpload.Item.pre.js
AjaxFileUpload.Control.pre.js
AjaxFileUpload.css
New content of the AjaxFileUpload.Item.pre.js file:
/// <reference path="../../../client/microsoftajax.extended/common/common.pre.js" />
Type.registerNamespace("Sys.Extended.UI.AjaxFileUpload");
Type.registerNamespace("AjaxFileUpload");
Sys.Extended.UI.AjaxFileUpload.Item = function (parentId, fileItem, onRemoveItem) {
this._deleteButton = null;
this._parentId = parentId;
this._inputElementValue = fileItem.value;
this._id = fileItem.id;
this._slices = fileItem.slices;
this._sliceIndex = 0;
this._fileInfoContainer = null;
this._fileStatusText = null;
this._isUploaded = false;
this._isUploading = false;
this._fileSize = 0;
this._fileName = "";
this._fileType = "";
this._bytesUploaded = 0;
this._fileComment = null;
this._ui = this.initUI(onRemoveItem);
};
Sys.Extended.UI.AjaxFileUpload.Item.prototype = {
initUI: function (onRemoveItem) {
var self = this, file = this._inputElementValue, utils = new Sys.Extended.UI.AjaxFileUpload.Utils(),
isHtml5Support = utils.checkHtml5BrowserSupport(),
// generate unique id for each item
id = this._id,
// create line item container
container = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemContainer_' + id,
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file info/status container
fileInfoContainer = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileInfoContainer_' + id,
style: {
display: 'inline-block'
}
}
}),
// create file info placeholder
fileInfoText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemInfo_' + id
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file status placeholder
fileStatusText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemStatus_' + id
},
cssClasses: ['uploadstatus']
}),
commentContainer = $common.createElementFromTemplate({
nodeName: 'div',
cssClasses: ['ajax__fileupload_fileItem_commentContainer']
}),
fileComment = $common.createElementFromTemplate({
nodeName: "input",
properties: {
id: this._parentId + '_FileItemComment_' + id,
type: 'text',
style: {
width: '100%'
}
},
cssClasses: ['ajax__fileupload_fileItem_commentInput']
}),
// init remove button
deleteButton = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemDeleteButton_' + id
},
cssClasses: ['removeButton']
});
this._fileName = utils.getFileName(file);
if (isHtml5Support) {
this._fileSize = file.size;
var fType = file.type ? '<span class="filetype">(' + file.type + ')</span>' : '';
fileInfoText.innerHTML = '<span class="filename">' + this._fileName + '</span> '
+ fType
+ ' - <span class="filesize">' + utils.sizeToString(file.size) + '</span> ';
this._fileType = file.type;
} else {
fileInfoText.innerHTML = '<span class="filename">' + this._fileName + '</span>';
this._fileType = utils.getFileType(file);
}
commentContainer.innerHTML = '<label class="ajax__fileupload_fileItem_commentLabel" for="' + this._parentId + '_FileItemComment_' + id + '">Description:</label>';
commentContainer.appendChild(fileComment);
fileInfoContainer.appendChild(fileInfoText);
fileInfoContainer.appendChild(fileStatusText);
fileInfoContainer.appendChild(commentContainer);
$common.setText(deleteButton, Sys.Extended.UI.Resources.AjaxFileUpload_Remove);
$addHandlers(deleteButton, {
'click': Function.createDelegate(this, function () {
onRemoveItem(self);
})
});
// build the line item
if (Sys.Browser.agent == Sys.Browser.InternetExplorer && Sys.Browser.version <= 8) {
container.appendChild(deleteButton);
container.appendChild(fileInfoContainer);
}
else {
container.appendChild(fileInfoContainer);
container.appendChild(deleteButton);
}
this._fileInfoContainer = fileInfoContainer;
this._deleteButton = deleteButton;
this._fileStatusText = fileStatusText;
this._fileComment = fileComment;
return container;
},
setStatus: function (fileStatusText, text) {
$common.setText(this._fileStatusText, ' (' + text + ')');
this._fileInfoContainer.setAttribute('class', fileStatusText + 'State');
},
disabled: function (on) {
if (on)
this._deleteButton.disabled = this._fileComment.disabled = 'disabled';
else
this._deleteButton.disabled = this._fileComment.disabled = '';
},
hide: function () {
this._deleteButton.style.visibility = 'hidden';
this._fileComment.readOnly = true;
},
destroy: function () {
$common.removeElement(this._inputElementValue);
$common.removeElement(this._deleteButton);
$common.removeElement(this._fileComment);
$common.removeElement(this._ui);
},
get_inputElementValue: function () {
return this._inputElementValue;
},
appendNodeTo: function (parent) {
parent.appendChild(this._ui);
},
removeNodeFrom: function (parent) {
parent.removeChild(this._ui);
},
get_fileComment: function () {
return this._fileComment.value;
}
};
In this code added new class field _fileComment, new function get_fileComment and modified initUI, disabled, hide and destroy functions. After these changes, each file item will have individual textbox for file description.
After that, change a bit AjaxFileUpload.Control.pre.js file. Rewrite the doneAndUploadNextFile function as below:
doneAndUploadNextFile: function (fileItem) {
/// <summary>
/// Mark fileItem as uploaded, and upload next file in queue.
/// </summary>
/// <param name="fileItem">Uploaded File</param>
// send message to server to finalize this upload
var xhr = new XMLHttpRequest(),
self = this;
xhr.open("POST", "?contextKey="+ this._contextKey +"&done=1&guid=" + fileItem._id + "&comment=" + fileItem.get_fileComment(), true);
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
// Mark as done and invoke event handler
self.raiseUploadComplete(Sys.Serialization.JavaScriptSerializer.deserialize(xhr.responseText));
// Upload next file
self._processor.startUpload();
} else {
// finalizing is error. next file will not be uploaded.
self.setFileStatus(fileItem, 'error', Sys.Extended.UI.Resources.AjaxFileUpload_error);
self.raiseUploadError(xhr);
throw "error raising upload complete event and start new upload";
}
}
};
xhr.send(null);
}
And the last step is a AjaxFileUpload.css file. Change heigh css rile in .ajax__fileupload_fileItemInfo class definition and add three additional classes for description:
.ajax__fileupload_fileItemInfo {
line-height: 20px;
height: 44px;
margin-bottom: 2px;
overflow: hidden;
}
.ajax__fileupload_fileItem_commentContainer {
display: table;
width: 100%;
}
.ajax__fileupload_fileItem_commentLabel {
display: table-cell;
width: 1px;
white-space: nowrap;
padding-right: 5px;
}
.ajax__fileupload_fileItem_commentInput {
display: table-cell;
width: 100%;
}
After these changes rebuild toolkit solution and use custom dlls.
Now you can get posted description from query string in OnUploadComplete event handler: var comment = Request.QueryString["comment"];