requireJS this context change within the callback - javascript

I've the following code:
require([templateName], function(template, view){
console.log(this); // outputs Window object
this.renderTemplate(template, {
articles: this.collection.toJSON() // rises error
});
});
now to make this work I need to change 'this' context as it is outside of it. How to achieve it?
/// EDIT - full code
define(['jquery', 'backbone', 'underscore', 'article/collections/Articles', 'generic-view-decorator','error-handler-decorator'],
function($, Backbone, _, ArticlesCollection, GenericViewDecorator, ErrorHelperDecorator) {
var ArticleView = Backbone.View.extend({
initialize: function(config) {
this.initializeCollection(ArticlesCollection, this.buildResourceUrl({
domain : 'oskszu.vgnett.no',
path : 'vgnett-dt/'+config.config.section + '/json',
query : [{
key : 'limit',
val : config.config.limit
}]
}));
this.initializeErrorCapturing(this.collection);
},
render: function() {
if(!this.collection.length) {
this.trigger('error-600');
} else {
var templateName = 'text!widgets/widgets/article/templates/article.tpl';
require([templateName]).call(this, function(template){
console.log(this);
/*this.renderTemplate(template, {
articles: this.collection.toJSON()
});*/
}, this);
}
}
});
_.extend(ArticleView.prototype, GenericViewDecorator);
_.extend(ArticleView.prototype, ErrorHelperDecorator);
return ArticleView;
}
);

Ok, I've solved it in the following way (for the future generations ;) ):
render: function() {
if(!this.collection.length) {
this.trigger('error-600');
} else {
var templateName = 'text!widgets/widgets/article/templates/article.tpl';
require([templateName], (function(template){
console.log(this);
/*this.renderTemplate(template, {
articles: this.collection.toJSON()
});*/
}).call(this));
}
}

Related

Backbone's Router.execute(callback, args, name) method gets undefined name

I try to implement router which simply show view from defined map according to route's name.
This is my router:
/*global define*/
define([
'jquery',
'underscore',
'backbone',
'models/user',
'views/header',
'views/login',
'views/registration'
], function ($, _, Backbone, UserModel, HeaderView, LoginView, RegistrationView) {
'use strict';
var Router = Backbone.Router.extend({
models: {
userModel: {}
},
staticViews: {
header: {}
},
views: {
login: {},
registration: {}
},
routes: {
'login': 'login',
'registration': 'registration'
},
initialize: function () {
this.models.userModel = new UserModel();
this.staticViews.header = new HeaderView({el: 'nav#header', model: this.models.userModel})
this.views.login = new LoginView({el: 'section#lifeline-login', model: this.models.userModel});
this.views.registration = new RegistrationView({el: 'section#lifeline-registration', model: this.models.userModel});
this.hideAllViews();
this.navigate('login', {trigger: true, replace: true});
},
execute: function (callback, args, name) {
this.hideAllViews();
console.log(name); //undefined
console.log(callback);
var view = this.views[name];
if (view) {
view.$el.show();
}
if (callback) callback.apply(this, args);
},
hideAllViews: function () {
_.each(this.views, function (view) {
view.$el.hide();
});
},
login: function () {
},
registration: function () {
}
})
return Router ;
});
The main problem is undefined name in execute method.
I'm not sure is this the best way to do this. Another idea which comes to my mind is something like that, but it's little tricky:
routes: {
':name': 'change',
},
change: function (name) {
this.hideAllViews();
var view = this.views[name];
if (view) {
view.$el.show();
}
}
I see two possibilities:
You have a pre 1.2.0 Backbone, because
in latest version the name argument cannot be undefined but an empty string and
passing it into execute() was introduced with 62320783 which is contained in 1.2.0+
You manually create a route elsewhere using route() but missing to pass a handler name as 2nd argument
See the code of the 1.3.3 router:
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
if (router.execute(callback, args, name) !== false) {
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
}
});
return this;
},
Update your Backbone or debug the cause of the undefined name by creating a breakpoint through the debugger in line 1 of route().
Ur backbone maybe too old!
At version 1.1.2,function execute has only 2 arguments,name is always null.

Cannot retrieve collection outside of the view

