Im getting the following error when I try to use jqueru autocomplete together with require js:
'cannot call methods on autocomplete prior to initialization; attempted to call method '/recruiter/temp-search/api/locations/get''
I have a module which initilizes my autocomplete:
define(['jqueryUi'],function($) {
function Location() {
this.autocompleteUrl = "/recruiter/temp-search/api/locations/get";
};
Location.prototype.initAutocomplete = function($txtTown, onSuccessDelegate, countryId, regionId, ignoredInputHandler, includeCountries) {
///<param name="$txtTown" type="jQuery">input text element to decorate with autocomplete</param>
///<param name="onSuccessDelegate" type="function">Invoked with upon item selection with selected value passed as a parameter</param>
///<param name="regionId" type="int">Region constraint. Defaults to null</param>
///<param name="countryId" type="int">Country Id. Defaults to UK id</param>
///<param name="ignoredInputHandler" type="function">
/// function(string term, function success(string term, {data, result, value}[] data), function failure(string term)) delegate
/// that is invoked on autocomplete requests before user input at leaset 2 chars
///</param>
var cId = countryId ? countryId : null;
var rId = regionId ? regionId : null;
var inclCountries = includeCountries === undefined ? false : includeCountries;
var onSuccess = onSuccessDelegate ? onSuccessDelegate : function() {};
$txtTown.autocomplete(this.autocompleteUrl, {
dataType: 'json',
parse: function(data) {
/* validation Location*/
/*To remove error field check on the parentsearch.js self.elements.searchLocation.on('keyup'*/
if ($txtTown.selector === "#Location") {
if (data.Locations.length == 0 && !data.IsPostcode && $txtTown.val().length > 0) {
var locationError = "We couldn't find the location you entered";
jQuery("[data-valmsg-for = 'Location']").text(locationError);
$('#Location').closest('.search-row').find('.search-error').show();
} else {
jQuery("[data-valmsg-for = 'Location']").text("");
}
}
/**/
var rows = [];
if ($.isArray(data.Locations)) {
var locations = data.Locations;
if (locations !== null) {
for (var i = 0; i < locations.length; i++) {
rows[i] = { data: locations[i], value: locations[i], result: locations[i] };
}
}
} else {
if (data.IsPostcode) {
onSuccess(data.Location, data.Postcode);
}
}
return rows;
},
extraParams: { countryId: cId, regionId: rId, includeCountries: inclCountries },
formatItem: function(row) { return row; },
width: 'auto',
minChars: 2,
delay: 100,
max: 10,
ignoredInputHandler: ignoredInputHandler,
selectFirst: false,
cacheLength: 1,
scroll: false
}).result(function(event, row) {
onSuccess(row);
});
};
return new Location();
});
It is being called like this:
location.initAutocomplete(this.elements.searchLocation, onSuccessAutocomplete, undefined, undefined, undefined, true);
here is my config file:
require.config({
paths: {
"JqueryAutocomplete": "../scripts/jquery/plugins/jquery.autocomplete",
"jqueryValidate": "../scripts/jquery/plugins/jquery.validate",
"jqueryUi": "../scripts/jquery/plugins/jquery-ui-1.10.3.custom",
"jquery": "../scripts/jquery/jquery-1.9.1",
"knockout": "../scripts/knockout/knockout-2.3.0",
"ko": "common/knockout-extensions"
},
shim: {
"JqueryAutocomplete": {
exports: "$",
deps: ['jquery']
},
"jqueryValidate": {
exports: "$",
deps: ['jquery']
},
"jqueryUi": {
exports: "$",
deps: ['jquery']
},
"knockout-extensions": {
exports: "knockout",
deps: ['knockout']
}
}
});
Related
I have this code:
circular = () => { //fix circular stuff for json.stringify
seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
var gameon = 1;
var fighter1 = {"userid":"97","username":"john","items":{},"ailments":{}};
var fighter2 = {"userid":"91","username":"james","items":{},"ailments":{}};
var resume = 30;
all = {gameon:gameon,fighter1:fighter1,fighter2:fighter2,resume:resume,inturn:fighter1,outturn:fighter2};
fs.writeFileSync(file,JSON.stringify(all,circular()),{encoding:'utf8',flag:'w'});
I expect to have the next output written to file:
{
"gameon":1,
"fighter1":{
"userid":"97",
"username":"john",
"items": {},
"ailments":{}
},
"fighter2":{
"userid":"91",
"username":"james",
"items":{},
"ailments":{}
},
"resume":"",
"inturn":{
"userid":"97",
"username":"john",
"items":{},
"ailments":{}
},
"outturn":{
"userid":"91",
"username":"james",
"items":{},
"ailments":{}
}
but this is what I get instead:
{
"gameon":1,
"fighter1":{
"userid":"97",
"username":"john",
"items":{},
"ailments":{}
},
"fighter2":{
"userid":"91",
"username":"james",
"items":{},
"ailments":{}
},
"resume":""
}
Please notice how the string truncates after "resume" like it couldn't read the variables fighter1 and fighter2 despite it could do it for the first iteration.
Why is that?
Thank you.
Caveat: I am not the author of this project. Whoever originally wrote this is no longer with the organization and I am seemingly the most knowledgeable on this topic at this point.
I know a little about javascript and unit tests, so I successfully added one .spec.js file. I tried adding a second one for another module, reusing a lot of the spec setup, and it immediately broke.
Project resources:
Nodejs 12.16.1
jasmine-node-karma: "^1.6.1"
karma: "^6.3.12"
Contents of ./karma.conf.js:
module.exports = function(config) {
config.set({
basePath: './public',
frameworks: ['jasmine', 'jquery-3.2.1'],
files: [
"../node_modules/angular/angular.js",
"../node_modules/angular-mocks/angular-mocks.js",
"../node_modules/bootstrap/dist/js/bootstrap.js",
"../public/**/*.js",
],
exclude: [
],
preprocessors: {
},
client: {
captureConsole: true
},
browserConsoleLogOptions: {
terminal: true,
level: ""
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['FirefoxHeadless', 'ChromeHeadlessNoSandbox', 'PhantomJS'],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
},
FirefoxHeadless: {
base: 'Firefox',
flags: ['-headless'],
}
},
singleRun: false,
concurrency: Infinity
})
}
Originally I added ./public/controllers.spec.js to match the existing ./public/controllers.js. These unit tests pass and continue to do so.
Yesterday I added ./public/backups/backupcontrollers.spec.js to match ./public/backups/backupcontrollers.js.
Contents of ./public/backups/backupcontrollers.js:
/**
* Angular controller.
*/
'use strict'
const backupApp = angular.module('backup', [])
const backupTypePath = 'elasticsearch'
backupApp.controller('BackupFormController', ['$scope', '$filter', '$http', function ($scope, $filter, $http) {
console.log('Started BackupFormController')
$scope.itemInstances = []
$scope.fetchStatus = 'Ready!'
$scope.processSelection = function (item, backupType = backupTypePath) {
$scope.currentItem = item.metadata.name
$scope.getBackup(backupType)
console.log('currentItem after selecting from dropdown: ' + $scope.currentItem)
}
$scope.init = function (backupType = backupTypePath) {
$scope.refreshItemInstances(backupType)
console.log('currentItem after loading page for first time: ' + $scope.currentItem)
}
$scope.getBackup = function (backupType = backupTypePath) {
const path = `/v1/backup/${backupType}`
$scope.fetchStatus = `Fetching Backups for Item ${$scope.currentItem}...`
console.log(`Fetching backups for item from ${path}`)
$http.get('/api', { headers: { path: path, item: $scope.currentItem } })
.success(function (data, status, headers, config) {
console.log(`Got data from GET on path ${path}, HTTP status ${status}: ${JSON.stringify(data)}`)
if (typeof data === 'string' || data instanceof String) {
$scope.backups = data.split(/\r?\n/)
} else {
$scope.backups = data
}
$scope.fetchStatus = 'Ready!'
console.log('Done fetching backup list for item:' + $scope.currentItem + '!')
})
.error(function (data, status, header, config) {
console.log(data)
$scope.fetchStatus = 'Ready!'
})
}
// Refresh the list of displayed Item instances
$scope.refreshItemInstances = function (backupType = backupTypePath) {
console.log('Fetching list of all items in the system ...')
$scope.fetchStatus = 'Fetching Items ... '
$http.get('/env')
.success(function (data, status, headers, config) {
console.log(data)
for (let i = 0; i < data.length; i++) {
$scope.itemInstances.push(data[i])
}
$scope.currentItem = $scope.itemInstances[0].metadata.name
console.log('Done fetching list of all items!')
console.log('currentItem after fetching list of all items: ' + $scope.currentItem)
$scope.fetchStatus = 'Ready!'
$scope.getBackup(backupType)
})
.error(function (data, status, header, config) {
console.log(data)
$scope.fetchStatus = 'Ready!'
})
}
}])
Contents of ./public/backups/backupcontrollers.spec.js:
describe('BackupFormController', function () {
let $controller, $rootScope, $httpBackend
beforeEach(module('backup'))
const mockBackupString = 'string of backup data'
const mockBackupData = {
body: mockBackupString
}
const mockItemsUnsorted = [
{
metadata: {
name: 'prod-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
},
{
metadata: {
name: 'dev-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
},
{
metadata: {
name: 'integ-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
}
]
beforeEach(inject(function ($injector) {
$rootScope = $injector.get('$rootScope')
const $controller = $injector.get('$controller')
$httpBackend = $injector.get('$httpBackend')
const mockEnv = $httpBackend.when('GET', '/env')
.respond(mockItemsUnsorted)
const mockAPI = $httpBackend.when('GET', '/api')
.respond(mockBackupString)
const createController = function () {
return $controller('BackupFormController', { '$scope': $rootScope })
}
}))
describe('$scope.getBackup', function () {
beforeEach(function () {
spyOn(console, 'log')
})
it('should GET /api and set $scope.backups', function () {
controller = createController()
console.log('Dumping fetchStatus: ', $rootScope.fetchStatus)
$rootScope.init()
$httpBackend.flush()
expect($rootScope.backups).toEqual(mockBackupString)
expect(console.log).toHaveBeenCalled()
})
})
})
It seems like this new spec isn't working correctly at all; when I run npm test I see the normal successful tests from ./public/controllers.spec.js but also:
Chrome Headless 105.0.5195.125 (Mac OS 10.15.7) BackupFormController $scope.getBackup should GET /api and set $scope.backups FAILED
ReferenceError: createController is not defined
at UserContext.<anonymous> (backup/backupcontrollers.spec.js:51:7)
at <Jasmine>
This is the only output concerning ./public/backups/backupcontrollers.spec.js.
Has anybody run into this before? I found some posts regarding including angular-mocks, but as you can see in karma.conf.js, it's being included.
I am using the following plugin to paralelized the uglify step in webpack
https://github.com/tradingview/webpack-uglify-parallel
unfortunately, I get the following error:
Fatal error: Cannot set property '__UglifyJsPlugin' of undefined
Here's my webpack config:
/*jslint node: true */
var webpack = require("webpack"),
_ = require("lodash"),
path = require("path"),
fs = require("fs"),
os = require('os'),
UglifyJsParallelPlugin = require('webpack-uglify-parallel'),
webpackConfig,
commonsPlugin = new webpack.optimize.CommonsChunkPlugin('m-common-[chunkhash:8].js'),
dedupePlugin = new webpack.optimize.DedupePlugin(),
uglifyPlugin = new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
parallelUglify = new UglifyJsParallelPlugin({
workers: os.cpus().length
}),
sourceMapPlugin = new webpack.SourceMapDevToolPlugin('[file].map', null,
'[absolute-resource-path]',
'[absolute-resource-path]'),
fnCaseSensitivityPlugin = function () {
this.plugin('normal-module-factory', function(nmf) {
nmf.plugin('after-resolve', function(data, done) {
var parentDir = path.dirname(data.resource);
var resourceName = path.basename(data.resource);
fs.readdir(parentDir, function(err, files) {
if (err) {
done(err);
}
if (files.indexOf(resourceName) === -1) {
var realName = _.find(files, function(filename) {
return filename.toLowerCase() === resourceName.toLowerCase();
});
done(new Error('CaseSensitivityPlugin: `' + resourceName + '` does not match the corresponding file on disk `' + realName + '`'));
return;
}
done(null, data);
});
});
});
},
fnDonePlugin = function () {
this.plugin("done", function(stats) {
var statsByChunk = JSON.parse(JSON.stringify(stats.toJson())).assetsByChunkName;
webpackConfig.statsByChunk = statsByChunk;
for (var chunkName in statsByChunk) {
if (statsByChunk.hasOwnProperty(chunkName)) {
var nameIn = statsByChunk[chunkName][0],
gzName = nameIn.substring(0, nameIn.length - 2) + "gz.js";
webpackConfig.filesToCompress['./marvel-web/dist/' + gzName] = './marvel-web/dist/' + nameIn;
// The mrvlBuildOut object contains a set of name/value
// pairs to be used during HTML template processing.
// There are certain filenames we need to add to the
// mrvlBuildOut object as properties so that they can be
// injected into HTML templates that make use of them.
// The code below takes the nameIn string which will have
// one of 2 forms:
//
// Debug Builds:
// m-core-fixed.js
//
// Produciton Builds:
// m-core-<chunkid>.js
//
// It strips off everything after the last dash including the
// .js extension so we are left with the base filename. We then
// use that filename base to look up the name of the property
// we should set to that filename on the mrvlBuildOut object.
var map = webpackConfig.mrvlBuildOutFilePropMap,
propName = map[nameIn.replace(/-[^-]+\.js$/ , '')];
if (propName) {
webpackConfig.mrvlBuildOut[propName] = nameIn;
}
}
}
webpackConfig.webpackDoneListeners.forEach(function (fnCall) {
fnCall(statsByChunk);
});
});
};
webpackConfig = {
mrvlBuildOut: {
//gets filled in with m-common-[chunkhash] and m-web-[chunkhash]
},
mrvlBuildOutFilePropMap: {
"m-common": "marvelcommon",
"m-web": "marvelweb",
"m-blog": "marvelblog",
"m-gallery": "marvelgallery",
"m-landing": "marvellanding",
"m-unsupported": "marvelunsupported",
"m-login": "marvellogin",
"m-voice-chrome": "voicechrome",
"m-apps-chrome": "appschrome",
"m-viewpage-chrome": "viewpagechrome"
},
webpackDoneListeners: [],
filesToCompress: {
},
marvel: {
addVendor: function (name, path, noparse) {
this.resolve.alias[name] = path;
if (noparse) {
this.module.noParse.push(new RegExp(path));
}
this.entry.vendors.push(name);
},
addBuildOutFilePropMapEntry: function( filenameBase, propName ) {
if ( filenameBase && propName ) {
webpackConfig.mrvlBuildOutFilePropMap[filenameBase] = propName;
}
},
stats: {
colors: true,
modules: true,
reasons: false
},
entry: {
'm-web': './marvel-web/js/app.js',
'm-blog': './marvel-web/js/app-blog.js',
'm-gallery': './marvel-web/js/app-gallery.js',
'm-landing': './marvel-web/js/app-landing.js',
'm-unsupported': './marvel-web/js/app-unsupported.js',
'm-login': './marvel-web/js/app-login.js',
'm-voice-chrome': './marvel-web/js/app-voice-chrome.js',
'm-apps-chrome': './marvel-web/js/app-apps-chrome.js',
'm-viewpage-chrome': './marvel-web/js/app-viewpage-chrome.js',
vendors: []
},
resolve: {
modulesDirectories: [
'node_modules',
'marvel-web',
'marvel-web/js'
],
alias: {
// If you find yourself wanting to add an alias here, think
// about if it would be better for your component to live in
// marvel-core instead of in marvel-web. See the README.md in
// marvel-core for more info.
'marvel-apps': __dirname + '/marvel-web/js/apps.js',
'uuid': 'node-uuid'
}
},
output: {
path: 'marvel-web/dist',
filename: '[name]-[chunkhash:8].js',
chunkFilename: '[name]-[chunkhash:8].js'
},
module: {
noParse: [
/[\/\\].html$/
]
},
plugins: [
commonsPlugin,
dedupePlugin,
parallelUglify,
fnCaseSensitivityPlugin,
sourceMapPlugin,
fnDonePlugin
]
}
};
var libs_dir = __dirname + '/marvel-core/src/js/lib/';
webpackConfig.marvel.addVendor("jquery", libs_dir + "jQuery/jquery.min.js", true);
webpackConfig.marvel.addVendor("lodash", libs_dir + "lodash/lodash.min.js", true);
webpackConfig.marvel.addVendor("underscore", libs_dir + "lodash/lodash.min.js", true);
webpackConfig.marvel.addVendor("q", libs_dir + "q/q.js", true);
webpackConfig.marvel.addVendor("backbone", libs_dir + "backbone/backbone-min.js", false);//if we don't parse backbone then require is not found??
webpackConfig.marvel.addVendor("cookie", libs_dir + "cookie/cookie.js", true);
webpackConfig.marvel.addVendor("canvas-to-blob", libs_dir + "canvas-to-blob/canvas-to-blob.js", true);
webpackConfig.marvelDebug = {
watch: true,
keepAlive: true,
stats: webpackConfig.marvel.stats,
entry: webpackConfig.marvel.entry,
resolve: webpackConfig.marvel.resolve,
output: {
path: 'marvel-web/dist',
filename: '[name]-fixed.js',
chunkFilename: '[name]-fixed.js'
},
module: webpackConfig.marvel.module,
//devtool: "eval-source-map",
devtool: "source-map",
//devtool: "eval",
addBuildOutFilePropMapEntry: webpackConfig.marvel.addBuildOutFilePropMapEntry,
plugins: [
commonsPlugin,
dedupePlugin,
fnCaseSensitivityPlugin,
sourceMapPlugin,
fnDonePlugin
]
};
webpackConfig.marvelDebug.output.pathinfo = true;
module.exports = webpackConfig;
The plugin works in my project when I don't include a separate entry for the original UglifyJsPlugin. Try removing that and add its config to the UglifyJsParallelPlugin.
I need to override M.util.init_block_hider function in moodle. The reason is to override the blockicons with my custom ones. I can see piece of code inside the function, as follows:
Y.extend(blockhider, Y.Base, blockhider.prototype, {
NAME : 'blockhider',
ATTRS : {
id : {},
preference : {},
iconVisible : {
value : M.util.image_url('t/switch_minus', 'moodle')
},
iconHidden : {
value : M.util.image_url('t/switch_plus', 'moodle')
},
block : {
setter : function(node) {
return Y.one(node);
}
}
}
});
where the urls for images are stated. I want to reuse this function and override the url in my theme so that only block icons could be replaced. Any thoughts and ideas are welcome.
Here is the complete function inside /lib/javascript-static.js file:
M.util.init_block_hider = function(Y, config) {
Y.use('base', 'node', function(Y) {
M.util.block_hider = M.util.block_hider || (function(){
var blockhider = function() {
blockhider.superclass.constructor.apply(this, arguments);
};
blockhider.prototype = {
initializer : function(config) {
this.set('block', '#'+this.get('id'));
var b = this.get('block'),
t = b.one('.title'),
a = null;
if (t && (a = t.one('.block_action'))) {
var hide = Y.Node.create('<img class="block-hider-hide" tabindex="0" alt="'+config.tooltipVisible+'" title="'+config.tooltipVisible+'" />');
hide.setAttribute('src', this.get('iconVisible')).on('click', this.updateState, this, true);
hide.on('keypress', this.updateStateKey, this, true);
var show = Y.Node.create('<img class="block-hider-show" tabindex="0" alt="'+config.tooltipHidden+'" title="'+config.tooltipHidden+'" />');
show.setAttribute('src', this.get('iconHidden')).on('click', this.updateState, this, false);
show.on('keypress', this.updateStateKey, this, false);
a.insert(show, 0).insert(hide, 0);
}
},
updateState : function(e, hide) {
M.util.set_user_preference(this.get('preference'), hide);
if (hide) {
this.get('block').addClass('hidden');
} else {
this.get('block').removeClass('hidden');
}
},
updateStateKey : function(e, hide) {
if (e.keyCode == 13) { //allow hide/show via enter key
this.updateState(this, hide);
}
}
};
Y.extend(blockhider, Y.Base, blockhider.prototype, {
NAME : 'blockhider',
ATTRS : {
id : {},
preference : {},
iconVisible : {
value : M.util.image_url('t/switch_minus', 'moodle')
},
iconHidden : {
value : M.util.image_url('t/switch_plus', 'moodle')
},
block : {
setter : function(node) {
return Y.one(node);
}
}
}
});
return blockhider;
})();
new M.util.block_hider(config);
});
};
This should work...then you just set the new vis/invis icon and call this.get('iconVisible') and it should chain through.
Y.extend(blockhider, Y.Base, blockhider.prototype, {
NAME : 'blockhider',
ATTRS : {
id : {},
preference : {},
iconVisible : {
value : M.util.image_url(this.get('visIcon'), 'moodle')
},
iconHidden : {
value : M.util.image_url(this.get('invisIcon'), 'moodle')
},
visIcon:{value: 't/switch_plus'},
invisIcon:{value: 't/switch_minus'}
block : {
setter : function(node) {
return Y.one(node);
}
}
}
});
I'm new to backbone.js and express and I have been adapting Christophe Coenraets Wine Cellar REST API example application for my own project.
I am building a form that has several menus needing to be populated from multiple unrelated collections in mongodb.
I am able to populate one menu with one collection, but I have no idea how to get more than one collection to my form View.
Here are the files I am using to populate one menu. How do I expand this to populate two menus?
I suppose I could make a new View for every menu I want to populate - but that seems like overkill.
Can I combine two mongodb find() collections into one object, and list them separately on a page? If so, how?
thanks in advance!
/routes/modules.js contains:
exports.findAllModules = function(req, res) {
db.collection('modules', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
};
/server.js contains:
app.get('/modules', module.findAllModules);
/public/js/main.js contains:
routes: {
"modules" : "list" }
...
list: function(page) {
var p = page ? parseInt(page, 10) : 1;
var moduleList = new ModuleCollection();
moduleList.fetch({success: function(){
console.log('in list function');
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
}});
this.headerView.selectMenuItem('home-menu');
},
...
utils.loadTemplate([
'ModuleListItemView' ], function() {
app = new AppRouter();
Backbone.history.start(); });
/public/models/models.js contains:
window.Module = Backbone.Model.extend({
urlRoot: "/modules",
idAttribute: "_id",
initialize: function () {
this.validators = {};
this.validators.name = function (value) {
return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a name"};
};
validateItem: function (key) {
return (this.validators[key]) ? this.validators[key](this.get(key)) : {isValid: true};
},
validateAll: function () {
var messages = {};
for (var key in this.validators) {
if(this.validators.hasOwnProperty(key)) {
var check = this.validators[key](this.get(key));
if (check.isValid === false) {
messages[key] = check.message;
}
}
}
return _.size(messages) > 0 ? {isValid: false, messages: messages} : {isValid: true};
},
defaults: {
_id: null,
name: ""
} });
window.ModuleCollection = Backbone.Collection.extend({
model: Module,
url: "/modules"
});
/public/js/views/modulelist.js contains:
window.ModuleListView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
} });
window.ModuleListItemView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
return this;
} });
/public/tpl/ModuleListView.html contains:
Not entirely sure how your code works, but here are a few backbone tips.
If you wanna build a menu from a collection don't pass the collection as a model.
Instead of:
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
Use:
$("#content").empty().append(new ModuleListView({collection: moduleList, page: p}).el);
Instead of:
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
}
Use:
render: function () {
this.$el.html('<ul class="thumbnails">');
this.collection.each(function(model) {
this.$('.thumbnails').append(new ModuleListItemView({model: model}).render().el);
}, this);
return this;
}
If you have no need in updating or deleting your models, it's enough to add the url path /modules only to the collection, for reading the initial modules.