passing variables in 'bind' trigger - binding

I'm using backbone.js with jquery droppable.
The problem I'm having is that upon entering the .droppable function, the 'this' no longer refers to the backbone object. I've read Yehuda Katz article about understanding 'this', but I still don't understand how to 'solve' the problem.
http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
I thought the way out of the issue would be to just bind the model changes event to trigger a method, but I need to pass a variable to the method, and I can't figure out how to do that.
here's an excerpt of my code. On the line MyApp.User.Trigger('new_class');, is there a way to add a variable?? How do I put that in the bind event??
initialize: function(){
user = MyApp.User;
MyApp.User.bind('new_class',this.save, this);
this.render();
},
render: function(){
$('li.empty').droppable({
hoverClass: "ui-state-active",
start: function(e){
$(this).click('unbind');
},
drop: function(event, ui){
var add_class_to_user = $(this).attr('data-id');
MyApp.User.Trigger('new_class');
}
})
},
save: function(class){
var add_class = new Myapp.Model.Class({
class_id: class,
user_id: user.id
});
add_class.save()
}

I'm not sure i understand what you are asking,
so i will answer both :
on the context of This:
and why don't you store reference this into a new variable?
...
render: function(){
// here we put this in a new variable myView.
var myView = this;
$('li.empty').droppable({
hoverClass: "ui-state-active",
start: function(e){
$(this).click('unbind');
// here you can use myView instead of this, if you want something from your view.
myView.model.set({'name', 'newname'});
},
drop: function(event, ui){
var add_class_to_user = $(this).attr('data-id');
MyApp.User.Trigger('new_class');
}
})
},
...
on the question of passing parameters to an event
...
render: function(){
$('li.empty').droppable({
hoverClass: "ui-state-active",
start: function(e){
$(this).click('unbind');
},
drop: function(event, ui){
var add_class_to_user = $(this).attr('data-id');
MyApp.User.Trigger('new_class', add_class_to_user);
}
})
},
...
in your user model you should of course do this: (or more likely in another view, you can bind to that event on the user model)
MyApp.User.bind('new_class', function(myparameter) {
alert('yay, my parameter is:' + myparameter);
$('#myElement').addClass(myparameter);
});
example in jsfiddle: can be found here....
http://jsfiddle.net/saelfaer/MWuHj/

Related

React-Rails: Load initial array state with ajax

I've been following along some tutorials with React and i'm starting out building an application on my own. I've come across a situation regarding components and i'm wondering if theres a best practice for this scenario. Please note, I'm just using react-rails; no flux or whatever for now.
setting the initial state with an array whose values get set through ajax and have that array display in the initial render
Here's what i'm trying to do: (stripped down for simplicity)
var ShoutList = React.createClass({
getInitialState: function(){
return {shouts: []};
},
componentDidMount: function(){
var component = this;
$.get('/api/shouts.json', function(data){
component.setState({shouts: data});
});
},
render: function(){
return (
<div>
{this.state.shouts[0].shout}
</div>);
}
});
So if I have this right, the order in which things are run go as follows:
On load, getInitialState sets shouts to an empty array
Render gets called and errors out because of trying to access the shout property on an empty array
ComponentDidMount gets called and sets the state of shouts to the data received from the ajax call. **I get an error when I try to do this in ComponentWillMount **
Render gets called again because the state has changed, but this time shouts[0].shout would contain data.
So I error out at step 2 and my work around is as follows:
var ShoutList = React.createClass({
getInitialState: function(){
return {shouts: []};
},
componentDidMount: function(){
var component = this;
$.get('/api/shouts.json', function(data){
component.setState({shouts: data});
});
},
emptyShouts: function(){
return(<div>No Shouts Yet!</div>);
},
shoutsList: function(){
return(<div>{this.state.shouts[0].shout}</div>);
},
render: function(){
if(this.state.shouts.length > 0){
return this.shoutsList();
}else {
return this.emptyShouts();
}
}
});
This works exactly like I need it to, but is there a better way of setting the initial state's array value with ajax and having it load in the initial render without having to do this if statement?
Thanks!
Without using Flux, I'd say your implementation is one of the few ways to get around this problem. Another way would be to have the logic before your render's return:
...
render: function () {
var renderedShout;
if (typeof this.state.shouts[0] === "undefined") {
renderedShout = <div>No Shouts Yet!</div>;
} else {
renderedShout = <div>{this.state.shouts[0].shout}</div>;
}
return renderedShout;
}
The advantage of doing it this way is that you will only have one return which could make it clearer for a reader in the long run.
If you want, you can try this change in your pre-edit code:
var ShoutList = React.createClass({
getInitialState: function(){
return {shouts: []};
},
componentDidMount: function(){
var component = this;
$.get('/api/shouts.json', function(data){
component.setState({shouts: data});
}.bind(this), 'json');
},
render: function(){
return (
<div>
{this.state.shouts[0].shout}
</div>);
}
});
bind your $.get call to the component making the call. It should work as expected from there.

