JointJS element containing HTML button onclick show form - javascript

I am showing a form on addDetail buttton inside this element. How can I bind my data to this cell and send it to the server using the toJSon() method?
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
template: [
'<div class="html-element">',
'<button class="delete">x</button>',
'<label></label>',
'<span></span>', '<br/>',
'<input type="text" name="name" placeholder="name"/>',
'<button class="addDetail">+</button>',
'</div>'
].join(''),
initialize: function () {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
// Prevent paper from handling pointerdown.
this.$box.find('input').on('mousedown click', function (evt) {
evt.stopPropagation();
});
// This is an example of reacting on the input change and storing the input data in the cell model.
this.$box.find('input').on('change', _.bind(function (evt) {
alert($(evt.target).val());
this.model.set('input', $(evt.target).val());
}, this));
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
this.$box.find('.addDetail').on('click', _.bind(function (evt) {
addActionDetail();
})
);
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
},
render: function () {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
this.updateBox();
return this;
},
updateBox: function () {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
this.$box.find('label').text(this.model.get('label'));
this.$box.find('span').text(this.model.get('select'));
this.$box.css({
width: bbox.width,
height: bbox.height,
left: bbox.x,
top: bbox.y,
transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'
});
},
removeBox: function (evt) {
this.$box.remove();
}
});
}

In order to save some data on your element you must follow this steps:
Add some elementData propery to the shape model.
Each time the user click on addDetail inside your element you must have the element id, extract the elementData out of it, and then to render the form (you can achieve this by adding custom event listener to your paper)
When clicking the submit form, add add some custom trigger event.
Listen to that triggered event on your graph and try look for the specific cell by the ModelId and update it.
Here is the basic idea example:
1.your shape model:
joint.shapes.myShapes = joint.shapes.myShapes || {};
joint.shapes.myShapes.Element = joint.shapes.basic.Generic.extend({
//basically the defaults function doesn't needed, because the set function adds that automatically.
defaults: _.defaultsDeep({
elementData: null,
}, joint.shapes.basic.Generic.prototype.defaults),
getElementData: function () {
return this.get("elementData");
},
setElementData: function (elementData) {
this.set("elementData", elementData);
},
});
2.On your paper init, add your custom event listener function,
notice that you must have the ModelId to be remembered:
paper.on('addDetail:click', function (cell) {
var elementData = cell.model.getElementData();
elementData.ModelId = cell.model.id;
formRender(elementData);
});
3.trigger some custom event on your submit and the object to be updated within the element model:
function formSubmit() {
graph.trigger('custom:update', newElementData);
}
4.Add some custom event listener to your graph, add call the setElementData by the ModelId:
graph.on('custom:update', function (elementData) {
var cell = graph.getCell(elementData.ModelId);
cell.setElementData(elementData);
}, this);
Now you can send it to the server using the toJSon() method.

Related

Bind event relative to the class that created it?

