Related
I have the following dropzone where users can either select images or drag images into the dropzone.
When I select files through the input type=file button and click the btnUpload button, the images are uploaded. However, when I drag items into the dropzone and then click btnUpload button, nothing happens: no logging, no network requests, nothing.
Why? Here's my code.
<div id="drop-area">
<span id="status"></span>
<p>Drop files here</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select files</label>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
<input id="btnUpload" type="submit" class="button green small" value="Upload" />
<script type="text/javascript">
let btnUpload = document.getElementById("btnUpload")
btnUpload.addEventListener('click', uploadFiles, false)
function uploadFiles(event) {
event.preventDefault();
// TODO - validate file size, extension & amount
files = [...fileElem.files]
// Submit each file separately.
files.forEach(uploadFile)
//check if success and if so, remove from gallery
}
// This all copy & paste
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for (let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
//console.log('update', fileNumber, percent, total)
progressBar.value = total
return total === 100;
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
//files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
//console.error('file.name: ' + file.name);
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function () {
let img = document.createElement('img')
img.id = file.name;//.toString().replaceAll('"', '').replaceAll('.', '').replaceAll(' ', '_');
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = '/api2/uploadfile/135/3435' // TODO: change end point
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function (e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
if (updateProgress(i, 100)) {
//$("'" + "#gallery #" + xhr.responseText.replaceAll('"', '').replaceAll('.', '').replaceAll(' ', '_') + "'").remove();//how do we handle spaces in filenames?
//console.error(i);
$('#gallery img:nth-child(' + (i + 1) + ')').hide();
//alert('Complete') // TODO
return true;
}
}
else if (xhr.readyState == 4 && xhr.status != 200) {
$('#status').html(GetMessageStatus('Error: ' + xhr.responseText, 1));
return false;
}
})
formData.append('file', file)
xhr.send(formData)
}
</script>
UPDATE 1
I added my code here: https://plnkr.co/edit/HHPDL6Ndc6nxPMGn?open=lib%2Fscript.js
I debugged everything and the same path is executed. However, when I select 1 image via the button and hit upload button, in function uploadFiles, on this line files = [...fileElem.files] the variable files has length 1, but when I drag and drop the same image, and hit upload button the variable files has length 0.
I think you need to managed the dropped images separately maybe in a array of dropped files because the files dropped in drop-area will not be considered in the File Upload html tag.
So you need to add a global variable to store dropped files array.
let droppedFiles = [];
Then in the handleDrop(e) method you need to add files in the droppedFiles array.
for (let i = 0; i < files.length; i++) {
droppedFiles.push(files[i]);
}
Now, at the submit/upload button you need to add this also, to make sure all files should get attached. (Files from file upload tag & dropped files).
for (let i = 0; i < droppedFiles.length; i++) {
files.push(droppedFiles[i]);
}
let droppedFiles = [];
let btnUpload = document.getElementById("btnUpload")
btnUpload.addEventListener('click', uploadFiles, false)
function uploadFiles(event) {
event.preventDefault();
// TODO - validate file size, extension & amount
files = [...fileElem.files];
for (let i = 0; i < droppedFiles.length; i++) {
files.push(droppedFiles[i]);
}
console.log(files.length);
// Submit each file separately.
//files.forEach(uploadFile)
//check if success and if so, remove from gallery
}
// This all copy & paste
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
for (let i = 0; i < files.length; i++) {
droppedFiles.push(files[i]);
}
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for (let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
//console.log('update', fileNumber, percent, total)
progressBar.value = total
return total === 100;
}
function handleFiles(files) {
files = [...files];
// fileElem= [...files];
initializeProgress(files.length)
//files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
//console.error('file.name: ' + file.name);
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function () {
let img = document.createElement('img')
img.id = file.name;//.toString().replaceAll('"', '').replaceAll('.', '').replaceAll(' ', '_');
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = '/api2/uploadfile/135/3435' // TODO: change end point
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function (e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
if (updateProgress(i, 100)) {
//$("'" + "#gallery #" + xhr.responseText.replaceAll('"', '').replaceAll('.', '').replaceAll(' ', '_') + "'").remove();//how do we handle spaces in filenames?
//console.error(i);
$('#gallery img:nth-child(' + (i + 1) + ')').hide();
//alert('Complete') // TODO
return true;
}
}
else if (xhr.readyState == 4 && xhr.status != 200) {
$('#status').html(GetMessageStatus('Error: ' + xhr.responseText, 1));
return false;
}
})
formData.append('file', file)
xhr.send(formData)
}
<div id="drop-area" style="border: 1px dashed">
<span id="status"></span>
<p>Drop files here</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select files</label>
<progress id="progress-bar" max=100 value=0></progress>
<br />
<br />
</div>
<div id="gallery"></div>
<br />
<input id="btnUpload" type="submit" class="button green small" value="Upload" />
Add fileElem.files = files to the top of your handleFiles() method, or modify your uploadFiles() method. Currently, uploadFiles() only pulls files from fileElem. As a fix, you can set the fileElem's file list to the dropped file in handleFiles(). Thus, your new handleFiles() method would look as follows:
function handleFiles(files) {
fileElem.files = files
files = [...files]
initializeProgress(files.length)
files.forEach(previewFile)
}
I have a View in which i have criteria a Supplier TextBox a LastMonth dropdown and a Months Textbox.
#using JouleBrokerDB.ViewModels;
#model AssignPayReportToDepositViewModel
#{
ViewBag.Title = "View";
}
<link href="#Url.Content("~/Content/kendo/kendo.common-bootstrap.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.bootstrap.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.dataviz.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.dataviz.bootstrap.min.css")" rel="stylesheet" />
<style>
.treediv {
display: inline-block;
vertical-align: top;
width: 440px;
/*height:400px;*/
min-height: 400px;
text-align: left;
margin: 0 2em;
border-radius: 25px;
border: 2px solid #8AC007;
padding: 15px;
overflow: auto;
}
</style>
<div class="row">
<div class="col-md-9 col-md-offset-1">
#using (Html.BeginForm("Show", "AssignPayReportToDeposit", FormMethod.Post, new { id = "AssignToPayReportForm", #class = "form-horizontal" }))
{
<fieldset>
<!-- Form Name -->
<legend>Assign Pay Report to Deposit</legend>
<div class="form-group">
<!-- Supplier -->
<div class="col-sm-4">
#Html.Label("", "Supplier:", new { #class = "control-label", #for = "textinput" })
<div id="suppliers">
#Html.DropDownListFor(x => x.SuppliersList, new SelectList(Model.SuppliersList, "SupplierID", "Name"), new { id = "ddSupplier", #class = "form-control" })
</div>
</div>
<!-- Last Month -->
<div class="col-sm-4">
#Html.Label("", "Last Month:", new { #class = "control-label", #for = "textinput" })
#Html.DropDownListFor(x => x.LastMonthsList, new SelectList(Model.LastMonthsList), new { #id = "ddLastMonth", #class = "form-control" })
</div>
<!-- Months-->
<div class="col-sm-4">
#Html.Label("", "Months:", new { #class = "control-label", #for = "textinput" })
#Html.TextBox("txtMonths", null, new { type = "number", step = 1, min = 1, max = 12, #class = "form-control", required = "required" })
</div>
</div>
</fieldset>
<div class="treediv">
#Html.Label("", "UnAssigned PayReport:", new { #class = "control-label", #for = "textinput" })
<div id="TreeView_UPR" style="padding:5px"></div>
</div>
<div class="treediv">
#Html.Label("", "Deposits:", new { #class = "control-label", #for = "textinput" })
<h4></h4>
<div id="TreeView_AD" style="padding:5px"></div>
</div>
}
</div>
</div>
<script src="#Url.Content("~/Scripts/kendo/kendo.all.min.js")"></script>
<script src="#Url.Content("~/Scripts/Views/AssignPayReportToDeposit/Show.js")"></script>
Here on this text box i have attached changed event though jQuery. The requirement is that whenever the criteria changes the treeview div will be filled with data will be refreshed.
AssignPayReportsToDeposit.AttachEvents = function () {
$("#ddSupplier").change(AssignPayReportsToDeposit.OnSupplierChange);
$("#ddLastMonth").change(AssignPayReportsToDeposit.OnLastMonthChange);
$("#txtMonths").change(AssignPayReportsToDeposit.OnMonthsChange);
}
these changed event handler will handle the refreshing the treeview. The whole thing is handled through ajax calls.
Now i know that using Ajax.ActionLink and UpdateTargetId parameter with Replace option i can return the treeview in partial view so the manual handling can be removed. but that will require me put the anchor button which user have to click. Requirement is that the refresh of treeview should be done on any criteria change.
Is there any way i am able to achieve this using Ajax.ActionLink (or any another razor syntax that will take load off from the manual handling ) ? On change event of the controls i would like to call a controller using ajax.actionlink which will return a partialview and update the div.
Edit: I am handling this through jQuery right now. so i will post the complete code for more understanding.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JouleBrokerDB;
using JouleBrokerDB.ViewModels;
using JouleBroker.Filters;
namespace JouleBroker.Controllers
{
[RoutePrefix("AssignPayReportToDeposit")]
[Route("{action=Show}")]
public class AssignPayReportToDepositController : Controller
{
// GET: AssignPayReportToDeposit
//[Route("Show",Name = "APTDShow")]
//[ValidateLogin]
public ActionResult Show()
{
List<SupplierViewModel> suppliers = DBCommon.GetAllSuppliers(false);
SuppliersList_LastMonthsList_ViewModel model = new SuppliersList_LastMonthsList_ViewModel()
{
SuppliersList = suppliers,
LastMonthsList = new List<string>()
};
return View(model);
}
[HttpPost]
[Route("GetUnAssignedPayReports")]
public JsonResult GetUnAssignedPayReports(int SupplierID,
string MonthPaid,
int Months)
{
var payreports = AssignPayReportsToDepositData.GetUnAssignedPayReports(SupplierID,
MonthPaid,
Months);
return Json(payreports);
}
[HttpPost]
[Route("GetAssignedPayReports")]
public JsonResult GetAssignedPayReports(int SupplierID,
string MonthPaid,
int Months)
{
var payreports = AssignPayReportsToDepositData.GetAssignedPayReports(SupplierID,
MonthPaid,
Months);
return Json(payreports);
}
[HttpPost]
[Route("AssignDepositIdToPayReport")]
public bool AssignDepositIdToPayReport(int PayReportID, int DepositID)
{
return AssignPayReportsToDepositData.AssignDepositIdToPayReport(PayReportID, DepositID);
}
}
}
JavaScript File (the code is a bit lengthy so you don't need to look at all of them you can see the methods which are calling the action methods. GetUnAssignedPayReports and GetAssignedPayReports which returns the data which is used to fill the tree view.) I just want this portion to moved to partial view and passing model to partial view generate treeview there and replace the div each time on change event with rendering partial view again. Hope i am clear enough. so change the above methods to return partial instead of json result that what i am trying to achive
function AssignPayReportsToDeposit() { }
AssignPayReportsToDeposit.SelectedSupplierID = 0;
AssignPayReportsToDeposit.SelectedLastMonth = null;
AssignPayReportsToDeposit.SelectedMonths = 0;
AssignPayReportsToDeposit.LastMonthsList = null;
AssignPayReportsToDeposit.UnAssignedPayReportsList = null;
AssignPayReportsToDeposit.AssignedPayReportsList = null;
AssignPayReportsToDeposit.LastTextChangedNode = null;
//--------- Document Ready Function -------- //
$(document).ready(function () {
//AttachEvents
AssignPayReportsToDeposit.AttachEvents();
});
AssignPayReportsToDeposit.AttachEvents = function () {
$("#ddSupplier").change(AssignPayReportsToDeposit.OnSupplierChange);
$("#ddLastMonth").change(AssignPayReportsToDeposit.OnLastMonthChange);
$("#txtMonths").change(AssignPayReportsToDeposit.OnMonthsChange);
}
//Handles Supplier ChangeEvents
AssignPayReportsToDeposit.OnSupplierChange = function () {
//Get Changed Supplier ID
AssignPayReportsToDeposit.SelectedSupplierID = $('#ddSupplier').val();
//Get Last Month List
AssignPayReportsToDeposit.LastMonthsList = CommonAction.GetLastPayReportMonthsBySupplierID(AssignPayReportsToDeposit.SelectedSupplierID);
//Fill Last Month List
AssignPayReportsToDeposit.FillLastMonths();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Handles Last Month Change Event
AssignPayReportsToDeposit.OnLastMonthChange = function () {
AssignPayReportsToDeposit.SelectedLastMonth = $('#ddLastMonth').val();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Handles Month Change Event
AssignPayReportsToDeposit.OnMonthsChange = function () {
AssignPayReportsToDeposit.SelectedMonths = $('#txtMonths').val();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Fills Last Month Dropdown with options
AssignPayReportsToDeposit.FillLastMonths = function () {
var ddLastMonth = $("#ddLastMonth");
if (ddLastMonth != undefined) {
ddLastMonth.empty();
if (AssignPayReportsToDeposit.LastMonthsList != undefined) {
$.each(AssignPayReportsToDeposit.LastMonthsList, function () {
Common.AddOptionToSelect(ddLastMonth, this.Text, this.Text);
});
ddLastMonth.val(AssignPayReportsToDeposit.LastMonthsList[0].Text);
AssignPayReportsToDeposit.SelectedLastMonth = ddLastMonth.val();
}
}
}
AssignPayReportsToDeposit.ValidateControls = function () {
var success = true;
if (AssignPayReportsToDeposit.SelectedSupplierID == undefined ||
AssignPayReportsToDeposit.SelectedSupplierID == 0) {
// bootbox.alert('Please select a Supplier');
success = false;
}
else if (AssignPayReportsToDeposit.SelectedLastMonth == undefined ||
AssignPayReportsToDeposit.SelectedLastMonth == '') {
// bootbox.alert('Please select Last Month');
success = false;
}
else if (AssignPayReportsToDeposit.SelectedMonths == undefined ||
AssignPayReportsToDeposit.SelectedMonths == 0) {
// bootbox.alert('Please Enter Months');
success = false;
}
return success;
}
//Assigns DepositIdToPayReport
AssignPayReportsToDeposit.AssignDepositIdToPayReport = function (PayReportID, DepositID) {
var success = false;
if (PayReportID != undefined && DepositID != undefined) {
var jsonData = JSON.stringify({ PayReportID: PayReportID, DepositID: DepositID });
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: 'AssignPayReportToDeposit/AssignDepositIdToPayReport',
data: jsonData,
async: false,
success: function (result) {
success = result;
},
error: Common.AjaxErrorHandler
});
}
return success;
}
//--------- Tree View UPR Functions -------- //
//Gets UnAssigned Pay Reports
AssignPayReportsToDeposit.GetUnAssignedPayReports = function () {
var payReports;
if (AssignPayReportsToDeposit.ValidateControls()) {
var jsonData = JSON.stringify(
{
SupplierID: AssignPayReportsToDeposit.SelectedSupplierID,
MonthPaid: AssignPayReportsToDeposit.SelectedLastMonth,
Months: AssignPayReportsToDeposit.SelectedMonths
});
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "AssignPayReportToDeposit/GetUnAssignedPayReports",
data: jsonData,
async: false,
success: function (data) {
if (data != undefined && data != "")
payReports = data;
},
error: Common.AjaxErrorHandler
});
}
return payReports;
}
AssignPayReportsToDeposit.BindTreeViewUPR = function () {
var treeview = $("#TreeView_UPR");
var inline = new kendo.data.HierarchicalDataSource({
data: AssignPayReportsToDeposit.UnAssignedPayReportsList,
schema: {
model: {
id: "PayReportID"
}
}
});
treeview.kendoTreeView({
dragAndDrop: true,
dataSource: inline,
dataBound: function (e) {
if (!this.dataSource.data().length) {
this.element.append("<p class='no-items'>No items yet.</p>");
} else {
this.element.find(".no-items").remove();
}
},
dataTextField: ["DisplayValue"],
drop: AssignPayReportsToDeposit.OnTreeViewUPRDrop
});
}
AssignPayReportsToDeposit.OnTreeViewUPRDrop = function (e) {
var isTargetTreeViewAD = false;
var sourceDataItem = this.dataItem(e.sourceNode);
var targetDataItem = this.dataItem(e.destinationNode);
if (targetDataItem == undefined) {
targetDataItem = $("#TreeView_AD").data("kendoTreeView").dataItem(e.destinationNode);
isTargetTreeViewAD = true;
}
if (sourceDataItem == undefined ||
targetDataItem == undefined) {
//Source and target both must exists
e.preventDefault();
return;
}
if (sourceDataItem.IsDeposit == true) {
//Deposits cannot be drag and Drop
e.preventDefault();
return;
}
if (isTargetTreeViewAD) {
if (e.dropPosition == "over" &&
sourceDataItem.IsPayReport == true &&
sourceDataItem.IsAssignedPayReport == false &&
targetDataItem.IsDeposit == true) {
//Source must UnAssigned Payreport Target Must be Deposit and Drop position must over
//Implement logic to assign deposit id to the Pay Report
var PayReportID = sourceDataItem.PayReportID;
var DepositID = targetDataItem.DepositID;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", true);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
else {
if ((e.dropPosition == "before" || e.dropPosition == "after") &&
sourceDataItem.IsPayReport == true &&
targetDataItem.IsPayReport == true &&
targetDataItem.IsAssignedPayReport == false) {
//Only allow sorting in this condition otherwise cancel drop event
//Means only allow sorting of unassigned payreports within the tree
}
else {
e.preventDefault();
return;
}
}
}
AssignPayReportsToDeposit.RefreshTreeViewUPR = function () {
//Destroy and empty tree
var treeview = $("#TreeView_UPR").data("kendoTreeView");
if (treeview != undefined) { treeview.destroy(); }
treeview = $("#TreeView_UPR");
treeview.empty();
AssignPayReportsToDeposit.UnAssignedPayReportsList = AssignPayReportsToDeposit.GetUnAssignedPayReports();
AssignPayReportsToDeposit.BindTreeViewUPR();
}
//--------- TreeView_AD Functions -------- //
//Gets Assigned Pay Reports
AssignPayReportsToDeposit.GetAssignedPayReports = function () {
var payReports;
if (AssignPayReportsToDeposit.ValidateControls()) {
var jsonData = JSON.stringify(
{
SupplierID: AssignPayReportsToDeposit.SelectedSupplierID,
MonthPaid: AssignPayReportsToDeposit.SelectedLastMonth,
Months: AssignPayReportsToDeposit.SelectedMonths
});
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "AssignPayReportToDeposit/GetAssignedPayReports",
data: jsonData,
async: false,
success: function (data) {
if (data != undefined && data != "")
payReports = data;
},
error: Common.AjaxErrorHandler
});
}
return payReports;
}
AssignPayReportsToDeposit.BindTreeViewAD = function () {
var treeview = $("#TreeView_AD");
var inline = new kendo.data.HierarchicalDataSource({
data: AssignPayReportsToDeposit.AssignedPayReportsList,
schema: {
model: {
id: "DepositID",
hasChildren: "HasAnyAssignedPayReports",
children: "AssignedPayReports"
}
}
});
treeview.kendoTreeView({
dragAndDrop: true,
dataSource: inline,
dataBound: function (e) {
if (!this.dataSource.data().length) {
this.element.append("<p class='no-items'>No items yet.</p>");
} else {
this.element.find(".no-items").remove();
}
},
dataTextField: ["DisplayValue", "DisplayValue"],
drop: AssignPayReportsToDeposit.OnTreeViewADDrop,
select: AssignPayReportsToDeposit.OnTreeViewADSelect
});
}
AssignPayReportsToDeposit.OnTreeViewADSelect = function (e) {
var dataItem = this.dataItem(e.node);
var treeview = this;
if (AssignPayReportsToDeposit.LastTextChangedNode != undefined) {
//Restore last node's Text
var previousDataItem = this.dataItem(AssignPayReportsToDeposit.LastTextChangedNode);
if (previousDataItem != undefined) {
var date = AssignPayReportsToDeposit.FormatDepositMonthToDisplay(previousDataItem.DepositDate);
var displaytext = "[" + date + "]" + "-[" + previousDataItem.BankName + "]-" + "[" + previousDataItem.Amount + "]";
this.text(AssignPayReportsToDeposit.LastTextChangedNode, displaytext);
}
AssignPayReportsToDeposit.LastTextChangedNode = undefined;
}
if (dataItem.IsDeposit) {
if (dataItem.hasChildren > 0) {
dataItem.set("expanded", true);
//Append sum to selected node's diplay value
var childs = dataItem.children.data();
var sum = 0;
$.each(childs, function () { sum += this.Amount });
var date = AssignPayReportsToDeposit.FormatDepositMonthToDisplay(dataItem.DepositDate);
var displaytext = "[" + date + "]" + "-[" + dataItem.BankName + "]-" + "[" + dataItem.Amount + "(" + sum + ")" + "]";
this.text(e.node, displaytext)
AssignPayReportsToDeposit.LastTextChangedNode = e.node;
}
}
}
AssignPayReportsToDeposit.FormatDepositMonthToDisplay = function (jsondate) {
var depositedate = "";
if (jsondate != undefined && jsondate != "") {
var date = Common.ParseDate(jsondate);
var month = ("0" + (date.getMonth() + 1)).slice(-2);
depositedate = date.getFullYear() + "-" + (month);
}
return depositedate;
}
AssignPayReportsToDeposit.OnTreeViewADDrop = function (e) {
var isTargetTreeViewURP = false;
var DroptoNoItemZone = false;
var sourceDataItem = this.dataItem(e.sourceNode);
var targetDataItem = this.dataItem(e.destinationNode);
var treeview_UPR = $("#TreeView_UPR").data("kendoTreeView");
if (targetDataItem == undefined) {
targetDataItem = treeview_UPR.dataItem(e.destinationNode);
if (treeview_UPR.element.find(".no-items").length > 0) DroptoNoItemZone = true;
isTargetTreeViewURP = true;
}
if ((sourceDataItem == undefined ||
targetDataItem == undefined) && DroptoNoItemZone == false) {
e.preventDefault();
return;
}
if (sourceDataItem.IsDeposit == true) {
//Deposits can not be moved within the tree view
e.preventDefault();
return;
}
if (isTargetTreeViewURP) {
if (((e.dropPosition == "before" || e.dropPosition == "after") &&
sourceDataItem.IsPayReport == true &&
sourceDataItem.IsAssignedPayReport == true &&
targetDataItem.IsPayReport == true) || (e.dropPosition == "over" && DroptoNoItemZone)) {
//Implement logic to unassing deposit id to PayReport
var PayReportID = sourceDataItem.PayReportID;
var DepositID = 0;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", false);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
else {
if (e.dropPosition == "over" &&
sourceDataItem.IsPayReport == true &&
targetDataItem.IsDeposit == true) {
//Implement Logic to change deposit ID for assigned payreport
var PayReportID = sourceDataItem.PayReportID;
var DepositID = targetDataItem.DepositID;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", true);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
}
AssignPayReportsToDeposit.RefreshTreeViewAD = function () {
//Destroy and empty tree
var treeview = $("#TreeView_AD").data("kendoTreeView");
if (treeview != undefined) { treeview.destroy(); }
treeview = $("#TreeView_AD");
treeview.empty();
AssignPayReportsToDeposit.LastTextChangedNode = undefined;
AssignPayReportsToDeposit.AssignedPayReportsList = AssignPayReportsToDeposit.GetAssignedPayReports();
AssignPayReportsToDeposit.BindTreeViewAD();
}
Unfortunately not out the box.
The Ajax extension methods are really just HTML helpers that work with other jQuery libraries. The helpers create the relavant HTML markup (such as adding custom addtributes data-*="") and the client scripts use this to determine their behaviour.
You could create your own MVC HTML helper and script library to handle change events for you however I would recommend looking at a front end framework such as Angular instead. This library would handle all the events declaratively so you don't need to waste time writing event handlers.
I am developing an app where I use 2 API's a.k.a Instagram API and Google Map API. Using AJAX, I get the first set of Images filtered by a tag name. In the 1st set we receive 20 images. Among the received images, the images that have the latitude and longitude info (geotagged images) are displayed on the map.
Now the first time when my page loads, I cannot see the map. But when I press the load more button to get the next set of images, the Map works fine showing my previous images too.
Here is the code for what happens on page load:
$( window ).load(function() {
$.ajax({
type: "GET",
url: "https://api.instagram.com/v1/tags/nyc/media/recent?client_id=02e****",
dataType:'JSONP',
success: function(result) {
onAction(result, 2, tag);
instaMap(result, 2, from);
}
});
});
These are the functions being called:
/**
* [initialize description]
* Initialize the map with markers showing all photos that are geotagged.
*/
var initialize = function(markers) {
var bounds = new google.maps.LatLngBounds(),
mapOptions = {
scrollwheel: false,
mapTypeId: 'roadmap',
center: new google.maps.LatLng(22.50, 6.50),
minZoom: 2
},
gmarkers = [],
map,
positions,
markCluster;
markers = remDuplicate(markers);
// Info Window Content
var infoWindowContent = [];
for (var j = 0; j < markers.length; j++ ) {
var content = [
'<div class="info_content">' +
'<h3>' + markers[j][2] + '</h3>' +
'<a href="' + markers[j][3] + '" target="_blank">' +
'<img src="' + markers[j][4] + '" style="z-index:99999">' + '</a>' +
'</div>'
];
infoWindowContent.push(content);
}
// Display a map on the page
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
map.setTilt(45);
// Display multiple markers on a map
var oms = new OverlappingMarkerSpiderfier(map);
var infoWindow = new google.maps.InfoWindow(), marker, i;
// Loop through our array of markers & place each one on the map
for( i = 0; i < markers.length; i++ ) {
positions = new google.maps.LatLng(markers[i][0], markers[i][1]);
marker = new google.maps.Marker({
position: positions,
map: map,
animation:google.maps.Animation.BOUNCE,
title: markers[i][2]
});
oms.addMarker(marker);
// Allow each marker to have an info window
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infoWindow.close();
infoWindow.setContent(infoWindowContent[i][0]);
infoWindow.open(map, marker);
map.setCenter(marker.getPosition());
};
})(marker, i));
gmarkers.push(marker);
}
google.maps.event.addListener(map, 'click', function() {
infoWindow.setMap(null);
});
markCluster = new MarkerClusterer(map, gmarkers);
// Override our map zoom level once our fitBounds function runs (Make sure it only runs once)
var boundsListener = google.maps.event.addListener((map), 'bounds_changed', function(event) {
map.setZoom(2);
google.maps.event.removeListener(boundsListener);
});
};
/**
* [onAction]
* OnAction() function helps in loading non-geotagged pics.
*
* #param {[type]} result [Result retruned from the Instagram API in json format]
* #param {[type]} likey [hearts the user has entered as per which the posts will be filtered]
*/
var onAction = function (result, likey, tag) {
$('.load-pics').remove();
if (result.pagination.next_url) {
paginate = removeURLParameter(result.pagination.next_url, 'count');
}
$.each(result, function(key, value) {
if (key === 'data') {
$.each(value, function(index, val) {
liked = val.likes.count;
link = val.link;
imgUrl = val.images.low_resolution.url;
locations = val.location;
if (liked >= likey) {
if (locations === null) {
output = '<li class="img-wrap">' + '<div class="main-img">' +
'<a href="' + link + '" target="_blank">' +
'<img src="' + imgUrl + '" ><span class="hover-lay"></span></a>' +'<p>' +
'<span class="heart"></span><span class="likes-no">' + liked + '</span>' +
'<span class="comment-box"></span><span class="comment-no">' +
val.comments.count + '</span> ' + '</p>' + '</div>' +
'<div class="img-bottom-part">'+ '' + '<div class="headin-hastag">' +
'by ' + '<h2>Sebastien Dekoninck</h2>#hello <span>#kanye</span> #helloagain #tagsgohere</div>'
+'</div></li>';
$('#instafeed').append(output);
}
}
});
}
});
if ($('#instafeed').children().length === 0) {
alert('There are no pics with ' + likey + ' likes or #' + tag + ' was not found.');
} else {
// $('.not-geo').remove();
// $('#instafeed').before('<button class="not-geo">Click To See Images That Are Not Geotagged <img src="assets/imgs/down.png" ></button>');
}
$('#instafeed').append('<div class="load-pics"><button id="show-more">Show more <span></span></button> </div>');
};
/**
* [instaMap]
* instaMap() will be the function which will deal with all map based functionalities.
*/
var instaMap = function(result, likey, from) {
$('.load-mark').remove();
if (result.pagination.next_url) {
pagiMap = removeURLParameter(result.pagination.next_url, 'count');
}
$.each(result, function(key, value) {
if (key === 'data') {
$.each(value, function(index, val) {
liked = val.likes.count;
link = val.link;
imgUrl = val.images.low_resolution.url;
locations = val.location;
if (liked >= likey) {
if (locations && locations.latitude !== null) {
tempArr = [
locations.latitude,
locations.longitude,
val.user.username,
val.link,
val.images.low_resolution.url
];
mark.push(tempArr);
}
}
});
}
});
if (mark.length) {
initialize(mark);
$('.map-parent-wrapper').append('<div class="load-mark"><button id="show-mark">See More </button></div>');
} else {
alert('No geotagged pics found in the retrieved set. Click see more');
$('.map-parent-wrapper').append('<div class="load-mark"><button id="show-mark">See More </button></div>');
}
};
I have created a See More button to retrieve the next set of images and load those on the Map. When clicking see more, everything seems to work fine. Not sure why it's happening so. Console.log does not show any error. Also, all the values I feed does flow appropriately. I even tried clearing cache. Not sure, why it's happening.
If instaMap is the function which is going to handle all your map based functionality, it has to be the one that loads map in your $( window ).load function ();
Otherwise, if you want Google maps to load on initial window load you need to put below in there:
google.maps.event.addDomListener(window, 'load', initialize);
I'm using a CDN to Load Bootstrap.css.
My question is how can i check if CDN bootstrap was loaded/found.
And if it wasn't, then load local Boostrap.
Here's Jquery fallback..
<script type="text/javascript">
Modernizr.load([
{
load: '//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.1/jquery.min.js',
complete: function () {
if ( !window.jQuery ) {
Modernizr.load([
{
load: config.js + 'vendor/jquery-1.10.1.min.js',
complete: function () {
console.log("Local jquery-1.10.1.min.js loaded !");
}
}
]);
} else {
console.log("CDN jquery-1.10.1.min.js loaded !");
}
}
}
]);
</script>
And this is how i load Modernizr than Css:
<script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
<script type="text/javascript">
if (typeof Modernizr == 'undefined') {
document.write(unescape("%3Cscript src='" + config.js + "/vendor/modernizr-2.6.2-respond-1.1.0.min.js' type='text/javascript'%3E%3C/script%3E"));
console.log("Local Modernizer loaded !");
}
</script>
<script type="text/javascript">
Modernizr.load([
{
load: config.css + "bootstrap.css",
complete: function () {
console.log("bootstrap.css loaded !");
}
},
{
load: config.css + "responsive.css",
complete: function () {
console.log("responsive.css loaded !");
}
},
{
load: config.css + "icons.css",
complete: function () {
console.log("Fontello icons.css loaded !");
}
},
{
load: config.css + "icons-ie7.css",
complete: function () {
console.log("Fontello icons-ie7.css loaded !");
}
},
{
load: config.css + "animation.css",
complete: function () {
console.log("Fontello animation.css loaded !");
}
}
]);
</script>
I have no idea how i could check if the css was loaded.. just like i did with modernizr and Jquery..
Thanks in advance...
Stoyan Stefanov has had a Pure JS solution for this for some time now, which actually just got an update not too long ago. Check out the link for an in depth breakdown.
But style.sheet.cssRules will only be populated once the file is loaded, so by checking for it repeatedly with a setInterval you are able to detect once your CSS file is loaded.
d = document;
d.head || (d.head = d.getElementsByTagName('head')[0]);
var style = d.createElement('style');
style.textContent = '#import "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"';
var fi = setInterval(function() {
try {
style.sheet.cssRules;
console.log('loaded!');
clearInterval(fi);
} catch(e){
console.log('loading...');
}
}, 10);
d.head.appendChild(style);
YepNope acknowledges the use of this technique, though they note in their documentation that YepNope's "callback does not wait for the css to actually be loaded".
Plus with YepNope's recent depreciation, let's move onto to a solution that integrates with Modernizr (who will no longer be including YepNope in their software), but does not utilize any of Modernizr's libraries, since they still do not have a native solution. Offirmo combines a couple of neat techniques from Robert Nyman and Abdul Munim to let Modernizr know the CSS is actually loaded.
We start with the following function, which allows us to get the CSS property of an element:
function getStyle(oElm, strCssRule){
var strValue = "";
if (document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
} else if (oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
Then we use the following funtion to create an hidden DOM element to test out CSS properties within:
function test_css(element_name, element_classes, expected_styles, rsrc_name, callback) {
var elem = document.createElement(element_name);
elem.style.display = 'none';
for (var i = 0; i < element_classes.length; i++){
elem.className = elem.className + " " + element_classes[i];
}
document.body.appendChild(elem);
var handle = -1;
var try_count = 0;
var test_function = function(){
var match = true;
try_count++;
for (var key in expected_styles){
console.log("[CSS loader] testing " + rsrc_name + " css : " + key + " = '" + expected_styles[key] + "', actual = '" + get_style_of_element(elem, key) + "'");
if (get_style_of_element(elem, key) === expected_styles[key]) match = match && true;
else {
console.error("[CSS loader] css " + rsrc_name + " test failed (not ready yet ?) : " + key + " = " + expected_styles[key] + ", actual = " + get_style_of_element(elem, key) );
match = false;
}
}
if (match === true || try_count >= 3){
if (handle >= 0) window.clearTimeout(handle);
document.body.removeChild(elem);
if (!match) console.error("[CSS loader] giving up on css " + rsrc_name + "..." );
else console.log("[CSS loader] css " + rsrc_name + " load success !" );
callback(rsrc_name, match);
}
return match;
}
if(! test_function()){
console.info("" + rsrc_name + " css test failed, programming a retry...");
handle = window.setInterval(test_function, 100);
}
}
Now we can reliably know, within Modernizr, if our CSS is ready to go:
Modernizr.load([
{
load: { 'bootstrap-css': 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css' },
callback: function (url, result, key){
//THIS IS OUR TEST USING THE ABOVE FUNCTIONS
test_css('span', ['span1'], {'width': '60px'}, function(match){
if (match){
console.log('CDN Resource Loaded!');
} else {
console.log('Fallback to local resource!')
}
}
}
}
]);
I have a javascript that must generate in runtime.The text of script is generate in controller class :
private string mapString
{
get
{
Locations loc = new Locations();
string appPath = Request.ApplicationPath;
loc.ReadXml(Path.Combine(Request.MapPath(appPath) + "\\App_Data", "Locations.xml"));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < loc.Specfications.Count; i++)
{
sb.Append("var myLatLng" + i.ToString() + "= new google.maps.LatLng(" + loc.Specfications[i].Y.ToString() + "," +
loc.Specfications[i].X.ToString() + ");");
sb.Append(" var beachMarker" + i.ToString() + " = new google.maps.Marker({position: myLatLng" + i.ToString() + ",map: map,icon: image,title:'" + loc.Specfications[i].Title + "'});");
....
...
...
ViewData["MapString"] = mapString;
When I use it in script tag :
<script type="text/javascript">
function initialize() {
#Server.HtmlDecode(ViewData["MapString"].ToString())
}
</script>
It dosen't return a true text and it retruns something like this:
contentString0 = '<table width="100%" style="font-family: tahoma; text-align: right; font
**update : The site didn't show my question correctly ,I want to show "'<" but it show "'<"
but it must return :
contentString0 ='
you see that it convert "'<" to "'<" .
But when I use : #Server.HtmlDecode(ViewData["MapString"].ToString()) out of script tag ,all things is OK.
You may want to do it this way, which I think is going to be more flexible than generating code in your controller :
Controller action :
public JsonResult GetCoords()
{
// your code here - im putting a generic result you may
// need to put some logic here to retrieve your location / locations
var result = new { lon = "51.0000", lat = "23.0000" };
return Json(result, JsonRequestBehavior.AllowGet);
}
in your view add :
<script type="text/javascript">
$(document).ready(function () {
$.getJSON('/YourController/GetCoords', function (jsonData) {
var lon = jsonData.lon;
var lat = jsonData.lat;
yourGoogleMapFunction(lon, lat);
});
});
</script>