backbone view instantiating incorrectly in jasmine - jasmine

Using chrome development tools. when I instantiate a view in the app I get this...
new job.SearchView
SearchView
$el: jQuery.fn.jQuery.init[1]
cid: "view8"
el: HTMLDivElement
mbti: function (){ return fn.apply(me, arguments); }
options: Object
__proto__: ctor
From with jasmine I get the following (and spec failure)
new job.SearchView
SearchView
$el: jQuery.fn.jQuery.init[0]
cid: "view17"
el: undefined
mbti: function (){ return fn.apply(me, arguments); }
options: Object
__proto__: ctor
Why would el: be undefined when instantiating in jasmine?
SearchView defined like this...
jQuery ->
class SearchView extends Backbone.View
el: '#search'
template: JST['resume']
<snip>
#job = if window.job then window.job else {}
#job.SearchView = SearchView
and the spec like this...
describe 'Search View', ->
it 'should be defined', ->
expect(job.SearchView).toBeDefined()
beforeEach ->
#view = new job.SearchView()
describe 'render', ->
it 'should render the task', ->
$el = #view.render().$el
expect($el).toBeDefined()

When you specify the el attribute in your view, this acts as a container. In your app #search exists in the DOM. In the jasmine test it doesn't.
You can create sandboxed containers using jasmine-jquery and use them when constructing your views in tests.
`

Related

How to qunit test function tied to event being fired

js file
window.onload = function() {
document.getElementById('example').addEventListener('mousedown', myFunction, false);
};
function myFunction(e) {
x = e.clientX-parseInt(document.getElementById('example').offsetLeft);
window.addEventListener('mousemove', anotherFunction, true);
}
Trying to figure out how to test with QUnit. the page doesn't use jQuery, just straight JavaScsript. As you can see, onload includes an eventlistener being added to an element, and when that mouse event is fired, the myFunction function is called. qunit code please.
In my mind this should be two tests:
One: you want to test to make sure the event listener is attached correctly. For this you can just override myFunction (or use a spy like from sinon). Then trigger a mouse click (see MDN for triggering events).
Then for the next function, do the same only attach the spy or override anotherFunction, and then just call your method directly.
** EDIT FOR INFO ABOUT SPIES **
If you've got sinon loaded, and myFunction is available in scope, you make myFunction a spy.
From the docs for spies and event triggering
test('callback', function(){
sinon.spy(myFunction);
// or
var oldFunc = myFunction;
myFunction = function(){
ok(true, true, 'function called');
}
var event = new MouseEvent('down', {
'view': window,
'bubbles': true,
'cancelable': true
});
// trigger event
document.getElementById('example').dispatchEvent(event);
ok(myFunction.called, true, 'function called');
myFunction.restore();
//or
myFunction = oldFunc;
});

Backbone Marionette - Uncaught TypeError: Cannot call method 'show' of undefined

I know that the error is generated by jQuery. Is there any way I can structure a backbone.marionette application to avoid the below error?
Uncaught TypeError: Cannot call method 'show' of undefined
The error occurs because I create a Layout, which has regions. In the onRender function I load a collection and execute fetch. When the fetch is complete, I populate the region. If I switch to a different view, the error is triggered because of self.content.show which no longer exists.
var view = Marionette.Layout.extend({
template: _.template(tplPage),
className: 'customers-view',
regions:{
content: '.customers-content'
},
onRender: function(){
var self = this,
colCustomers = new ColCustomers();
colCustomers.fetch({success: function(){
self.content.show(new ViewCustomersList({collection: colCustomers}));
}});
}
});
NOTE: At the moment I wrap self.content.show() in a try catch and its working. Ideally I avoid that.
Modify the onRender function and listen for the fetch differently.
onRender: function(){
var colCustomers = new ColCustomers();
this.listenTo(colCustomers, "sync", function () {
this.content.show(new ViewCustomersList({collection: colCustomers}));
});
colCustomers.fetch();
}
What I did was change the approach to binding the event listener. By using the listenTo function from Backbone.Events, you get handlers that are cleaned up for free when the listener object dies.

KendoUI DataSource binding to MVVM grid in durandal (using hottowel template) doesn't seem to work

I am using Visual Studio 2012 Update 2 hottowel template with updated durandal and jquery nuget packages...
Here is my code:
Durandal main.js:
require.config({
paths: { "text": "durandal/amd/text" }
});
define(['durandal/app', 'durandal/viewLocator', 'durandal/viewModelBinder', 'durandal/system', 'durandal/plugins/router', 'services/logger'],
function (app, viewLocator, viewModelBinder, system, router, logger) {
// Enable debug message to show in the console
system.debug(true);
app.start().then(function () {
toastr.options.positionClass = 'toast-bottom-right';
toastr.options.backgroundpositionClass = 'toast-bottom-right';
router.handleInvalidRoute = function (route, params) {
logger.logError('No Route Found', route, 'main', true);
};
// When finding a viewmodel module, replace the viewmodel string
// with view to find it partner view.
router.useConvention();
viewLocator.useConvention();
// Adapt to touch devices
app.adaptToDevice();
kendo.ns = "kendo-";
viewModelBinder.beforeBind = function (obj, view) {
kendo.bind(view, obj.viewModel || obj);
};
//Show the app by setting the root view model for our application.
app.setRoot('viewmodels/shell', 'entrance');
});
});
Durandal viewmodel:
define(['services/datacontext', 'durandal/plugins/router'],
function (datacontext, router) {
var activate = function () {
//yes yes - I will separate this out to a datacontext - it is here for debugging simplicity
var service = $data.initService("https://open.jaystack.net/c72e6c4b-27ba-49bb-9321-e167ed03d00b/6494690e-1d5f-418d-adca-0ac515b7b742/api/mydatabase/");
//return promise as durandal seems to want...
return service.then(function (db) {
vm.set("airports", db.Airport.asKendoDataSource());
});
};
var deactivate = function () {
};
var viewAttached = function (view) {
//kendo.init($("#airportGrid"));
//kendo.bind(view, vm);
//kendo.bind($("#airportGrid"), vm);
};
var vm = new kendo.data.ObservableObject({
activate: activate,
deactivate: deactivate,
airports: [],
title: 'Airports',
viewAttached: viewAttached
});
return vm;
});
Durandal view:
<section>
<h2 class="page-title" data-bind="text: title"></h2>
<div id="airportGrid" data-kendo-role="grid" data-kendo-sortable="true" data-kendo-pageable="true" data-kendo-page-size="25" data-kendo-editable="true" data-kendo-columns='["id", "Abbrev", "Name"]' data-kendo-bind="source: airports"></div>
</section>
I see the call being made to jaystack in Chrome's network monitor:
https://open.jaystack.net/c72e6c4b-27ba-49bb-9321-e167ed03d00b/6494690e-1d5f-418d-adca-0ac515b7b742/api/mydatabase//Airport?$inlinecount=allpages&$top=25
And I see data coming back.
The kendoui grid is created nicely but there is no data in it (I think this means kendoui is happy and the MVVM bindings are being bound to, however the created kendoui grid doesn't seem to want to understand the kendoui datasource created from jaydata)
Without durandal this works just nicely as demonstrated in:
http://jsfiddle.net/t316/4n62B/29/
I have been trying and trying for 2 days now - can someone please help me out?
Thanks
TJ
Sounds like everything is working now after removing the parts that are only required by breeze.
Nevertheless I'd suggest restructuring the working dFiddle code slightly to ensure that a) vm is defined before setting vm.airports in activate and b) there's no need to create a dummy vm.airports kendo.data.DataSource() that gets overwritten in activate anyway.
define(function( ) {
var vm = new kendo.data.ObservableObject({
activate: activate,
deactivate: deactivate,
// airports: new kendo.data.DataSource(),
title: 'Airports',
viewAttached: viewAttached
});
return vm;
function activate () {
var service = $data.initService("https://open.jaystack.net/c72e6c4b-27ba-49bb-9321-e167ed03d00b/6494690e-1d5f-418d-adca-0ac515b7b742/api/mydatabase/");
return service.then(function( db ) {
vm.airports = db.Airport.asKendoDataSource();
});
}
function deactivate () {
}
function viewAttached ( view ) {
//kendo.init($("#airportGrid"));
//kendo.bind(view, vm);
//kendo.bind($("#airportGrid"), vm);
}
});
Which version on jQuery do you use? Try with 1.8.3 or 1.9 + Migration.
In Chrome switch the stop sign to purple (two clicks) to catch uncaught exceptions and see if there is any.

Click event not firing in main view in Backbonejs

I have really weird problem. I am trying to implement "root" view which also works as some namespace structure. Same principle introduced in codeschool.com course part II. In this root view, I want to catch event "click button", but that's the problem. When I click on button nothing happened.
window.App = new (Backbone.View.extend({
el: $("#app"),
Collections: {},
Models: {},
Views: {},
Routers: {},
events: {
'click button' : function(e) {
alert("Thank god!");
}
},
render: function(){
//for test purposes
console.log($("#app").find("button"));
console.log(this.$el.find('button'));
},
start: function(){
this.render();
new App.Routers.PostsRouter();
Backbone.history.start({pushState: true});
}
}))();
$(document).ready(function() { App.start() });
The HTML look like this
<body>
<div id="app">
<div id="posts"></div>
<button>Click me</button>
</div>
</body>
And what's really weird is output from console.log in render function. Both selectors are same, even the context is same, so where is problem?
console.log($("#app").find("button")); //this returns correct button
console.log(this.$el.find('button')); //this returns 0 occurences (WTF?)
EDIT:
After little change at el: "#app", still same problem. Problem was (thanks to #nemesv) in instantiating this class before DOM is loaded. But however, it's not possible to instantiating after DOM is loaded, because then it's not possible to use that namespace structure (eg. App.Model.Post = Backbone.Model.extend() ). But this "main view with namespace structure" is introduced in codeschool.com course as some sort of good practice. Solution can be found there http://jsfiddle.net/BckAe
You have specified your el as a jquery selector but because you are inside an object literal it evaluates immediately so before the DOM has been loaded.
So the el: $("#app"), won't select anything.
You can solve this by using one of the backbone features that you can initilaize the el as a string containing a selector.
So change your el declaration to:
el: "#app"
Your click event is not triggered because you instantiate your view before the DOM is loaded so backbone cannot do the event delegation your you.
So you need separate your view declaration and creation into two steps. And only instantiate your view when the DOM is loaded:
var AppView = Backbone.View.extend({
el: "#app",
//...
});
$(document).ready(function()
{
window.App = new AppView();
App.start()
});
Demo: JSFiddle.

Reference a button created in constructor from Controller on Sencha Touch 2?

I've been researching on how to reference methods of a button using MVC on Sencha Touch, but none of the articles has worked fine for me since I declare all my controls on the constructor of my views.
Here is an example of my code:
Ext.define('TPTMOBILE.view.Login',{
extend: 'Ext.Panel',
...
config: {
...
},
constructor: function(){
var submitButton = Ext.create('Ext.Button', {
text: 'Login'
});
}
});
So I'd like to know how to reference the onTap method of my 'submiButton' button.
Thanks in advance.
Your constructor method will not working, try this instead:
constructor: function(config){
this.callParent(config);
this.add(
{
xtype: "button",
text: 'Login',
action: 'doTap'
}
);
}
Your constructor method must contain a call to callParent in order to pass the config object to the parent constructor otherwise it won't work.
After that, you have several ways to achieve your onTap method on the button but since you want to use MVC on Sencha Touch so you can set an action for the button
action: 'doTap'
Then in your controller you can do as following to run your code when tapping on that button:
refs: {
doTap: 'button[action=doTap]'
},
control: {
doTap: {
tap: 'doTap'
},
},
doTap: function() {
alert('doTap function activvated');
}
Hope it helps :)

Resources