Let's say I have a class laid out like so:
function slider() {
this.init = function(options, title, content) {
var self = this;
$('body').append('<button type="button">' + title + '</button>')
},
this.create = function(title, content, options) {
var self = this;
self.init(options, title, content);
},
this.closeSlider = function(elem) {
var self = this;
self.assignPositions();
},
this.assignPositions = function() {
alert('assign positions called from button?');
}
}
To create the 'slider', I use this:
var slider = new slider();
Then I call the create function:
slider.create('title', 'content');
My question is, how can I bind the closeSlider function to the button, but it's only linked to the instance that created it? If that makes sense?
Basically, I'll have many buttons with the 'closeSlider' function, and I don't want them all to fire at once, I only want it linked to the instance that created it.
This is also a VERY trimmed down version of my class, just trying to figure this little problem out :)
Cheers
Use bind on the callback function when setting the event callback
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this));
$('body').append(btn)
},
This will make it so when the closeSlider function is called it retains the context of the slider instance that made it. But note this will no longer be the context of the html element that triggered the event. So you would need to get the target from event.target
There might be another way of doing this without losing the context of the html element i will have to look and re-edit.
Edit
Using event.target
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this));
$('body').append(btn)
},
this.closeSlider:function(event){
//`this` will refer to slider instance
//and event.target will be the button dom object
var element = event.target;
});
Passing the button object as an argument in bind
this.init = function(options, title, content) {
var btn = $('<button type="button">' + title + '</button>');
btn.click(this.closeSlider.bind(this,btn));
$('body').append(btn)
},
this.closeSlider:function(btn,event){
//`this` will refer to slider instance
//btn will refer to the jQuery wrapped button dom object
//event.target will still refer to the button dom object
});
Instead of simply appending a string to the body, create a live element with document.createElement and attach the onclick event before releasing it into the wild.
Here's an example:
function slider() {
this.init = function(options, title, content) {
var myButton = document.createElement("button");
myButton.setAttribute("type", "button");
myButton.innerHTML = title;
myButton.onclick = this.closeSlider;
$('body').append(myButton);
},
//Other object definitions
}
First of all, you should add methods onto the prototype rather than on each instance (saves memory).
Secondly, you can use jQuery's .proxy() to create an anonymous function that will "hardwire" this to a particular value when it calls your method.
// empty constructor
function slider()
{
}
// define prototype
$.extend(slider.prototype, {
init: function(options, title, content) {
$('<button>', { text: title })
.on('click', $.proxy(this, 'closeSlider'))
.appendTo('body');
},
create: function(title, content, options) {
this.init(options, title, content);
},
closeSlider: function(event) {
// event.target is the HTML element
this.assignPositions(event.target);
},
assignPositions: function(elem) {
alert('assign positions called from button?');
}
});

Get Component Reference in AuraJS

I'm using jQuery dataTables to display a table. I need to be able to pass a row selection event on to my Aura component that handles the selection and performs some operations on the data from that row.
In the initialize() function:
initialize: function()
{
$("#mytable tbody").click(function(event)
{
$(mytable.fnSettings().aoData).each(function ()
{
$(this.nTr).removeClass('row_selected');
});
$(event.target.parentNode).addClass('row_selected');
});
mytable = $('#mytable').dataTable();
},
I set up the click handler for the row selection, but how do I get a reference to the enclosing component so I can sandbox.emit() function to issue messages? I can put a reference to the component into the Closure, but that essentially makes this component a singleton and I could never have two instances of the component on the page at the same time.
Is there a standard way, using jQuery selectors or some other method, that I can retrieve a reference to the enclosing component from inside the click() handler?
Edit: I should never try to write code until I have had 32oz of caffine. You can pass a reference to the current component via the click() method itself. Like so:
$("#mytable tbody").click(this, function(event)
{
$(mytable.fnSettings().aoData).each(function ()
{
$(this.nTr).removeClass('row_selected');
});
$(event.target.parentNode).addClass('row_selected');
event.data.sandbox.emit('mychannel', {data: 'stuff'});
});
If I understand your question correctly, you could try something like this
initialize: function () {
var that = this;
$("#mytable tbody").click(function(event) {
//have acces to component as 'that'
});
}
what I used for events is view inside component configuration:
View: {
events: {
'click a[data-question-edit-id]': function (e) {
var button = $(e.currentTarget),
id = button.attr('data-question-edit-id'),
examId = this.component.examModel.get('id');
this.sandbox.router.navigate('/exams/' + examId + '/questions/' + id + '/edit', {trigger: true});
},
'click a[data-question-delete-id]': function (e) {
var button = $(e.currentTarget),
id = button.attr('data-question-delete-id');
this.component.showDeleteConfirmation(id);
}
}
}
If you'll find be helpful, here is my repo of aura project I'm working on:
https://github.com/lyubomyr-rudko/aura-test-project

how to call a listener of view from controller in Extjs

