Event bubbling in toolkitchen - javascript

I'm thinking of best practices for event bubbling in toolkitchen library. I have a nested markup with components and somewhere in this a button is pressed that should trigger an event somewhere in the hierarchy of components. This is an example, and I curious if there's a better way. Perhaps even a built in event system in toolkithcen lib itself.
// In one component
mouseClicked: function () {
var evt = new CustomEvent('ganttChartNewEventRequested');
document.dispatchEvent(evt);
}
// In another component
document.addEventListener('ganttChartNewEventRequested', function(e){
alert('create new event');
}, false);

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<script src='http://toolkitchen.github.io/cdn/toolkit.min.js?shadow'></script>
</head>
<body>
<my-sink></my-sink>
<element name="my-source">
<script>
Toolkit.register(this, {
ready: function() {
setTimeout(function() {
this.send("hello-world");
}.bind(this), 500);
setTimeout(function() {
this.send("goodbye-world");
}.bind(this), 1500);
}
});
</script>
</element>
<element name="my-sink" on-goodbye-world="goodbyeWorld">
<template>
<my-source on-hello-world="helloWorld"></my-source>
<div >
{{message}}
</div>
</template>
<script>
Toolkit.register(this, {
message: '...',
helloWorld: function() {
this.message = 'hello world';
},
goodbyeWorld: function() {
this.message = 'goodbye world';
}
});
</script>
</element>
</body>
</html>

Related

Difference between init events in js file or html template

Is there a difference between lauch js functions from same JS file where they declared after page load, or in html template? When both signed into $(document).ready(function () {...}).
I assume that no, but I ran into a problem when replace my ExampleService.init() function from template to separate JS file.
For example i have that construction:
common.js
var ExampleService= {
catalogSpinner: '',
init: function() {
this.initEvents();
},
initEvents: function() {
var self = this;
$('.example-button').on('click', function() {
//do some logic, append spinner...
self.removeSpinner();
});
},
removeSpinner: function() {
$(this.catalogSpinner).fadeOut('slow', function() {
$(this).remove().css({display: 'block'});
});
}
}
index.html
<script src="js/common.js"></script>
<script>
$(document).ready(function () {
ExampleService.catalogSpinner = '<div class="spinner"></div>'; // css3 animation
ExampleService.init();
});
</script>
That way all works perfect, my catalogSpinner overriden from template, and i can use them like DOM element.
But! if i move ExampleService.init(); to common.js file, like that:
common.js
var ExampleService= {
...
// all the same...
...
};
$(document).ready(function () {
'use strict';
ExampleService.init();
});
index.html
<script src="js/common.js"></script>
<script>
$(document).ready(function () {
ExampleService.catalogSpinner = '<div class="spinner"></div>';
});
</script>
That way it wouldn't work. And throw console error Uncaught TypeError: this.catalogSpinner.fadeOut is not a function
Why it's happens? After all in both cases init functions starts only after full page load, and no matters that i override my variable after starting base functions. What im doing wrong?
About orders in which inits will executed. How i understand its no matter. Cause in any case, second document.ready from template file, always ovverride empty catalogSpinner variable from JS file, before click event happens
It's almost certainly a timing issue. What guarantee do you have that $(document).ready in common.js will fire after the same event handler in your html file (which is what needs to happen according to your implementation)?
Or, you need to make sure that when it occurs in common.js, that code can somehow retrieve the catalogSpinner value.
Also, catalogSpinner needs to be a valid jQuery object, not a string.
It will and it does work in both the cases. To use jQuery methods over DOM elements, you must have valid jQuery selectors which will return objects binded with jQuery methods.
Try this:
case 1:
common.js
var ExampleService= {
catalogSpinner: '',
init: function() {
this.initEvents();
},
initEvents: function() {
var self = this;
$('.example-button').on('click', function() {
//do some logic, append spinner...
self.removeSpinner();
});
},
removeSpinner: function() {
this.catalogSpinner.fadeOut('slow', function() {
$(this).remove().css({display: 'block'});
});
}
};
index.html
<!doctype html>
<html>
<head>
<script src="jquery.min.js"></script>
<script src="common.js"></script>
<style>
</style>
</head>
<body>
<div class="spinner">Spinner</div>
<button type="button" class="example-button">Remove</button>
<script type="text/javascript">
$(document).ready(function () {
ExampleService.catalogSpinner = $('.spinner');
ExampleService.init();
});
</script>
</body>
</html>
case 2:
common.js
var ExampleService = {
catalogSpinner: '',
init: function () {
this.initEvents();
},
initEvents: function () {
var self = this;
$('.example-button').on('click', function () {
//do some logic, append spinner...
self.removeSpinner();
});
},
removeSpinner: function () {
this.catalogSpinner.fadeOut('slow', function () {
$(this).remove().css({display: 'block'});
});
}
};
$(document).ready(function () {
'use strict';
ExampleService.init();
});
index.html
<!doctype html>
<html>
<head>
<script src="jquery.min.js"></script>
<script src="common.js"></script>
<style>
</style>
</head>
<body>
<div class="spinner">Spinner</div>
<button type="button" class="example-button">Remove</button>
<script type="text/javascript">
$(document).ready(function () {
ExampleService.catalogSpinner = $('.spinner');
});
</script>
</body>
</html>

