Creating a jQuery container to hold other functions - javascript

I am trying to create a simple class or a container to hold few JavaScript methods as shown below.
var Core = (function () {
var Error = {
alert: function() {
alert('Error => alert called');
},
display: function() {
alert('Error => display called');
}
};
var ajax = {
view: function(){
alert('Ajax => view called');
},
update: function(){
alert('Ajax => update called');
}
};
var call = function(){
Error.alert();
Error.display();
ajax.view();
ajax.update();
};
$(document).ready(function(){
call(); // This works fine.
}());
But for some reason I am not able to call these methods outside the container. For instance calling functions as shown below throws error.
$(document).ready(function(){
Core.Error.alert(); // This doesn't work.
Core.Call(); // This doesn't work.
});
Error: Uncaught TypeError: Cannot read property 'Error' of undefined
I can call the functions from within the container. I am new to the concept of jQuery plugins and would appreciate if someone can help me with this.

You can export these methods (read more about Module Pattern), like so
var Core = (function () {
...
return {
Error: Error,
Ajax: ajax,
Call: call
};
})();
Core.Error.alert();
Core.Call();
Example

Change it in:
var Core = {
error: {
alert: function () {
alert('Error => alert called');
},
display: function () {
alert('Error => display called');
}
},
ajax: {
view: function () {
alert('Ajax => view called');
},
update: function () {
alert('Ajax => update called');
}
},
call: function () {
Core.error.alert();
Core.error.display();
Core.ajax.view();
Core.ajax.update();
}
}
$(document).ready(function () {
Core.call(); // This works
}());
Working fiddle

Related

Argument is returning empty value on console.log

I am trying to pass an argument down the tree to the successResponse errorResponse functions and display the value in the console before I do any work with it.
Currently I am getting an empty value in the console so there must be something missing in my code. I am thinking its a return statement but when I attempt this I get no result.
Any help would be greatly appreciated.
The console.log is below.
successResponse: function (getSel) {
requestResponses.errorCode = false;
requestResponses.redLight.removeClass(requestResponses.redBright);
requestResponses.greenLight.addClass(requestResponses.greenBright);
console.log(getSel);
},
Here is the full version of my code
var requestResponses = {
greenLight: $('.cp_trafficLight_Light--greenDimmed'),
redLight: $('.cp_trafficLight_Light--redDimmed'),
greenBright: 'cp_trafficLight_Light--greenBright',
redBright: 'cp_trafficLight_Light--redBright',
settings: {
flashError: 400,
requestTime: 10000
},
init: function (url, getSel) {
requestResponses.url = url;
requestResponses.getResponse(requestResponses.url, getSel);
setInterval(function () {
if (requestResponses.errorCode === true) {
requestResponses.redLight.toggleClass(requestResponses.redBright);
}
}, requestResponses.settings.flashError);
},
successResponse: function (getSel) {
requestResponses.errorCode = false;
requestResponses.redLight.removeClass(requestResponses.redBright);
requestResponses.greenLight.addClass(requestResponses.greenBright);
console.log(getSel);
},
errorResponse: function () {
requestResponses.greenLight.removeClass(requestResponses.greenBright);
},
getResponse: function (serverURL, getSel) {
$.ajax(serverURL, {
success: function (getSel) {
requestResponses.errorCode = false;
requestResponses.successResponse(getSel);
},
error: function () {
requestResponses.errorCode = true;
requestResponses.errorResponse();
},
complete: function () {
setTimeout(function () {
requestResponses.getResponse(requestResponses.url);
}, requestResponses.settings.requestTime);
}
});
},
errorCode: false
}
requestResponses.init('/status');
Appreciate any help.
Your code looks fine. Make sure that the server actually responds with data. The problem is most likely on back-end.

EmberJS: Refreshing a model?

Hello again everyone.
EDIT: I want to emphasize that I can find no docs on the solution for this.
I am using a route to perform a search query to my server. The server does all the data logic and such and returns a list of objects that match the keywords given. I am taking those results and feeding them to the model so that I can use the {{#each}} helper to iterate over each result.
The problem I am having is that the model does not want to refresh when the searchText (search input) changes. I've tried several things. I'm not worried about creating too many ajax requests as my server performs the search query in 2ms. Here's what I have now.
App.SearchView = Ember.View.extend({...
EDIT:
Thank you for the answer.
App.SearchView = Ember.View.extend({
didInsertElement: function () {
this._super();
Ember.run.scheduleOnce('afterRender', this, this.focusSearch);
},
focusSearch: function () {
$(".searchInput").focus().val(this.get("controller").get('searchTextI'));
}
});
App.SearchRoute = Ember.Route.extend({
model: function () {
return this.controllerFor('search').processSearch();
}
});
App.SearchController = Ember.ArrayController.extend({
searchTextI: null,
timeoutid: null,
processid: null,
updateSearch: function () {
if(this.get('timeoutid')) {clearTimeout(this.get('timeoutid')); }
var i = this.get('searchTextI');
var sc = this;
clearTimeout(this.get('processid'));
this.controllerFor('index').set('searchText', i); //set the search text on transition
if(i.length < 3) {
this.set('timeoutid', setTimeout(function () {
sc.controllerFor('index').set("transitioningFromSearch", true);
sc.transitionToRoute('index');
}, 1500));
} else {
var self = this;
this.set('processid', setTimeout(function() {
self.processSearch().then(function(result) {
self.set('content', result);
});
}, 1000));
}
}.observes('searchTextI'),
processSearch: function () {
return $.getJSON('http://api.*********/search', { 'token': guestToken, 'search_query': this.get('searchTextI') }).then(function(data) { if(data == "No Results Found.") { return []; } else { return data; } }).fail(function() { return ["ERROR."]; });
}
});
Don't observe anything within a route and don't define any computed properties. Routes are not the place for these. Apart from that, the model doesn't fire because controller is undefined.
One way to achieve what you want:
App.SearchRoute = Ember.Route.extend({
model: function () {
this.controllerFor('search').searchQuery();
}.observes('controller.searchText') //not triggering an ajax request...
});
App.SearchController = Ember.ArrayController.extend({
searchQuery: function() {
return $.getJSON('http://api.**************/search', { 'token': guestToken, 'search_query': t }).fail(function() {
return null; //prevent error substate.
});
}
onSearchTextChange: function() {
var controller = this;
this.searchQuery().then(function(result) {
controller.set('content', result);
});
}.observes('searchText')
});
Putting an observes on the model hook is not going to do anything. You should simply do what you were thinking of doing and say
processSearch: function () {
this.set('content', $.getJSON....);
}

Failed to load routed module requirejs? durandal bug?

I created an Asp.Net MVC and used nuget to add HotTowel (V2.0.1 of 9/11/2013). I created a couple of ViewModel, Models. However, I got the following error.
"Failed to load routed module (viewmodels/myVM). Details: Load timeout for modules: durandal/plugins/router\nhttp://requirejs.org/docs/errors.html#timeout"
Is it the problem of durandal/plugins/router? Or it can be caused by some code I added?
The error occurred at Scripts/durandal/system.js.
var logError = function(error) {
if(error instanceof Error){
throw error;
}
throw new Error(error);
};
The following is the VM code.
define(['services/datacontext', 'durandal/plugins/router', 'services/logger'],
// Remove the durandal/plugins/router and the functions will get rid of the error.
function (datacontext, router, logger) {
var title = 'Event';
var vm = {
activate: activate,
deactivate: deactivate,
refresh: refresh,
events: events,
title: title
};
return vm;
//#region Internal Methods
var events = ko.observableArray();
function activate() {
logger.log(title + ' View Activated', null, title, true);
return datacontext.getEventPartials(events);
}
var deactivate = function () {
events([]);
};
var refresh = function () {
return datacontext.getEventPartials(events, true);
};
//#endregion
});
The following is the call stack
logError [system.js] Line 92 Script
Anonymous function [router.js] Line 359 Script
[External Code]
Anonymous function [system.js] Line 260 Script
[External Code]
[Async Call]
....
Code at router.js,
isProcessing(true);
router.activeInstruction(instruction);
if (canReuseCurrentActivation(instruction)) {
ensureActivation(activator.create(), currentActivation, instruction);
} else {
system.acquire(instruction.config.moduleId).then(function(module) {
var instance = system.resolveObject(module);
ensureActivation(activeItem, instance, instruction);
}).fail(function(err){
system.error('Failed to load routed module (' + instruction.config.moduleId + '). Details: ' + err.message);
});
}
}
And previous one in system.js.
acquire: function() {
var modules,
first = arguments[0],
arrayRequest = false;
if(system.isArray(first)){
modules = first;
arrayRequest = true;
}else{
modules = slice.call(arguments, 0);
}
return this.defer(function(dfd) {
require(modules, function() {
var args = arguments;
setTimeout(function() {
if(args.length > 1 || arrayRequest){
dfd.resolve(slice.call(args, 0));
}else{
dfd.resolve(args[0]);
}
}, 1);
}, function(err){
dfd.reject(err);
});
}).promise();
},
Based on the comments I'd recommend to modify the vm code slightly, so that all variables that are returned via vm are defined before use. In addition 'plugins/router' is used instead of 'durandal/plugins/router'.
define(['services/datacontext', 'plugins/router', 'services/logger'],
// Remove the durandal/plugins/router and the functions will get rid of the error.
function (datacontext, router, logger) {
var title = 'Event';
var events = ko.observableArray();
var deactivate = function () {
events([]);
};
var refresh = function () {
return datacontext.getEventPartials(events, true);
};
var vm = {
activate: activate,
deactivate: deactivate,
refresh: refresh,
events: events,
title: title
};
return vm;
//#region Internal Methods
function activate() {
logger.log(title + ' View Activated', null, title, true);
return datacontext.getEventPartials(events);
}
//#endregion
});
BTW the name Internals methods is misleading as everything in that region is returned via vm. I prefer to work with named function instead, which get created before the return statement if they are returned and place them below the return statement in a Internal methods region if they are not returned.
define(['services/datacontext', 'plugins/router', 'services/logger'],
function( datacontext, router, logger ) {
var title = 'Event';
var events = ko.observableArray();
function deactivate () {
events([]);
}
function refresh () {
return datacontext.getEventPartials(events, true);
}
function activate () {
logger.log(title + ' View Activated', null, title, true);
return datacontext.getEventPartials(events);
}
return {
activate: activate,
deactivate: deactivate,
refresh: refresh,
events: events,
title: title
};
//#region Internal Methods
//#endregion
});

angular-ui/bootstrap: Testing a controller that uses a dialog

I've a controller that uses a Dialog from angular-ui/bootstrap:
function ClientFeatureController($dialog, $scope, ClientFeature, Country, FeatureService) {
//Get list of client features for selected client (that is set in ClientController)
$scope.clientFeatures = ClientFeature.query({clientId: $scope.selected.client.id}, function () {
console.log('getting clientfeatures for clientid: ' + $scope.selected.client.id);
console.log($scope.clientFeatures);
});
//Selected ClientFeature
$scope.selectedClientFeature = {};
/**
* Edit selected clientFeature.
* #param clientFeature
*/
$scope.editClientFeature = function (clientFeature) {
//set selectedClientFeature for data binding
$scope.selectedClientFeature = clientFeature;
var dialogOpts = {
templateUrl: 'partials/clients/dialogs/clientfeature-edit.html',
controller: 'EditClientFeatureController',
resolve: {selectedClientFeature: function () {
return clientFeature;
} }
};
//open dialog box
$dialog.dialog(dialogOpts).open().then(function (result) {
if (result) {
$scope.selectedClientFeature = result;
$scope.selectedClientFeature.$save({clientId: $scope.selectedClientFeature.client.id}, function (data, headers) {
console.log('saved.');
}, null);
}
});
};
});
I'm almost completely new to testing, and figured that maybe I need to test two things:
That a dialog opens when $scope.editClientFeature() is called
That $save is called successfully after a dialog is closed and returns a 'result'
My really messed up test now looks like this:
describe('ClientFeatureController', function () {
var scope, $dialog, provider;
beforeEach(function () {
inject(function ($controller, $httpBackend, $rootScope, _$dialog_) {
scope = $rootScope;
$dialog = _$dialog_;
//mock client
scope.selected = {};
scope.selected.client = {
id: 23805
};
$httpBackend.whenGET('http://localhost:3001/client/' + scope.selected.client.id + '/clientfeatures').respond(mockClientFeatures);
$controller('ClientFeatureController', {$scope: scope});
$httpBackend.flush();
});
});
it('should inject dialog service from angular-ui-bootstrap module', function () {
expect($dialog).toBeDefined();
console.log($dialog); //{}
});
var dialog;
var createDialog = function (opts) {
dialog = $dialog.dialog(opts);
};
describe('when editing a clientfeature', function () {
createDialog({});
console.log(dialog); //undefined
// var res;
// var d;
// beforeEach(function () {
// var dialogOpts = {
// template: '<div>dummy template</div>'
// };
// console.log(dialog);
// d = $dialog.dialog(dialogOpts);
// d.open();
// });
//
// it('should open a dialog when editing a client feature', function () {
// expect(d.isOpen()).toBe(true);
// });
});
});
The immediate problem now is that I'm unable to create/open a dialog. I get the following error:
Chrome 25.0 (Mac) ClientFeatureController when editing a clientfeature encountered a declaration exception FAILED
TypeError: Cannot call method 'dialog' of undefined
It would be great if someone has already written a test for a similar use case and can provide me with an example as I'm pretty lost.
Thanks,
Shaun
I was struggling with the same problem until right now, after trolling the the github repo i found the dialog tests and used that as a starting point :
var $dialog,$scope,$httpBackend;
beforeEach(module('ui.bootstrap.dialog'));
beforeEach(function(){
inject(function (_$dialog_, _$httpBackend_, $controller){
$dialog = _$dialog_;
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/appServer/list')
.respond([{
id:1,
name:'test1'
},
{
id:2,
name:'test2'
},
{
id:3,
name:'test3'
}]);
//setup controller scope
scope = {};
ServerCtrl = $controller('ServerCtrl', {
$scope: scope,
$dialog:$dialog
});
});
});
I also prefer a proper mock. When it is not available, i patch the service
To test this:
$dialog.messageBox(title, msg, btns)
.open()
.then(function (result) {
if (result == 'ok') {
// block executed if user click OK
}
});
You can patch $dialog like this:
$dialog.messageBox = function (title, msg, btns) {
return {
open: function () {
return {
then: function (callback) {
callback('ok'); // 'ok' will be set to param result
}
}
}
}
};
Personally I try to mock all services out. If the ui-bootstrap project does not provide a $dialog mock, you should open a bug ticket there and ask them for one. However creating one is as easy.
The mock service should have fake methods that do nothing but return promises. It should also give you a method to flush all asynchronous methods to make it easier to do synchronous testing.
I find it clearest to write my own mock of the dialog. Here's an example of mocking out a dialog to simulate "yes" being chosen.
Code under test
.controller('AdminListingCtrl', function AdminListingController($scope, $dialog, houseRepository) {
$scope.houses = houseRepository.query();
$scope.remove = function (house) {
var dlg = $dialog.messageBox('Delete house', 'Are you sure?', [
{label: 'Yep', result: 'yes'},
{label: 'Nope', result: 'no'}
]);
dlg.open().then(function (result) {
if (result == 'yes') {
houseRepository.remove(house.id);
$scope.houses = houseRepository.query();
}
});
};
}
Tests
describe('when deleting a house', function () {
var fakeDialog = {
open: function()
{
return {
then: function(callback) {
callback("yes");
}
};
}
};
beforeEach(inject(function($dialog) {
spyOn($dialog, 'messageBox').andReturn(fakeDialog);
}));
it('should call the remove method on the houseRepository', function () {
scope.remove({id: 99});
expect(houseRepository.remove).toHaveBeenCalledWith(99);
});
// etc
});

Backbonejs when to initialize collections

I'm building small one page application with rails 3.1 mongodb and backbonejs.
I have two resources available through json api. I created two models and collections in backbone which look like this
https://gist.github.com/1522131
also I have two seprate routers
projects router - https://gist.github.com/1522134
notes router - https://gist.github.com/1522137
I generated them with backbonejs-rails gem from github so code inside is just template. I initialize my basic router inside index.haml file
#projects
:javascript
$(function() {
window.router = new JsonApi.Routers.ProjectsRouter({projects: #{#projects.to_json.html_safe}});
new JsonApi.Routers.NotesRouter();
Backbone.history.start();
});
I don't want fetch notes when application is starting, because there is big chance that user will never look inside notes. So there isn't good reason to fetch it on start. Inside NotesRouter in all action I rely on #notes variable but without .fetch() method this variable is empty. Also I should can reproduce notes view from url like
/1/notes/5
project_id = 1
note_id = 5
What is best practices in backbonejs to solve this kind of problem ?
Why don't you lazy load the notes when it's requested? Here's an example:
var State = Backbone.Model.extend({
defaults: {
ready: false,
error: null
}
});
var Note = Backbone.Model.extend({
initialize: function () {
this.state = new State();
}
});
var Notes = Backbone.Collection.extend({
model: Note,
initialize: function () {
this.state = new State();
}
});
var NoteCache = Backbone.Model.extend({
initialize: function () {
this._loading = false;
this._loaded = false;
this._list = new Notes();
},
_createDeferred: function (id) {
var note = new Note({ id: id });
this._list.add(note);
this._load();
return note;
},
getNote: function (id) {
return this._list.get(id) || this._createDeferred(id);
},
getNotes: function () {
if (!this._loaded)
this._load();
return this._list;
},
_load: function () {
var that = this;
if (!this._loading) {
this._list.state.set({ ready: false, error: null });
this._loading = true;
$.ajax({
url: '/api/notes',
dataType: 'json',
cache: false,
type: 'GET',
success: function (response, textStatus, jqXHR) {
_.each(response.notes, function (note) {
var n = that._list.get(note.id);
if (n) {
n.set(note);
} else {
that._list.add(note, { silent: true });
n = that._list.get(note.id);
}
n.state.set({ ready: true, error: null });
});
that._list.state.set({ ready: true, error: null });
that._list.trigger('reset', that._list);
that._loaded = true;
},
error: function (jqXHR, textStatus, errorThrown) {
that._list.state.set({ error: 'Error retrieving notes.' });
that._list.each(function (note) {
note.state.set({ error: 'Error retrieving note.' });
});
},
complete: function (jqXHR, textStatus) {
that._loading = false;
}
});
}
}
});
In this example, I'm defining a NoteCache object that manages the lazy loading. I also add a "state" property to the Note model and Notes collection.
You'll probably want to initialize NoteCache somewhere (probably inside your route) and whenever you want a note or notes, just do this:
var note = noteCache.getNote(5);
var notes = noteCache.getNotes();
Now inside your view, you'll want to listen for state changes in case the note/notes is not loaded yet:
var NoteView = Backbone.View.extend({
initialize: function(){
this.note.state.bind('change', this.render, this);
},
render: function(){
if (this.note.state.get('error') {
// todo: show error message
return this;
}
if (!this.note.state.get('ready') {
// todo: show loader animation
return this;
}
// todo: render view
return this;
}
});
I haven't tested this, so there may be some bugs, but I hope you get the idea.

Categories

Resources