The method is called in the ready event. On the first call it works. When the modal is closed, I am destroying the cropper - cropper.destroy() . After opening the modal second time, the cropper is initialized again but this time cropper.getCroppedCanvas() returns null
let cropper = new Cropper(image, {
dragMode: 'move',
aspectRatio: ratio,
restore: false,
guides: false,
center: false,
highlight: false,
cropBoxMovable: false,
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
ready: function () {
modal.find(".save").on("click", function () {
console.log(cropper.getCroppedCanvas())
cropper.getCroppedCanvas().toBlob(function (blob) {
let formData = new FormData()
formData.append("croppedImage", blob)
jQuery.ajax({
method: "post",
url: "index.php?option=com_image_slideshows&task=slideshow.saveCroppedImage",
data: formData,
processData: false,
contentType: false
})
.done(function (response) {
modal.modal("hide")
})
})
})
cropper.crop()
}
})
On modal closing this happens:
modal.on("hidden.bs.modal", function () {
cropper.destroy()
jQuery("#cropper-modal .modal-body").html(
jQuery("<img>", {
id: "image",
"class": "cropper-hidden"
})
)
})
My guess would be that the cropper variable you set initially:
let cropper = new Cropper(...)
is still being referenced in your ready function the second time around. I would first try ensuring that the cropper variable is set to null after cropper.destroy() is called.
You could also try accessing the correct Cropper instance in your ready function by accessing this.cropper, for example:
ready: function () {
modal.find(".save").on("click", function () {
console.log(this.cropper.getCroppedCanvas());
}
}
Besides calling destroy() method you also need to reinitialize the event listeners.
I think that event listeners still hold a reference to the old cropper so you need to unbind() them first and create them again on every ready() function call. I also suggest using this pointer instead of variable cropper to ensure you access the current instance.
ready: function () {
var that = this;
modal.find(".save").unbind('click');
modal.find(".save").on("click", function () {
console.log(that.cropper.getCroppedCanvas());
}
}
#jmfolds is correct, it is important that you reference this.cropper.
However, that is not enough. You have to verify that the cropper is ready for the second cropping instance even if the function is defined in the "ready" event of the cropper and assuming that the first cropper is destroyed.
You can do that easily using an if statement:
if (this.cropper.ready === true) {
// Your code as is
} else {
return
}
This solved my problem when I was implementing it.
In your destroy function do you remove the .on event too?
modal.find(".save").off();
After the cropper.destroy(); did you validate that it is really destroyed?
maybe set in the variable cropper too null or undefined too after destroy.
So my 2 cents, It looks all correct so far I see.
Related
I'm using a jquery script called magnific popup and I'm trying to access a variable I created in a callback function, inside another callback function but I can't work out how to do it. My code for the magnific init looks like this:
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
var communityClass = item.community;
console.log(item.community);
// this ^ does actually print the data-community
console.log('Parsing content. Item object that is being parsed:', item);
},
resize: function() {
console.log('Popup resized');
// resize event triggers only when height is changed or layout forced
$('.mfp-bg').addClass(communityClass);
}
}
});
If I try and set $('.mfp-bg').addClass(communityClass); or $('.mfp-bg').addClass(item.community); I get a Uncaught ReferenceError: communityClass is not defined.
I can't apply a class to mfp-bg inside elementParse as that element hasn't been created yet.
I know that I can't use variables from different functions in javascript, but I'm a bit stuck at this point on how I can actually use the data-community attribute inside the resize callback, because it seems like I can only create the variable inside the elementParse callback?
Any help would be much appreciated, cheers.
You could create a global variable, outside the function and assign item.community to it. That way you will be able to access it in the other callback aswell
For example:
var communityClass;
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
communityClass = item.community;
console.log(item.community);
// this ^ does actually print the data-community
console.log('Parsing content. Item object that is being parsed:', item);
},
resize: function() {
console.log('Popup resized');
// resize event triggers only when height is changed or layout forced
$('.mfp-bg').addClass(communityClass);
}
}
});
I realised after console.logging this there was already a currItem bit I could access, so I changed the code to this and it works fine now.
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
},
resize: function() {
$('.mfp-bg').addClass(this.currItem.community);
}
}
});
I'm using require.js with backbone.js to structure my app. In one of my views:
define(['backbone', 'models/message', 'text!templates/message-send.html'], function (Backbone, Message, messageSendTemplate) {
var MessageSendView = Backbone.View.extend({
el: $('#send-message'),
template: _.template(messageSendTemplate),
events: {
"click #send": "sendMessage",
"keypress #field": "sendMessageOnEnter",
},
initialize: function () {
_.bindAll(this,'render', 'sendMessage', 'sendMessageOnEnter');
this.render();
},
render: function () {
this.$el.html(this.template);
this.delegateEvents();
return this;
},
sendMessage: function () {
var Message = Message.extend({
noIoBind: true
});
var attrs = {
message: this.$('#field').val(),
username: this.$('#username').text()
};
var message = new Message(attrs);
message.save();
/*
socket.emit('message:create', {
message: this.$('#field').val(),
username: this.$('#username').text()
});
*/
this.$('#field').val("");
},
sendMessageOnEnter: function (e) {
if (e.keyCode == 13) {
this.sendMessage();
}
}
});
return MessageSendView;
});
When keypress event is triggered by jquery and sendMessage function is called - for some reason Message model is undefined, although when this view is first loaded by require.js it is available. Any hints?
Thanks
Please see my inline comments:
sendMessage: function () {
// first you declare a Message object, default to undefined
// then you refrence to a Message variable from the function scope, which will in turn reference to your Message variable defined in step 1
// then you call extend method of this referenced Message variable which is currently undefined, so you see the point
var Message = Message.extend({
noIoBind: true
});
// to correct, you can rename Message to other name, e.g.
var MessageNoIOBind = Message.extend ...
...
},
My guess is that you've bound sendMessageOnEnter as a keypress event handler somewhere else in your code. By doing this, you will change the context of this upon the bound event handler's function being called. Basically, when you call this.sendMessage(), this is no longer your MessageSendView object, it's more than likely the jQuery element you've bound the keypress event to. Since you're using jQuery, you could more than likely solve this by using $.proxy to bind your sendMessageOnEnter function to the correct context. Something like: (note - this was not tested at all)
var view = new MessageSendView();
$('input').keypress(function() {
$.proxy(view.sendMessageOnEnter, view);
});
I hope this helps, here is a bit more reading for you. Happy coding!
Binding Scopes in JavaScript
$.proxy
I have the follwoing JQuery/AJAX code:
<script>
$('.warning-dialog').click(function () {
alert($(this).data("id"));
});
$(function () {
//twitter bootstrap script
$("button#delete").click(function () {
$.ajax({
type: "GET",
url: "deleteArticleType.php",
data: { 'typeID': $('.warning-dialog').data("id") },
success: function (msg) {
$("#thanks").html(msg)
$("#form-content").modal('hide');
},
error: function () {
alert("failure");
}
});
});
});
</script>
The first function gets the data-id of a button . The second function calls a PHP page and with the method GET should get the value from the first function.
I tried the code above but it didn't work.
My question is why and how can I fix it?
If these are two separate events, disconnected in time and you want to store the value from the first click and then use it in the second click, then you will have to store it somewhere. There are several options, the simplest being a variable.
$(function () {
var lastClickId;
$('.warning-dialog').click(function () {
lastClickId = $(this).data("id");
});
//twitter bootstrap script
// FIXME: you need to add logic here for what to do if lastClickId isn't set yet
// or create a default behavior in that case
$("button#delete").click(function () {
$.ajax({
type: "GET",
url: "deleteArticleType.php",
data: { 'typeID': lastClickId },
success: function (msg) {
$("#thanks").html(msg)
$("#form-content").modal('hide');
},
error: function () {
alert("failure");
}
});
});
});
Since it looks like you are requiring the first click to happen before the second click can have something to operate on, then you should probably either modify the UI to use different types of controls or you will need to add some error handling if the user doesn't click in the right order.
Actually it should have worked using $('.warning-dialog').data("id")
If your page contains only a single class warning-dialog, you approach will be worked. It seems you're referring this class to many elements.
I have a very simple scenario where I want to POST the form using JQuery's ajax() method but perform request some confirmation from the user before actually performing the post.
I have a JQuery UI dialog with localized buttons in place in case you wonder what all the code with buttons below is about.
This is my code so far:
var i18n_deleteButtons = {};
i18n_deleteButtons[i18n.dialogs_continue] = function () {
return true;
$(this).dialog('close');
};
i18n_deleteButtons[i18n.dialogs_cancel] = function () {
return false;
$(this).dialog('close');
};
$('#delete-dialog').dialog({
open: function () {
$(this).parents('.ui-dialog-buttonpane button:eq(1)').focus();
},
autoOpen: false,
width: 400,
resizable: false,
modal: true,
buttons: i18n_deleteButtons
});
$("form#form_attachments").submit(function (event) {
/* stop form from submitting normally */
event.preventDefault();
/* get some values from elements on the page: */
var $form = $(this), url = $form.attr('action');
// build array of IDs to delete (from checked rows)
var jdata = { 'attachmentIdsToDelete': [] };
$('input:checked').each(function () {
jdata['attachmentIdsToDelete'].push($(this).val());
})
$.ajax({
beforeSend: function (request) {
// Return false if you don't want the form submit.
$('#delete-dialog').dialog('open');
},
url: url,
type: "POST",
dataType: "json",
data: jdata,
traditional: true,
success: function (msg) {
$('#msg').html(msg);
}
});
});
The dialog actually opens up fine but clicking at any of the buttons results in nothing happening. The post doesn't happen and the dialog does not close regardless of which button was clicked.
How can I make this work?
Why not move the actual ajax call from the submit handler and trigger it when pushing a button in the delete dialog?
You could store you ajax call in a separat function and pass this functions and the url as parameters to the confirmation routine.
[pseudo code]
function callAjax() {
...
}
function submitHandler() {
confirmDialog({callback:callAjax,param:you_url});
}
function confirmDialog(obj) {
$('#yes_btn').click(function() {
// call the callback
obj.callback(obj.param);
});
$('#no_btn').click(function() {
// just close the dialog
});
}
This way the confirmDialog don't have to know anything about the ajax call, it will just execute the callback with the given parameter when the user clicks ok.
Because the default jquery UI dialog is a bit cumbersome for regular use and trouble to configure in some custom scenarios I looked around and found this Easy Confirm plugin which is based upon jquery&jquery UI default stuff. It also allows for very simple internationalization.
https://github.com/wiggin/jQuery-Easy-Confirm-Dialog-plugin
I have created a class in mootools that when the initialize() method is first called it creates a div elements which is then appended to the document.body. I then attach a context menu handler which will call functions when an option from the context menu is selected in the browser.
The trouble I am having is that the context menu handler will not actually call any functions and I can't quite figure out why and was wondering if anyone here could spot the problem?
Here is the class I have created and the attached context-menu handler, some of the other code has been removed for brevity:
var uml_Canvas = new Class({
initialize: function()
{
this.mainCanvasDiv = document.createElement("div");
this.mainCanvasDiv.id = "mainCanvas";
this.mainAppDiv.appendChild(this.mainCanvasDiv);
this.paper = Raphael(this.mainCanvasDiv.id, 500, 400);
this.paper.draggable.enable();
$("#"+this.mainCanvasDiv.id).contextMenu('canvasPanel_Menu',
{
bindings:
{
'clear': function(t)
{
this.clearPaper();
}
}
});
},
clearPaper : function()
{
this.paper.clear();
}
});
So a quick overview, an object is created which creates a div and then appends it to the body. The div then has a context-menu assigned. When the 'clear' option is called the method clearPaper() should be called be for some reason it is not. If, however, I replace the this.clearPaper(); line with a simple alert() call, it does indeed run.
Can anyone see a reason why it is not possible to call a method?
BTW the error I get is this.clearPaper is not a function if that helps.
Try binding "this" to your clear function:
'clear': function(t)
{
this.clearPaper();
}.bind(this)
This takes the "this" scope and allows the anonymous function to use it as if it were a member of that class.
Note that you have to do this whenever you try to use "this." inside of any anonymous function. For instance, if you have inside a class:
method: function() {
button.addEvent('click', function(e) {
new Request({
onComplete: function(res) {
this.process_result(res);
}
}).send();
});
},
process_results: function(res) {...}
You have to bind all the way down:
method: function() {
button.addEvent('click', function(e) {
new Request({
onComplete: function(res) {
this.process_result(res);
}.bind(this)
}).send();
}.bind(this));
},
process_results: function(res) {...}
Notice the new bind()s on the event function and the onComplete function. It may seem like an annoying extra step, but without doing this, you'd have scope free-for-all. Mootools makes it extremely easy to take your class scope and attach it to an anonymous function.