I'm Calling onchange function from HTML. It works as expected for the first time. But Second time, its not. Not even the debugger is getting hitted in the function.
HTML
#Html.TextBoxFor(m => m.Attachment.AttachmentFile, new { type = "file", onchange = "GetAttachmentFileName()", style = "display:none" })
JavaScript
function GetAttachmentFileName() {
$("#Filesize").hide();
if ($("#Attachment_AttachmentFile").val() != null && $("#Attachment_AttachmentFile").val() != "") {
var filename = $("#Attachment_AttachmentFile").val().split('\\').pop().replace(" ", "");
$("#Attachment_StorageName").val(filename);
$("#filename").val(filename);
$("#attachmentFileerror span").css("display", "none");
var fileSize=0;
var maxFileSize = 10240000 // 10MB -> 10000 * 1024
fileSize = $("#" + "Attachment_AttachmentFile")[0].files[0].size //size in kb
if(fileSize>maxFileSize){
$("#Filesize").html("Please choose file less than 10MB");
$("#Filesize").css("display", "block");
$('#filename').val('');
}
else{
$("#Filesize").css("display", "none");
}
}
else
$("#Attachment_StorageName").val("");
}
You may want to consider explicitly clearing out the contents of your file <input> when the file that was added was too large. This should reset the onchange() event so that it will trigger again (as it will only be triggered if the actual file name is different) :
if(fileSize>maxFileSize){
$("#Filesize").html("Please choose file less than 10MB");
$("#Filesize").css("display", "block");
$('#filename').val('');
$("#Attachment_AttachmentFile").val('')'
}
Related
I found below code and trying to modify it to print an specific div element. Below code opens print dialog but shows empty screen. I dont know where to put my id of div element in the code.
print(){
var doc = new jsPDF("portrait", "mm", "a4");
doc.autoPrint();
const hiddFrame = document.createElement('iframe');
hiddFrame.style.position = 'fixed';
// "visibility: hidden" would trigger safety rules in some browsers like safariļ¼
// in which the iframe display in a pretty small size instead of hidden.
// here is some little hack ~
hiddFrame.style.width = '1px';
hiddFrame.style.height = '1px';
hiddFrame.style.opacity = '0.01';
const isSafari = /^((?!chrome|android).)*safari/i.test(window.navigator.userAgent);
if (isSafari) {
// fallback in safari
hiddFrame.onload = () => {
try {
hiddFrame.contentWindow.document.execCommand('print', false, null);
} catch (e) {
hiddFrame.contentWindow.print();
}
};
}
var blob = doc.output("blob");
window.open(URL.createObjectURL(blob), '_blank');
}
On IOS, when I close photoswipe to return to the page, it wont return to the scroll position I was at when I clicked the thumbnail.
Instead the page scrolls back to the # which was specified when I initially called the page.
For example if photoswipe is on www.somepage.html, and I navigate to the page using:
www.somepage.html#footer
and then scroll up and click a thumnail in #middle of page, on closing photoswipe, the page scrolls back down to the footer.
I've tried disabling history in the photswipe options, and i've also tried clearing the hash data from the url using:
//clear hash
//$(document).ready(function (e) {
// window.location.hash = '';
// window.history.pushState("", document.title, window.location.pathname);
//
//});
But none of it seems to work. If I navigate to the page without the # in the page, everthing is fine.
I'm guessing I may have to pass a variable in the url instead of the # and scroll to the div in question via javascript?
I already have the javascript in place to scroll, but I'm not sure how to read the variable from the url and then use it's value in Javascript.
If this is likely to be the best fix for the issue, could anyone give an example of the javascript code needed?
Here's my current scroll code:
$(function () {
$('a[href*=#]:not([href=#],[data-toggle],[data-target],[data-slide])').click(function () {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});
If anyone else has the same issue, I've managed to fix this by passing the div id to the page in the query string rather than using a #.
Here's the code:
$(window).ready(function () {
if (document.location.search.length) {
target = getUrlVars()["id"];
scrollToID('#' + target, 750);
} else {
return;
}
//target = $url().param('id');
//if (target == '') return;
});
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
// scroll function
function scrollToID(id, speed){
var offSet = 100;
var targetOffset = $(id).offset().top - offSet;
var mainNav = $('#main-nav');
$('html,body').animate({scrollTop:targetOffset}, speed);
if (mainNav.hasClass("open")) {
mainNav.css("height", "1px").removeClass("in").addClass("collapse");
mainNav.removeClass("open");
}
}
if (typeof console === "undefined") {
console = {
log: function() { }
};
}
I want to upload multiple files to rails server and want to show separate progress bar for each files. I am using ng-file-upload to upload files. files are getting uploaded but it showing progress for only last file and not for others..I am attaching my code. please help.
Angular controller code:
$scope.upload_file = function() {
var files = $scope.files;
var uploadUrl = base_url+"/upload_image";
var job_id = $scope.directive.id;
if(current_user.role == "third_party_vendor" || current_user.role == "employee")
{
var source = current_user.user_login+"-"+current_user.role;
}
else
{
var source = $scope.dataObj.source["user_login"]+"-"+$scope.dataObj.source["role"];
}
if(job_id === "" || job_id === undefined || files === undefined || files === ""){
error_notification("Please select a job and a file.");
return;
}
hideLoader();
$("#upload_resume_queue").modal('show');
var formData = new Array();
formData['job_id'] = job_id;
formData['context'] = "inside_page";
formData['source'] = source;
for (var i = 0; i < files.length; i++) {
var file = files[i];
console.log(file.name);
$scope.upload = $upload.upload({
url: uploadUrl,
data:{myObj: formData},
file: file,
}).progress(function(evt) {
//console.log('percent: ' +parseInt(100.0 * evt.loaded / evt.total));
file.progress = Math.round(evt.loaded * 100 / evt.total)
}).success(function(responseText) {
hideLoader();
try{
var response = responseText;
}catch(e){
error_notification("Invalid Excel file Imported.");
return;
}
if(response.status==='wrong_content_type')
error_notification("Please upload a valid file format.",0);
if(response.status==='job_application_present'){
$scope.duplicate = true;
$scope.jobID = job_id;
$scope.user_id = response.user_id;
$scope.application_id = response.application_id;
//showModal('#duplicate_application_modal');
error_notification("Job Application already present for this user and job.",0);
}
if(response.status==='invalid_email')
error_notification("The email in the resume is an invalid one.",0);
if(response.status==='success')
success_notification("The uploaded resume has been parsed.",0);
});
}
};
Html code:
<input type="file" class="required file_browse" ng-file-select="" ng-model="files" multiple />
I am not able test the following but, I think I found what is wrong.
In JavaScript variables are scoped to functions. So, in your for loop you change value of file in var file = files[i]; line. At the end, after for loop finished the value of file is the last file.
At some point .progress event is fired by ng-file-upload to notify you about progress (for one of the files). And you update the status, but, since file has the value of the last file, not the one you expected to be, the last file's status is being updated.
That's why only the last file updated. To solve, you need to access the correct file for each progress event. To do this, you can keep file variable using an anonymous function.
for (var i = 0; i < files.length; i++) {
(function(file) {
console.log(file.name);
$scope.upload = $upload.upload({
url: uploadUrl,
data:{myObj: formData},
file: file,
}).progress(function(evt) {
//console.log('percent: ' +parseInt(100.0 * evt.loaded / evt.total));
file.progress = Math.round(evt.loaded * 100 / evt.total)
}).success(function(responseText) {
// the other stuff
});
})(files[i])
}
In your code there may be other problems related to variable scopes and javascript event loop. For more information please take a look at this.
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...
Below is the code I'm using to place a blur event on a text box in my ASP MVC 3 view. The code works fine if #MailingState is empty, however it cannot tell if #channelName is empty.
For example, if #channelName is empty but #MailingState is not, then when I place a value in #MailingZip the getDrmTerritory Ajax call is fired off every time.
Here is the jQuery
$('#MailingZip').blur(function () {
if ($('#AlignmentM').is(':checked')) {
if ($('#MailingState').val() != "" && $('#channelName').html() != "") {
getDrmTerritory($('#MailingZip').val(), $('#MailingState').val(), $('#channelName').html());
}
}
});
and here is the HTML for the #channelName segment it is checking
<div id="channelName" class="M-display-field">
#*this will be updated via ajax from Market Segment *#
#Html.DisplayFor(model => model.Channel, new { style = "margin-left: 300px;" } )
</div>
The section mentioned in the comments is updated via another jQuery method that looks like this
function ChangeChannel() {
//this function called if Market Segment changes, to update the channel
var pendistcode = document.getElementById('Pendist');
if (pendistcode == null) alert('Error: Cannot find Market Segment control');
//alert('!pendistcode value is ' + pendistcode.value);
$.ajax({
type: 'POST',
url: '/AgentTransmission/GetChannel/',
data: { pendist: pendistcode.value },
success: function (data) {
// alert("success: " + data);
$('#channelName').html(data);
$('#Channel').val(data);
},
error: function (data) {
alert("failure to obtain Channel name");
}
});
CheckTerritory('channel');
} //end ChangeChannel
That jQuery method (ChangeChannel) appends text to the channelName div which, when rendered with a value in it, looks like this
Here is the HTML you get when you inspect the Life Sales from that picture
You can check if #channelName is empty or not like this:
if ( $('#channelName').is(':empty') )
and combine with your code like this:
if ($('#MailingState').val() != "" && !$('#channelName').is(':empty'))
I put in an alert statment to find out channelName's length and it came back with 35, so there must obviously be some white space that gets rendered each time. I had to change the statement to the following to trim out the whitespace and add the variable to the condition.
var channel = $.trim($('#channelName').html());
if ($('#MailingState').val() != "" && channel != "")