i have a bar chart in my view where i have a listener which is retrieving the bar column items ,now i have to call that listener from controller here is my code ...
This is the listener in my view..
listeners: {
itemmousedown: function (obj) {
alert(obj.storeItem.data['source'] + ' &' + obj.storeItem.data['count']);
}
},
and i have to call this listener from my controller.here is my code..
init: function () {
this.control({
'barColumnChart': { //this is the id of the bar chart in my View
click: function () {
}
}
});
},
The itemmousedown event is fired by the "series", and the series is not a component, and is only initialized by the chart after layout. So in order to get a reference to the series object we need to wait for the afterlayout event of the chart. But unfortunately the chart is not firing an afterlayout event...
So, first override the afterComponentLayout method of chart, such that it fires the event:
Ext.define('MyNewChart',{
extend: 'Ext.chart.Chart',
afterComponentLayout: function(width, height, oldWidth, oldHeight) {
this.callParent(arguments);
this.fireEvent('afterlayout', this);
}
});
Now, use our new chart class to create your chart:
Ext.create('MyNewChart', {
id: 'myChart',
...
});
And now we can create a controller that actually create a listener on the itemmousedown event:
Ext.define('Gamma.controller.ControlFile', {
extend : 'Ext.app.Controller',
initializedEvents: false,
init: function() {
this.control({
'#myChart': {
afterlayout: this.afterChartLayout
}
});
},
afterChartLayout: function(){
if(this.initializedEvents==true) return;
this.initializedEvents=true;
Ext.getCmp('myChart').series.items[0].on('itemmousedown',function(obj){
console.log(obj);
});
}
});
Here is a working fiddle: http://jsfiddle.net/qjfBC/
Have you tried using '#barColumnChart' as the selector? I'm a little rusty but I thought that's the way you get a element by its id inside the controller.
You don't "call" a listener, a listener is being called when an event is fired.
So you should set the itemmousedown listener inside the controller, and remove it from the view.
I don't know which view has the itemmousedown event, but if it is indeed the bar chart, it should look like this:
this.control({
'#barColumnChart': { //this is the id of the bar chart in my View
itemmousedown: function(obj) {
alert(obj.storeItem.data['source'] + ' &' + obj.storeItem.data['count']);
}
}
});
If, however, the event is of another view, you should replace '#barColumnChart' with the id of the correct view (or another selector for that view)
If you create the listener into the controller, you won't have to create it into the view.
In the controller, you can create a reference to your view like this:
refs: [{
ref : 'barColumnChart',
selector: 'your_view'
}
}]
Then create the function that will be called on item mouse down:
me.control({
'barColumnChart#your_chart': {
click: me.your_function
}
}),
your_function(button, e, options) {
alert(obj.storeItem.data['source'] + ' &' + obj.storeItem.data['count']);
}

YUI modifying and detecting changes of a <div>

I want to see an alert message when the value of a div changes. This value is being modified by modify_div. When I click the button this function modifies the div, but the alert "value changed" is not displayed. Am I missing something?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" " http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script>
<script>
YUI().use('node', function (Y) {
var demo = Y.one('#test');
demo.on('click', function (e) {
//alert('You clicked me');
});
});
YUI().use('node','event', function (Y) {
var demo = Y.one('#variable-name');
demo.on('change', function (e) {
alert('Value changed');
});
});
</script>
<script type="text/javascript">
function modify_div()
{
//var thevar = "This is a test";
var thevar = 7;
document.getElementById('variable-name').innerHTML = thevar;
}
</script>
</head>
<body>
<!-- Click me button -->
<input type="button" id="test" value="Click me" enabled="true" onclick="modify_div();"> </input>
</br>
<div id="variable-name" style="display:inline;">01010101</div>
</body>
</html>
based on http://www.quirksmode.org/dom/events/change.html,
change event only fires if its form field
e.g. input textarea and select
so change event will not fire when contents of div is changed.
It will work if you replace div with input and update its value.
other option is to manually fire event where ever you are changing the value your variable
http://tech.groups.yahoo.com/group/ydn-javascript/message/13216
following SO question has answers but it requires jQuery
Detect element content changes with jQuery
The correct answer was given by #N30: there is no change event for divs. He provides good alternatives but no YUI specific information, so I'd like to extend his answer with an example of a YUI Plugin.
Like he explained, the basic idea is to keep a value in JavaScript memory and fire an event when you change that value. You can do this by extending Y.EventTarget which provides you with custom events:
YUI().use('node', 'plugin', function (Y) {
function NodeValuePlugin(config) {
// Boilerplate
NodeValuePlugin.superclass.constructor.apply(this);
// config.host is the Y.Node instance
this._node = config.host;
// we keep the value in a private property
this._value = this._node.get('text');
// we publish a 'change' event and set a default
// function to run when the event is fired
// This function will change the private property
// and update the DOM
// This means you can call e.preventDefault() and
// stop the default behavior (the change of value)
this.publish('change', {
emitFacade: true,
defaultFn: this._defValueChangeFn
});
}
Y.extend(NodeValuePlugin, Y.EventTarget, {
set: function (newVal) {
// we want to do stuff only when the value changes
if (newVal != this._value) {
// instead of changing the DOM here,
// we fire an event and let the event
// do the work
// We pass it the new and old values
this.fire('change', {
newVal: newVal,
prevVal: this._value
});
}
// make this method chainable
return this;
},
get: function () {
return this._value;
},
_defValueChangeFn: function (e) {
// sync everything
this._value = e.newVal;
this._node.set('text', e.newVal);
},
// this is necessary boilerplate for plugins
destroy: function () {}
}, {
// we can access the plugin from node[namespace]
// in this case, node.value
NS: 'value'
});
var node = Y.one('#test').plug(NodeValuePlugin);
node.value.on('change', function (e) {
console.log('Here\'s the old value: ' + e.prevVal);
console.log('Here\'s the new value: ' + e.newVal);
});
// Freebie:
// since we're using node.set('text', foo)
// this prevents XSS vulnerabilities
node.value.set('qwer');
});​
You can learn more about plugins from the Plugin User Guide in the YUI website.