I'm making a simple list of people with option when clicking on person's name the Router will take a name as a parameter 'student/:name' and find a right person's object in a collection. I instantiate collection in a GroupView class by fetching it from the server. And that's where the Error appears: to get the access to collection (so I can find right object) in my viewStudent() method in Router class, I'm making one more instance of GroupView(), and console shows an error and that's right, 'cause there're no objects in collection.
I cannot wrap my head around this, why in GroupView() I receive data from the server and my collection just works fine, but second time I instantiate GroupView() in a Router - there's no collection? Maybe there's any other way I can get access to the collection in my Router? Any help would be greatly appreciated.
var StudentModel = Backbone.Model.extend({
defaults: {
name: 'Volodya',
lastName: 'Peterson',
age: 22,
gender: 'male'
}
});
var StudentsCollection = Backbone.Collection.extend({
model: StudentModel,
url: '/students.json'
});
var StudentView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#studentTpl').html()),
events: {
'click': function () {
eventAggregator.trigger('student:selected', this.model);
}
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var GroupView = Backbone.View.extend({
tagName: 'ul',
initialize: function () {
this.collection = new StudentsCollection();
this.collection.on('update', this.render, this);
this.collection.fetch();
},
render: function () {
var self = this;
this.collection.each(function (student) {
var studentView = new StudentView({
model: student
});
self.$el.append(studentView.render().el);
});
$('body').html(this.$el);
}
});
var RouterView = Backbone.View.extend({
tagName: 'ul',
render: function () {
var self = this;
_.each(this.model.toJSON(), function (value) {
self.$el.append('<li>' + value + '</li>');
});
return this;
}
});
var GroupController = function () {
this.start = function () {
var groupView = new GroupView();
};
};
var Router = Backbone.Router.extend({
routes: {
'': 'index',
'student/:name': 'viewStudent'
},
index: function () {
groupController.start();
},
viewStudent: function (name) {
var groupView = new GroupView();
var selectedStudent = groupView.collection.find(function (student) {
return student.get('name') === name;
});
$('body').append((new RouterView({ model : selectedStudent})).render().el);
}
});
var eventAggregator= _.extend({}, Backbone.Events),
groupController;
$(function () {
var router = new Router();
groupController = new GroupController();
Backbone.history.start();
eventAggregator.on('student:selected', function (student) {
var urlpath= 'student/'+ student.get('name');
router.navigate(urlpath, {trigger: true});
});
});

Testing RequireJS modules with QUnit

I'm trying to test a requirejs module that has two dependencies (jquery and another custom module).
myModule-Test.js
'use strict';
(function() {
var uut,
modulePath= "../../main/webapp/js/modules/myModule.js";
module("myModule object test suite", {
setup: function() {
QUnit.stop();
require.config({
map: {
"*": {
"jquery": "../../main/webapp/js/jquery/jquery-1.11.0.min.js",
"screenLabelsResolver": "../../main/webapp/js/modules/my-screen-labels-resolver"
}
}
});
require([modulePath], function(module) {
uut = module;
QUnit.start();
});
},
teardown: function() {
require.undef(modulePath);
require.config({
map: {
"*": {
"jquery": "jquery",
"screenLabelsResolver": "../../main/webapp/js/modules/my-screen-labels-resolver"
}
}
});
}
});
test("Given A Page I Expect The Global myModule Object To Exist", function() {
ok( uut !== undefined );
});
}());
I am using require.config to pass in the dependencies with stop() and a Start().
myModule.js
'use strict';
define(["jquery", "screenLabelsResolver"], function($, screenLabelsResolver) {
var metaTag = $("meta[name='application-name']"),
currentBrand = metaTag.attr("data-brand"),
currentWidth,
viewState,
sessionTimeoutValue = metaTag.attr("data-sessiontimeoutvalue"),
sessionTimeoutWarningValue = metaTag.attr("data-sessiontimeoutwarningvalue"),
screenLabels = {},
perceptionDate = metaTag.attr("data-todayatmidnight"),
currentViewportWidth = $(window).width(),
isViewState = metaTag.attr("data-isviewstate"),
isTouch = $("html").hasClass("touch")
return {
metaTag: function () {
return metaTag;
},
currentBrand: function(){
return currentBrand;
},
currentViewportWidth: function(){
return currentViewportWidth;
},
isViewState: function(){
return isViewState;
},
sessionTimeoutValue: function(){
return sessionTimeoutValue;
},
sessionTimeoutWarningValue: function(){
return sessionTimeoutWarningValue;
},
getPerceptionDate: function(){
return perceptionDate;
},
getOrientation: function () {
return ( window.orientation == -90 || window.orientation == 90 ) ? "landscape" : "portrait";
},
isTouch: function(){
return isTouch;
},
screenLabels: function() {
if (screenLabels = {}) {
screenLabels = screenLabelsResolver( metaTag.attr("data-viewstate") /* or however you want to get the current viewstate name */ );
}
return screenLabels;
}
};
});
I get the error "Uncaught TypeError: undefined is not a function" where I try to use jQuery ($) in line var metaTag = $("meta[name='application-name']").
Somehow, jQuery is not loaded properly by the time the call is made.
My question that is this the correct approach to test r.js modules with multiple dependencies? If so what's the fundamental error in the above code?
Many Thanks in advance.

Render each row with template - Backbone

Heres my code:
var RowsSubView = Backbone.View.extend({
initialize: function() {
log.debug(this.collection);
},
render: function() {
var html = RowView();
this.setElement(html);
return this;
}
});
var View = BaseView.extend({
id: 'wrapper',
className: 'container-fluid',
events: {
},
initialize: function() {
_.bindAll(this, 'render');
log.debug('Initialized Queue View');
this.opportunities = new Opportunities();
this.opportunities.on('add', function(model){
});
this.opportunities.fetch({
success: function(response, options) {
},
error: function(response) {
}
});
},
render: function() {
var template = QueueView();
this.$el.html(template);
this.renderRowsSubView();
return this;
},
renderRowsSubView: function() {
// render rows
this.row = new RowsSubView({collection: this.opportunities});
this.row.render();
this.$el.find('tbody').append(this.row.el);
}
});
Heres my question:
Sorry for the noob question! I am learning Backbone and having a bit of an issue. I've looked at a bunch of tutorials/guides, but I think I've confused myself.
I am trying to create a list of items and render them in a table. I want to pass each item into my template and spit it out in the view.
I am stuck after passing my collection to my RowsSubView. I'm not sure how to render each object in the template. Then insert those.
PS: I am able to log this.collection in my RowsSubView and see an object with the array of items.
Thanks.
Ok well start with this. Looks like there's quite a bit of cleanup that needs to be done =)
var RowsSubView = Backbone.View.extend({
initialize: function() {
log.debug(this.collection);
},
render: function() {
//var html = RowView(); // Looks like you're already placing a tbody as the container
//this.setElement(html);
this.collection.forEach(function( model ){
this.$el.append( RowView( model.toJSON() ) ); // Assuming RowView knows what to do with the model data
});
return this;
}
});
Then change the renderRowsSubView to
renderRowsSubView: function() {
// render rows
this.row = new RowsSubView({collection: this.opportunities});
this.row.render();
this.$el.find('tbody').append(this.row.$el.html());
}
For those that this might help, heres what I ended up with:
var RowsSubView = Backbone.View.extend({
initialize: function() {
},
render: function() {
var html = RowView({
opp: this.model.toJSON()
});
this.setElement(html);
return this;
}
});
var View = BaseView.extend({
id: 'wrapper',
className: 'container-fluid',
events: {
},
initialize: function() {
_.bindAll(this, 'render', 'add');
log.debug('Initialized Queue View');
this.opportunities = new Opportunities();
this.opportunities.on('add', this.add);
this.fetch();
},
add: function(row) {
this.row = new RowsSubView({model: row});
this.row.render();
$('tbody').append(this.row.el);
},
fetch: function() {
this.opportunities.fetch({
data: $.param({
$expand: "Company"
}),
success: function(response, options) {
// hide spinner
},
error: function(response) {
// hide spinner
// show error
}
});
},
render: function() {
var template = QueueView();
this.$el.html(template);
return this;
}
});
return View;
});

