Merry Christmas, everyone!
I want to do something when the source element or target element of a joint.dia.Link is changed. Firstly I tried to put the code in the callback function of 'change:source' and 'change:target' events. However, it turns out that those callback functions are called as soon as the link's position changes, instead of being called when the source element or target element changes. Then I tried to put the code in the LinkView.pointup() function, by adding a tag, which is set in the callback function of 'change:source' and 'change:target' events, to indicate the changed element. The resulted code looks like this:
link.on('change:source', function(){this.src_changed = true;});
link.on('change:target', function(){this.dest_changed = true;});
joint.shapes.custom.ModelLink = joint.dia.Link.extend({
defaults: joint.util.deepSupplement({
type: 'custom.ModelLink',
}, joint.dia.Link.prototype.defaults)
});
joint.shapes.custom.ModelLinkView = joint.dia.LinkView.extend({
pointerdown: function () {
joint.dia.LinkView.prototype.pointerdown.apply(this, arguments);
},
pointermove: function () {
joint.dia.LinkView.prototype.pointermove.apply(this, arguments);
},
pointerup: function (evt, x, y) {
var link = this.model;
if(link.src_changed) { // do something}
if(link.dest_changed) {// do something}
joint.dia.LinkView.prototype.pointerup.apply(this, arguments);
}
});
However, I found src_changed and dest_changed are both set to true sometimes when I am just dragging one end of the link. Why does this happen? How can I fix this? Or any new approach to do some response to the change of source element or target element?
Besides, After I reset the events of joint.shapes.uml.State using model.set('events', events), the text doesnot change on the graph? How can I refresh graph to show the changed state element?
Thanks very much!
The change:source and change:target events are indeed triggered also when the position of the arrowheads changes. In general, the source and target of a link can either be a point (an object with x and y properties) or an element (and in the near future also a link) - an object with id property pointing to the linked element. If you're only interested in source/target being an element, you can just check in your handlers for the change:source and change:target events whether the source/target of the link contains the id property:
if (this.get('source').id) { /*... do something ...*/ }
Related
I extend a Control to create a new custom control in UI5 and this control renders a tree as UL items nicely. Now I need to implement a collapse/expand within that tree. Hence my renderer writes a tag like
<a class="json-toggle" onclick="_ontoggle"></a>
and within that _ontoggle function I will handle the collapse/expand logic.
No matter where I place the _ontoggle function in the control, I get the error "Uncaught ReferenceError: _ontoggle is not defined"
I am missing something obvious but I can't find what it is.
At the moment I have placed a function inside the
return Control.extend("mycontrol",
{_onToggle: function(event) {},
...
Please note that this event is not one the control should expose as new event. It is purely for the internals of how the control reacts to a click event.
I read things about bind and the such but nothing that made sense for this use case.
Took me a few days to crack that, hence would like to provide you with a few pointers.
There are obviously many ways to do that, but I wanted to make that as standard as possible.
The best suggestion I found was to use the ui5 Dialog control as sample. It consists of internal buttons and hence is similar to my requirement: Render something that does something on click.
https://github.com/SAP/openui5/blob/master/src/sap.ui.commons/src/sap/ui/commons/Dialog.js
In short, the solution is
1) The
<a class="json-toggle" href></a>
should not have an onclick. Neither in the tag nor by adding such via jQuery.
2) The control's javascript code should look like:
sap.ui.define(
[ 'sap/ui/core/Control' ],
function(Control) {
var control = Control.extend(
"com.controlname",
{
metadata : {
...
},
renderer : function(oRm, oControl) {
...
},
init : function() {
var libraryPath = jQuery.sap.getModulePath("mylib");
jQuery.sap.includeStyleSheet(libraryPath + "/MyControl.css");
},
onAfterRendering : function(arguments) {
if (sap.ui.core.Control.prototype.onAfterRendering) {
sap.ui.core.Control.prototype.onAfterRendering.apply(this, arguments);
}
},
});
control.prototype.onclick = function (oEvent) {
var target = oEvent.target;
return false;
};
return control;
});
Nothing in the init(), nothing in the onAfterRendering(), renderer() outputs the html. So far there is nothing special.
The only thing related with the onClick is the control.prototype.onclick. The variable "target" is the html tag that was clicked.
I'm using the .each method with the .getJSON method to print out objects in a JSON file. This works fine, however I am unable to add a click function to an element that has been printed out. I am trying to bind a function to the div with 'click' ID.
var loadData = function () {
$.getJSON("profiles2.json", function (data) {
var html = [];
html.push("<div id='click'>Click here</div>");
$.each(data.profiles, function (firstIndex, firstLevel) {
html.push("<h2>" + firstLevel.profileGroup + "</h2>");
});
$("#data").html(html.join(''));
});
};
$(document).ready(function () {
loadData();
$("#click").click(function () {
console.log('clicked');
});
});
$.getJSON() (like other Ajax methods) is asynchronous, so it returns immediately before the results have come back. So your loadData() method also returns immediately and you then try to bind a handler to an element not yet added.
Move the .click(...) binding into the callback of $.getJSON(), after adding the element(s), and it will work.
Alternatively, use a delegated event handler:
$("#data").on("click", "#click", function() {
console.log('clicked');
});
...which actually binds the handler to the parent element that does exist at the time. When a click occurs it then tests whether it was on an element that matched the selector in the second parameter.
And as an aside, don't bind click handlers to divs unless you don't care about people who are physically unable to (or simply choose not to) use a mouse or other pointing device. Use anchor elements (styled as you see fit) so that they're "click"-accessible via the keyboard and the mouse.
$.getJSON is an asynchronous call and probably hasn't finished by the time you are trying to bind to the element that it injects into your DOM. Put your binding inside the $.getJSON call after you append the element to the page at the bottom.
How do you keep track of your UI elements in Titanium? Say you have a window with a TableView that has some Switches (on/off) in it and you'd like to reference the changed switch onchange with a generic event listener. There's the property event.source, but you still don't really know what field of a form was just toggled, you just have a reference to the element. Is there a way to give the element an ID, as you would with a radiobutton in JavaScript?
Up to now, registered each form UI element in a dictionary, and saved all the values at once, looping through the dictionary and getting each object value. But now I'd like to do this onchange, and I can't find any other way to do it than create a specific callback function for each element (which I'd really rather not).
just assign and id to the element... all of these other solution CAN work, but they seem to be over kill for what you are asking for.
// create switch with id
var switcher0 = Ti.Ui.createSwitch({id:"switch1"});
then inside your event listener
myform.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "switch1" ) {
// do some magic!!
}
});
A simple solution is to use a framework that helps you keep track of all your elements, which speeds up development quite a bit, as the project and app grows. I've built a framework of my own called Adamantium.js, which lets you use a syntax like jQuery to deal with your elements, based on ID and type selectors. In a coming release, it will also support for something like classes, that can be arbitrarily added or removed from an element, tracking of master/slave relationships and basic filter methods, to help you narrow your query. Most methods are chainable, so building apps with rich interaction is quick and simple.
A quick demo:
// Type selector, selects all switches
$(':Switch')
// Bind a callback to the change event on all switches
// This callback is also inherited by all new switch elements
$(':Switch').bind('change', function (e) {
alert(e.type + ' fired on ' + e.source.id + ', value = ' + e.value);
});
// Select by ID and trigger an event
$('#MyCustomSwitch').trigger('change', {
foo: 'bar'
});
Then there's a lot of other cool methods in the framework, that are all designed to speed up development and modeled after the familiar ways of jQuery, more about that in the original blog post.
I completely understand not wanting to write a listener to each one because that is very time consuming. I had the same problem that you did and solved it like so.
var switches = [];
function createSwitch(i) {
switches[i] = Ti.UI.createSwitch();
switches[i].addEventListener('change', function(e) {
Ti.API.info('switch '+i+' = '+e.value);
});
return switches[i];
}
for(i=0;i<rows.length;i++) {
row = Ti.UI.createTableViewRow();
row.add(createSwitch(i));
}
However keep in mind that this solution may not fit your needs as it did mine. For me it was good because each time I created a switch it added a listener to it dynamically then I could simply get the e.source.parent of the switch to interact with whatever I needed.
module Id just for the hold it's ID. When we have use id the call any another space just use . and use easily.
Try This
var but1 = Ti.Ui.createButton({title : 'Button', id:"1"});
window.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "1" ) {
// do some magic!!
}
});
window.add(but1);
I, think this is supported for you.
how do you create your tableview and your switcher? usually i would define a eventListener function while creating the switcher.
// first switch
var switcher0 = Ti.Ui.createSwitch();
switch0.addEventListener('change',function(e){});
myTableViewRow.add(switch0);
myTableView.add(myTableViewRow);
// second switch
var switch1 = ..
so no generic event listener is needed.
I want to dynamically add some preconfigured HTML-Elements in use of a 'click'-event with mootools.
So I can make it work with my basic knowledge, although it isn´t very nifty. I coded this so far...
This is my preconfigured element, with some text, a classname and some event, cause i wanna have events already added, when it´s inserted into my container:
var label = new Element('label', {
'text': 'Label',
'class': 'label',
'events': {
'click': function(el){
alert('click');
}
}
});
Here is my function, which adds the label-Element:
function addText(){
$('fb-buildit').addEvent('click', function(){
row.adopt(label, textinput, deletebtn);
$('the-form').adopt(row.clone());
row.empty();
/*
label.clone().inject($('the-form'));
textinput.inject($('the-form'));
deletebtn.inject($('the-form'));
*/
});
}
The second part which uses inject also works, but there, my click-Event, which fires the "alert('click')" works too. The method with adopt doesn´t add any event to my label Object, when its inserted in the dom.
Can anyone help me with this. I just wanna know why adobt ignores my "events" settings and inject doesn´t.
Thanks in advance.
(sorry for my english ^^)
you go label.clone().inject but row.adopt(label) and not row.adopt(label.clone()) -
either way. .clone() does not cloneEvents for you - you need to do that manually.
var myclone = label.clone();
myclone.cloneEvents(label);
row.adopt(label);
this is how it will work
as for why that is, events are stored in the Element storage - which is unique per element. mootools assigns a uid to each element, eg, label = 1, label.clone() -> 2, label.clone() -> 3 etc.
this goes to Storage[1] = { events: ... } and so forth. cloning an element makes for a new element.uid so events don't work unless you implicitly use .cloneEvents()
you are sometimes not doing .clone() which works because it takes the ORIGINAL element along with its storage and events.
suggestion consider looking into event delegation. you could do
formElement.addEvent("click:relay(label.myLabel)", function(e, el) {
alert("hi from "+ el.uid);
});
this means no matter how many new elements you add, as long as they are of type label and class myLabel and children of formElement, the click will always work as the event bubbles to the parent.
So I have been adding my events thusly:
element.addEvent('click', function() {
alert('foobar');
});
However, when attempting to remove said event, this syntactically identical code (with "add" switched to "remove") does not work.
element.removeEvent('click', function() {
alert('foobar');
});
I assume this is because the two functions defined are not referenced the same, so the event is not technically removed. Alright, so I redefine the event addition and removal:
element.addEvent('click', alert('foobar'));
element.removeEvent('click', alert('foobar'));
Which works great, except now when the page loads, the click event is fired even before it's clicked!
The function is removed, though, which is great......
update: when you do .addEvent('type', function(){ }) and .removeEvent('type', function(){ }), even though the functions may have the same 'signatures', they are two separte anonymous functions, assigned on the fly. function 1 is !== to function 2 - hence there is no match when MooTools tries to remove it.
to be able to remove an exact handler, o:
function handler(){ ... }
el.addEvent('click', handler);
// .. later
el.removeEvent('click', handler);
Internally, events are actually a map of keys to functions in element storage. have a look at this fiddle i did a while back for another SO question - http://www.jsfiddle.net/mVJDr/
it will check to see how many events are stacked up for a particular event type on any given element (or all events).
similarly, removeEvent looks for a match in the events storage - have a look on http://jsfiddle.net/dimitar/wLuY3/1/. hence, using named functions like Nikolaus suggested allows you to remove them easily as it provides a match.
also, you can remove events via element.removeEvents("click") for all click events.
your page now alerts because you pass on alert as the function as well as execute it with the params 'foobar'. METHOD followed by () in javascript means RUN THE METHOD PRECEDING IT IMMEDIATELY, NOT LATER. when you bind functions to events, you pass the reference (the method name) only.
to avoid using an anonymous function and to pass argument,s you can do something like:
document.id('foobar').addEvent('click', alert.bind(this, 'foo'));
as bind raps it for you, but removing this will be even more complicated.
as for event delegation, it's:
parentEl.addEvents({
"click:relay(a.linkout)": function(e, el) {
},
"mouseover:relay(li.menu)": function(e, el) {
}
});
more on that here http://mootools.net/docs/more/Element/Element.Delegation#Element:removeEvent
keep in mind it's not great / very stable. works fine for click stuff, mouseenter is not to be used delegated, just mouseover - which means IE can fire mouseout when it should not. the way i understand it, it's coming improved in mootools 2.0
edit updating to show an example of bound and unbound method within a class pattern in mootools
http://www.jsfiddle.net/wmhgw/
var foo = new Class({
message: "hi",
toElement: function() {
return this.element = new Element("a", {
href: "http://www.google.com",
text: "google",
events: {
"click": this.bar.bind(this), // bind it
"mouseenter": this.bar // unbound -> this.element becomes this
}
});
},
bar: function(event) {
event.stop();
// hi when bound to class instance (this.message will exist)
// 'undefined' otherwise.
console.log(this.message || "undefined");
}
});
document.id(new foo()).inject(document.body);
the mouseenter here will be unbound where this will refer to the default scope (i.e the element that triggered the event - the a href). when bound, you can get the element via event.target instead - the event object is always passed on to the function as a parameter.
btw, this is a slightly less familiar use of class and element relation but it serves my purposes here to illustrate binding in the context of classes.
assig the function to a variable and use the same reference to add and remove the event.
if you use an anonymous function you will get to different references
var test = function(){ alert('test: ' + this.id); }
$('element').addEvent('click', test);
...
$('element').removeEvent('click', test);
addEvent : Attaches an event listener to a DOM element.
Example -
$('myElement').addEvent('click', function(){
alert('clicked!');
});
removeEvent : Works as Element.addEvent, but instead removes the specified event listener.
Example -
var destroy = function(){ alert('Boom: ' + this.id); } // this refers to the Element.
$('myElement').addEvent('click', destroy);
//later...
$('myElement').removeEvent('click', destroy);
This means when you add an event with a eventhandler not an anonymous function if you than remove the event than it will be removed.