change loses bind reference

Have a link that uses the ID to grab specific content from an external file (works, no problems here). I then change the ID of the link so that a new ID so that new info can be gotten the from the external file but the same info is always shown. So, I'm looking for some help in figuring out why the new info isn't being loaded but the old instead.
Here is the code for setting the new ID value. It resides in a completely separate section of the code. It then calls the function fncSetupInfoWindow() and it creates the bindings. .windowInfo is a class set on specific elements.
$('#pickedCenterProgram a').text(newProgram[0]).parent().slideDown().attr('id', newVal).attr('title', newProgram);
fncSetupInfoWindow();
function fncSetupInfoWindow() {
$('.windowInfo').unbind('mouseover mouseout').each(function () {
var obj = $(this), position = [], contentID = globalObjects.emptyString, title = obj.attr('title'), contentID = obj.attr('id').toLowerCase();
obj.bind({
mouseover: function (e) {
position = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(position[0], position[1], contentID, title);
},
mouseout: function () {
$('#modInfoWindow').closeModal();
}
});
});
}
Here is the code for loading the info
function fncLoadStatusInfo(x, y, programName, title) {
initLoader.className = 'txt-c'; //create loader
initLoader.iconClass = 'alignVert-b';
$('#modInfoWindow').createModalWindow({ isModal: false, left: x, top: y, ignorePosition: false, title: title, width: 250, hideCloseIcon: true, autoOpen: true }).html(initLoader.createLoader());
$('#modInfoWindow').load('../pages/infoFile.htm ' + '#' + programName);
return false;
}
Everything works, well almost, except that the newly assigned ID is not being used but the original for when the page is created. I've tried numerous things as well as even destroying the modal (aka dialog) window all with the same results.
thanks ^.^
Thanks.
The problem is that you're using the id at the time it was bound (inside the .each()), rather than at the time of the event (inside the event handler). You can do it at the time of the event and simplify things overall like this:
function fncSetupInfoWindow() {
$('.windowInfo').bind({
mouseover: function (e) {
var pos = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(pos[0], pos[1], this.id.toLowerCase(), this.title);
},
mouseout: function () {
$('#modInfoWindow').closeModal();
}
});
}
Or even simpler (probably what you want) using .hover():
function fncSetupInfoWindow() {
$('.windowInfo').hover(function (e) {
var pos = fncGetPositionArray(e.pageX, e.pageY);
fncLoadStatusInfo(pos[0], pos[1], this.id.toLowerCase(), this.title);
}, function () {
$('#modInfoWindow').closeModal();
});
}

Categories