backbone events are not triggered by the button

I am working with Backbone and Jquery. I have a button inside a template and for some reason that button does not trigger any click events. I've tried all the suggested solutions on stackoverflow but none of them worked for me. Below is the code Im working with - I've made it as short as possible.
Any ideas why the YES button does not work?
EditView.html
<section id="EditView">
<button id="button-yes">YES</button>
</section>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<header id="header"></header>
<div id="content"><div id="content-inner"></div></div>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"> </script>
<script type="text/javascript">
var myapp = myapp || {};
myapp.EditView = Backbone.View.extend({
events: {
'click #button-yes': 'buttonClickHandler'
},
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template());
return this; // support chaining
},
buttonClickHandler : function(event){
alert( $(event.currentTarget).text() );
return false;
}
});
myapp.utils = {
loadTemplates: function(views, callback) {
var deferreds = [];
$.each(views, function(index, view) {
if (myapp[view]) {
deferreds.push($.get(view + '.html', function(data) {
myapp[view].prototype.template = _.template(data);
}));
} else {
console.log(view + " not found");
}
});
$.when.apply(null, deferreds).done(callback);
}
};
myapp.AppRouter = Backbone.Router.extend({
routes: {
"": "add"
},
initialize: function() {
this.add;
},
add: function() {
if (!this.editView) {
this.editView = new myapp.EditView({el: $("#content-inner")});
};
$('#content').html(this.editView.el);
}
});
myapp.utils.loadTemplates([ 'EditView'], function() {
myapp.router = new myapp.AppRouter();
Backbone.history.start();
});
</script>
</body>
</html>
I'm not entirely sure why you're having a problem, seems to be some race condition where the element doesn't exist when backbone attempts to bind the event to it. It's an interesting case. Anyway, you can solve it for now by adding
myapp.router.editView.delegateEvents();
after Backbone.history.start();

Backbone - Trigger the parent view event from child view