How to listen to jquery-ui events within a Marionette.ItemView

I do have some dynamically loaded ItemViews that load into layout regions. The templates contain jquery-ui components, e.g. a slider '#mySlider'. I do initialize the component with the Maronette.Region onShow event. My view looks like this:
var MyView = Backbone.Marionette.ItemView.extend({
template: '#my-tpl',
initialize: function() {
_.bindAll(this);
},
onShow: function(){
$('#mySlider').slider({
min: 1,
max: 10,
slide: function( event, ui ) {
//some code here to update the the Views model, like
//this.model.set({'stroke': ui.value});
}});
.....
Obviously this does not work, because the this.model is not defined like so within #mySlider. How do I properly do this and capture the jquery-ui events as as Marionette.ItemView event?
A simple closure will do.
fiddle: http://jsfiddle.net/puleos/z7QsC/
var SliderView = Marionette.ItemView.extend({
template: "#slide-template",
initialize: function() {
_.bindAll(this);
},
onShow: function() {
var self = this;
$('#slider-range').slider({
min: 1,
max: 10,
slide: self.updateSlider
});
},
updateSlider: function(event, ui) {
this.model.set({'stroke': ui.value});
}
});

CKEditor 4 and jQuery UI sortable removes content after sorting

I've ran into an issue with CKEditor 4 and jQuery UI's sortable method where if I sort a container that has a CKEditor instance, it removes the value and throws an error "Uncaught TypeError: Cannot call method 'getSelection' of undefined". It also makes the editor uneditable. I was able to get around this in CKEditor 3 with one of the following hacks found here:
CKEditor freezes on jQuery UI Reorder
In looking at the Chrome DOM inspector, it appears that the contents of the iframe are removed.
Below is crude test code:
<html>
<head>
<title>test</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.24/jquery-ui.min.js"></script>
<script src="ckeditor.js"></script>
<script type="text/javascript">
$(function(){
var tmpStore = {};
$('#sortable').sortable({
cursor: 'move',
// Hack that use to work on V3 but not on V4:
// https://stackoverflow.com/questions/3379653/ckeditor-freezes-on-jquery-ui-reorder
start:function (event,ui) {
$('textarea').each(function(){
var id = $(this).attr('id');
tmpStore[id] = CKEDITOR.instances[id].getData();
})
},
stop: function(event, ui) {
$('textarea').each(function(){
var id = $(this).attr('id');
CKEDITOR.instances[id].setData(tmpStore[id]);
})
}
});
$('textarea').each(function(){
var ckId = $(this).attr('id');
config = {};
CKEDITOR.replace(ckId, config);
})
})
li { padding: 10px; width: 800px; height: 300px; }
</head>
<body>
<ul id="sortable">
<li><textarea id="test1" name="test1">test1</textarea></li>
<li><textarea id="test2" name="test1">test2</textarea></li>
<li><textarea id="test3" name="test1">test3</textarea></li>
</ul>
</body>
</html>
I was facing the same problem and have fixed based on answers here. Please see fiddles below
ISSUE:
https://jsfiddle.net/33qt24L9/1/
$(function() {
$( "#sortable" ).sortable({
placeholder: "ui-state-highlight"
});
CKEDITOR.replace( 'editor1' );
CKEDITOR.replace( 'editor2' );
CKEDITOR.replace( 'editor3' );
CKEDITOR.replace( 'editor4' );
});
RESOLVED ISSUE: https://jsfiddle.net/57djq2bh/2/
$(function() {
$( "#sortable" ).sortable({
placeholder: "ui-state-highlight",
start: function (event, ui)
{
var id_textarea = ui.item.find(".ckeditor").attr("id");
CKEDITOR.instances[id_textarea].destroy();
},
stop: function (event, ui)
{
var id_textarea = ui.item.find(".ckeditor").attr("id");
CKEDITOR.replace(id_textarea);
}
});
CKEDITOR.replace( 'editor1' );
CKEDITOR.replace( 'editor2' );
CKEDITOR.replace( 'editor3' );
CKEDITOR.replace( 'editor4' );
});
EDIT: If like me you had seperate configs per editor here's updated code that will help:
start: function (event, ui)
{
$('.wysiwyg', ui.item).each(function(){
var tagId = $(this).attr('id');
var ckeClone = $(this).next('.cke').clone().addClass('cloned');
ckeConfigs[tagId] = CKEDITOR.instances[tagId].config;
CKEDITOR.instances[tagId].destroy();
$(this).hide().after(ckeClone);
});
},
stop: function(event, ui) {
// for each textarea init ckeditor anew and remove the clone
$('.wysiwyg', ui.item).each(function(){
var tagId = $(this).attr('id');
CKEDITOR.replace(tagId, ckeConfigs[tagId]);
$(this).next('.cloned').remove();
});
}
Thanks to: https://github.com/trsteel88/TrsteelCkeditorBundle/issues/53
You have to re-create CKEditor once underlying DOM structure is modified. Save editor data with editor.getData() before editor.destroy() and restore contents with editor.setData( data ) once you create a new instance. There's no other way to fix this since CKEditor strongly depends on the DOM structure.
Remove CKEditor start Sortable
var ckeConfigs = [];
$('.ui-sortable').sortable({
start:function (event,ui) {
$('.lineItemCommentBox', ui.item).each(function(){
var tagId = $(this).attr('id');
ckeConfigs[tagId] = CKEDITOR.instances[tagId].config;
CKEDITOR.instances[tagId].destroy();
});
},
stop: function(event, ui) {
$('.lineItemCommentBox', ui.item).each(function(){
var tagId = $(this).attr('id');
CKEDITOR.replace(tagId, ckeConfigs[tagId]);
});
}
});
The code below works for me, we have to destroy the editor on start and recreate it when the drag is ended getting the value of the textarea which come the data from :
jQuery(function($)
{
var panelList = $("#nameofyourdiv");
panelList.sortable(
{
handle: ".classofyourdivforsorting",
start: function (event, ui)
{
var id_textarea = ui.item.find("textarea").attr("id");
CKEDITOR.instances[id_textarea].destroy();
}
stop: function (event, ui)
{
var id_textarea = ui.item.find("textarea").attr("id");
CKEDITOR.replace(id_textarea);
}
});
});
Hope it helps someone.
i ve solved this kind of problem by instantiating the CKEditor after having opened the jquery dialog
I had the Similar issue with CKEDITOR , The Code Below worked for me. Destroy the Ckeditor instance and Remove the Ckeditor and when the dragging ends replace the current textarea with Ckeditor again
$("#sortable").sortable({
items: '.dynamic',
start: function (event , ui) {
var editorId = $(ui.item).find('.ckeditor').attr('id');// get the id of your Ckeditor
editorInstance = CKEDITOR.instances[editorId]; // Get the Ckeditor Instance
editorInstance.destroy(); // Destroy it
CKEDITOR.remove(editorId);// Remove it
},
stop: function(event, ui) {
var editorId = $(ui.item).find('.ckeditor').attr('id');// Get the Id of your Ckeditor
CKEDITOR.replace(editorId);// Replace it
}
}
});
$("#sortable").disableSelection();
Here #sortable is the id of the DIV which is sortable and '.dynamic' is the Class assigned to DIV that are eligible to sort and '.ckeditor' is the class for the Textarea .
I got my solution from Here , Hope this helps for someone in future.
I simply use the ckeditorOff() and ckeditorOn() functions to keep data and re/de-instance ckeditor instances during movement.
$('#sortable').sortable({
cursor: 'move',
start:function (event,ui) {
if(typeof ckeditorOff=='function')ckeditorOff();
},
stop: function(event, ui) {
if(typeof ckeditorOn=='function')ckeditorOn();
}
});
I put the typeof ckeditorOff statement to make the code compatible with future versions of ckeditor in case they decide to remove these two functions.

Pass variable by Post method from JQuery UI Autocomplete to PHP page

I have two JQuery UI autocomplete input fields.
When an option is selected in the first one, the value of the selection will be used as condition for a database query that will send the source data for the second autocomplete field.
My problem is how do I send the value of the first selection to the PHP page via Post method?
The code so far is shown below (this code is from a tutorial which used the GET method; but I want to use Post):
<script>
$("input#divisions").autocomplete ({
//this is the first input
source : [
{ value: "81", label: "City1" },
{ value: "82", label: "City2" },
{ value: "83", label: "City3" } ],
minLength : 0,
select: function(event, ui) {
$('#divisions').val(ui.item.label);
return false;
},
focus: function(event, ui){
$('#divisions').val(ui.item.label);
return false;
},
change: function(event, ui){
//the tutorial has this value sent by variables in the URL; I want the selection value sent by POST. How can I change this?
c_t_v_choices = "c_t_v_choices.php?filter=" + ui.item.value;
$("#c_t_v").autocomplete("option", "source", c_t_v_choices);
}
}).focus (function (event)
{
$(this).autocomplete ("search", "");
});
$("#c_t_v").autocomplete({
source: "",
minLength: 2,
select: function(event,ui){
//$('#city').val(ui.item.city);
}
});
</script>
Can anyone please help?
Dont hesitate to let me know if you have any questions.
The solution I found for this is to use AJAX to create the source when a selection is made in the first autocomplete.
Sample code can be found here: https://stackoverflow.com/questions/12715204/assigning-source-for-jquery-autocomplete

Prevent droppable from accepting when it is overlaid by another element in jqueryUI?

I've positioned 2 containers such a way that container one overlays on another. But when I drop an item onto the container one, the dropped item goes into both containers. How do I restrict that?
//JS Code:
$("div.draggable").draggable({
helper: "clone",
cursor: "move",
containment: 'body'
});
$("div.droppable").droppable({
addClasses: false,
drop: function (event, ui) {
var $draggable = $(ui.draggable),
$droppable = $(this);
$droppable.html($draggable.clone());
}
});
Demo: http://jsfiddle.net/HL8VR/
Thanks #Nal but nothing works for me, So I Created this hack.
I store some data with an element once an item is dropped onto it. Unfortunately the data is stored with both Droppables which I could not isolate.
$("div.droppable").droppable({
addClasses: false,
drop: function (event, ui) {
var $draggable = $(ui.draggable),
$droppable = $(this);
$droppable.data({'drop': true, 'draggable': $draggable});
}
});
But once the item is dropped, I could figure out which droppable I'm hovering on.
$('div.droppable').hover(function() {
if ($(this).data('drop') === true) {
$(this).html($($(this).data('draggable')).clone());
// Clears the data from both droppables to avoid duplicating the item in them
$('div.droppable').data({'drop': false, 'draggable': false});
}
});
Demo: http://jsfiddle.net/codef0rmer/AVQVs/

Resources