Backbonejs usage of el

I've created 2 separate views, 1 to render the template and the other one is where I bind the events, then I tried merging them into one in which case it causes an Uncaught TypeError: Object [object Object] has no method 'template'. It renders the template and the events are working as well, but I get the error.
edit.js, this is the combined view, which I think it has something to do with their el where the error is coming from
window.EditView = Backbone.View.extend ({
events: {
"click #btn-save" : "submit"
},
initialize: function() {
this.render();
},
render: function() {
$(this.el).html(this.template());
return this;
},
submit: function () {
console.log('editing');
$.ajax({ ... });
return false;
}
});
var editView = new EditView();
signin.js, this is the view that I can't merge because of the el being used by the ajax call and in SigninView's $(this.el) which causes the rendering of the templates faulty
window.toSigninView = Backbone.View.extend ({
el: '#signin-container',
events: {
"click #btn-signin" : "submit"
},
initialize: function() {
console.log('Signin View');
},
submit: function() {
$.ajax({ ... });
return false;
}
});
var toSignin = new toSigninView();
window.SigninView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
$(this.el).html(this.template());
return this;
}
});
and I use utils.js to call my templates
window.utils = {
loadTpl: function(views, callback) {
var deferreds = [];
$.each(views, function(index, view) {
if (window[view]) {
deferreds.push($.get('templates/' + view + '.html', function(data) {
window[view].prototype.template = _.template(data);
}));
} else {
alert(view + " not found");
}
});
$.when.apply(null, deferreds).done(callback);
}
};
In my Router.js, this is how I call the rendering of templates
editProfile: function() {
if (!this.editView) {
this.editView = new EditView();
}
$('#global-container').html(this.editView.el);
},
utils.loadTpl (['SigninView', 'EditView'],
function() {
appRouter = new AppRouter();
Backbone.history.start();
});
I think that I figured out your problem.
First merge your views and delete the line var toSignin = new toSigninView();
Second modify your utils.js code like this :
window[view].prototype.template = _.template(data);
new window[view]();

Categories