I am trying to trigger the event of parent view from its child view. But it seems that the parent view's event did not gets triggered.
Following is the sample for my code:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>
<script>
MySubView = Backbone.View.extend({
id : "MySubView",
initialize: function() {
console.log("pro1");
this.trigger("testGo", "test");
}
});
MyView = Backbone.View.extend({
id : "MyView",
initialize: function() {
console.log("pro");
this.subview = new MySubView();
this.subview.listenTo("testGo", this.addFoo, this);
},
addFoo: function() {
console.log("ok");
alert("ok");
}
});
new MyView();
</script>
</head>
<body>
</body>
</html>
I tried to get hint from many solutions found via google search, but it seems I am got struck somewhere. Some of the options which I found are :
1/ How to trigger an event from child view to parent view in backbonejs and requirejs
2/ Backbone: How to trigger methods in a parent view
The problem is that you are using listenTo wrong.
anObject.listenTo(anotherObject, 'forSomeEvent', function () { console.log('do something'); });
So, in your case, you should do it like this:
MyView = Backbone.View.extend({
id : "MyView",
initialize: function() {
console.log("pro");
this.subview = new MySubView();
this.listenTo(this.subview, 'testGo', this.addFoo);
},
addFoo: function() {
console.log("ok");
alert("ok");
}
});
Hope it helps!
Your listenTo usage is slightly off
The signature is object.listenTo(otherObject, event, callback) so you want something like:
this.listenTo(this.subview, "testGo", this.addFoo);
Which tells this to listen to this.subview for the event testGo and call this.addFoo when the event is triggered
try this
this.listenTo(this.subview, "testGo",this.addFoo);
signature:
object.listenTo(other, event, callback)
Triggering event:
Backbone.Events.trigger('<eventname>', {data1 to be passed with event}, {data2 to be passed with event}...);
Listener:
Backbone.Events.bind('<eventname>', callback, context);

How to listen to events fired on page load

How do you write a CasperJS test that can listen to an event fired on page load? The event I'm interested in fires at the end of the page body.
// my-page.html
<html>
...
<body>
...
<script type="text/javascript">
$(function() {
fireInterestingEvent();
});
</script>
</body>
</html>
I've tried injecting a client script but it seems to load after fireInterestingEvent() has already fired.
// test/my-test.js
casper.options.clientScripts.push("test/lib/my-client-script.js")
// test/lib/my-client-script.js
listenToInterestingEvent();
I've also tried initializing a test object before the page finishes loading but my object isn't staying on the DOM.
// test/my-test.js
casper.on('load.started', function() {
this.evaluate(function() {
window.testObject = { foo: 'bar' };
});
});
// my-page.html
<script type="text/javascript">
if (window.testObject) listenToInterestingEvent(); // window.testObject is undefined
fireInterestingEvent();
</script>
#Artjom B. figured it out in the comments section. Initializing an object on window during url.changed works but not during load.started.
// test/my-test.js
casper.on('url.changed', function() {
this.evaluate(function() {
window.testObject = { foo: 'bar' };
});
});
// my-page.html
<script type="text/javascript">
if (window.testObject) listenToInterestingEvent();
fireInterestingEvent();
</script>

Events with backbone.js

Trying to set up a basic backbone.js example - can't get events to fire. What am I missing?
HTML
<html lang="en">
<head>
<script type="text/javascript" src="js/libs/jquery/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="js/libs/underscore/underscore-min.js"></script>
<script type="text/javascript" src="js/libs/backbone/backbone.js"></script>
<script type="text/javascript" src="js/test.js"></script>
<title></title>
</head>
<body>
<div id="helloworld">
<button id="sayhello">
Say Hello
</button>
</div>
</body>
And here is JS
(function($) {
HelloWorldView = Backbone.View.extend({
el : '#helloworld',
events: {'click #sayhello': 'onButtonClicked'},
onButtonClicked: function()
{
console.log("buttonclicked");
},
render : function() {
console.log("rendering");
$(this.el).html("Hello world");
}
});
var helloView = new HelloWorldView().render();})(jQuery);
Any idea why I can't get the events to fire?
Your render method kills #sayhello:
render : function() {
console.log("rendering");
$(this.el).html("Hello world"); // #sayhello is now gone
}
So there is no #sayhello to click on and your event handler never gets called.
Perhaps your render is supposed to look more like this:
render: function() {
console.log("rendering");
$(this.el).find('button').html("Hello world");
}
Demo: http://jsfiddle.net/ambiguous/4empG/
actually you replace whole html with "Hello World"
$(this.el).html("Hello World");
it's better, you just find the button and change html.
this.$('#sayhello').html('Hello World');

Categories