requirejs text plugin downloads all templates which are not required - backbone.js

I'm using RequireJS along with text plugin to load the Handlebar templates dynamically in Backbone Layout Manager. But on page load all the templates get downloaded instead of the specified one.
In the case shown below when I just want to render footer all the files (header, modal) gets fetched instead of only footer.tpl.
templateLoader.js
define(function (require) {
var Handlebars = require('handlebars');
var getTemplateFile = function (templateName) {
var tmpl = null;
switch (templateName) {
case 'header':
tmpl = require('text!../html/templates/header.tpl');
break;
case 'footer':
tmpl = require('text!../html/templates/footer.tpl');
break;
case 'modal':
tmpl = require('text!../html/templates/modal.tpl');
break;
}
return tmpl;
};
var _compiled = function (tpl, context) {
var compiled = Handlebars.compile(tpl);
return context ? compiled(context) : compiled;
};
return {
getTemplate: function (templateName, model) {
return _compiled(getTemplateFile(templateName), model);
}
}
});
MyView.js - LayoutManager
App.Views.StoreFooter = Backbone.Layout.extend({
beforeRender: function () {
this.$el.html(Templates.getTemplate('footer'));
}
});
When I check the resources downloaded in Chrome I see modal.tpl, header.tpl which should not be there according to above code.

This is a side-effect of the syntax sugar, described in the documentation :
define(function (require) {
var dependency1 = require('dependency1'),
dependency2 = require('dependency2');
return function () {};
});
The AMD loader will parse out the require('') calls by using Function.prototype.toString(), then internally convert the above define call into this:
define(['require', 'dependency1', 'dependency2'], function (require) {
var dependency1 = require('dependency1'),
dependency2 = require('dependency2');
return function () {};
});
Since it parses the function body as a string, it has no way of seeing that the require statements are inside a switch which is guaranteed to only match one case.
edit:
I thought this could be fixed by refactoring your code a bit:
var getTemplateFile = function (templateName) {
var path = null;
switch (templateName) {
case 'header':
path = 'text!../html/templates/header.tpl';
break;
case 'footer':
path = 'text!../html/templates/footer.tpl';
break;
case 'modal':
path = 'text!../html/templates/modal.tpl';
break;
}
return require(path);
};
Unfortunately, this causes:
Uncaught Error: Module name "text!blah.txt_unnormalized2" has not been loaded yet for context: _
...which makes sense when you realise this is just syntactic sugar, not a way to make RequireJS work in synchronous mode.

Related

protractor --page objects ,getting error Cannot read property 'click' of undefined. How to resolve this error?

trying to access menu of an angular application from spec.js using page object menu.js but when running the protractor error is coming up. Failed: Cannot read property 'click' of undefined
spec.js
var loginpage = require('./login');
var Menu = require('./menu');
describe('todo', function() {
it('activity', function() {
var question = new loginpage();
var menu = new Menu();
browser.sleep(10000);
menu.dropdown('Inquiry').click();
});
});
PAGE OBJECT FILE menu.js
var MenuPage = function() {
this.dropdown = function(dropdownName) {
var openDropdown = function() {
element(by.css('.nav'))
.element(by.css('[title=dropdownName]'))
.click();
};
return {
option: function(optionName) {
openDropdown();
return element(by.css('.dropdown.open'))
.element(by.linkText(optionName));
}
}
};
};
module.exports = MenuPage;
Wow, you have old style in your tests. It's much more easier to use new features of JS.
However, you return object. Instead of it return element. You can return function that will return element regarding to your code style:
return function(optionName) {
openDropdown();
return element(by.css('.dropdown.open'))
.element(by.linkText(optionName));
}

How To Get Constant Inside ui-router (Angular) state function

I want to reference a "constant" that is defined outside this state function but if I try to pass it in as a provider it errors because constants are not providers. That is, I want to do something like:
var exports = module.exports = function ($stateProvider,configData) { ...
But that fails. How can I get my javascript variable baseDirectory passed in.
The bigger problem is that the webroot is not always at the same url. sometimes it is /ng and sometimes it is just / and I want to be able to set that as a config someplace I can load into a config file (not hard code into the state function.
var exports = module.exports = function ($stateProvider) {
$stateProvider.state('home', {
url: baseDirectory + '/home',
templateUrl: '/templates/home/home.html',
controller: 'HomeController',
});
};
exports.$inject = ['$stateProvider'];
I got the same issue, what I did, is to build a constant in the app, and replace them with new value in gulp task when the base url changed.
It had many apps, so my idea is dumb and simple, just find and replace the value of the content in the app by the new value input from gulp task configuration.
It's not smart, but works.
In the example, I have three apps in /clinic, /panel, /company, and for each of them, it had a build.js in it.
The build.js will build the app, and replace the constant value.
I have many apps, so I do this:
var gulp = require('gulp');
var concat = require('gulp-concat');
var taskBuildClinic = require("./clinic/build");
var taskBuildPanel = require("./panel/build");
var taskBuildCompany = require("./company/build");
var migrate = require("./laravel/migrate");
var env = {
'dev': {
EndpointBaseUrl: "'/admin/public/';",
TemplateBaseUrl: "'/admin/public';"
}
};
// ./node_modules/.bin/gulp
gulp.task('clinic', function (cb) {
return taskBuildClinic(gulp, env);
});
gulp.task('company', function (cb) {
return taskBuildCompany(gulp, env);
});
gulp.task('panel', function (cb) {
return taskBuildPanel(gulp, env);
});
In each build.js located in different app, it had:
var concat = require('gulp-concat');
var replace = require('gulp-replace');
...
if (env['production']) {
// replace something not for production
endpoint = "var EndpointBaseUrl = " + env['production']['EndpointBaseUrl'];
templateBaseUrl = "var TemplateBaseUrl = " + env['production']['TemplateBaseUrl'];
console.log(endpoint);
console.log(templateBaseUrl);
} else {
endpoint = "var EndpointBaseUrl = " + env['dev']['EndpointBaseUrl'];
templateBaseUrl = "var TemplateBaseUrl = " + env['dev']['TemplateBaseUrl'];
console.log(endpoint);
}
return gulp.src(files)
.pipe(concat(prefix + '.js'))
.pipe(replace(/var EndpointBaseUrl.*?;/g, endpoint))
.pipe(replace(/var TemplateBaseUrl.*?;/g, templateBaseUrl))
.pipe(gulp.dest('./build'));
In the app, I had:
var TemplateBaseUrl = "";
var EndpointBaseUrl = "";
Their content will be replaced by the value from gulp task.

Using angular functions ($q) in protractor

I want to use the $q service of angular in my e2e tests. (I want to get the texts of a bunch of elements via getText() which returns a promise. After all promises are resolved, I want to test the list. So I want to use $q.all() etc.)
angular.injector(['myApp']).get('$q'); results in "ReferenceError: angular is not defined"
Installing angular via node and then var angular = require("angularjs"); results in "Error: Cannot find module 'angular'"
Also, inserting a browser.waitForAngular() does not help there.
Using the inject(function($q) {}) syntax has the same problem.
How can I use such angular functions in protractor?
edit:
Here's the very naive version of what I want to achieve
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
var key = getSomeKey();
var entry = processEntry(text);
entries[key] = entry;
});
});
return entries;
};
That works in principle, at some point in time entries contains all data. However, I need to wait for that moment. What I would do is create and return a promise that gets resolved as soon as all getText promises are resolved.
e.g.
var deferred = $q.defer();
$q.all(getTextPromises).then(function () {
deferred.resolve(entries);
});
return deferred.promise;
From the looks of your code containers is a list of elementFinders? (i.e. var containers = [element(by.x), element(by.y), element(by.z)]):
Using q: (you need to add q as dependency in package.json first)
var q = require('q');
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var deferred = q.defer();
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
deferred.resolve(processEntry(text));
});
entries[getSomeKey()] = deferred.promise();
});
return q.all(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
But protractor knows promise itself (and it's preferably that you don't mix protractor's promise with q promise unless you know what you're doing):
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
entries[getSomeKey()] = container.element(by.tagName('h2')).
getText().then(function (text) {
return processEntry(text);
});
});
return protractor.promise.fullyResolved(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
If your containers are found using a single selector (i.e. var containers = element.all(by.xyz)), it's even easier:
var collectEntries = function(containers) {
return containers.reduce(function(entries, elem) {
return elem.getText().then(function(text) {
entries[getSomeKey()] = processEntry(text);
return entries;
});
}, {});
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})

AngularJs - get list of all registered modules

Can I get a list of all registered modules at run time?
For example:
// Some code somewhere in some .js file
var module1 = angular.module('module1', []);
// Some code in some other .js file
var module2 = angular.module('module2', []);
// Main .js file
var arrayWithNamesOfAllRegisteredModules = .....
// (result would be: ['module1', 'module2'])
Angular does not provide a way to retrieve the list of registered modules (at least I was not able to find a way in source code). You can however decorate angular.module method to store names in array. Something like this:
(function(orig) {
angular.modules = [];
angular.module = function() {
if (arguments.length > 1) {
angular.modules.push(arguments[0]);
}
return orig.apply(null, arguments);
}
})(angular.module);
Now you can check angular.modules array.
Demo: http://plnkr.co/edit/bNUP39cbFqNLbXyRqMex?p=preview
You can simply do :
console.log(angular.module('ModuleYouWantToInspect').requires);
It should return of an array of strings (dependencies). You can do the same for the output.
Given an angular.element, the $injector.modules array contains the list of registered modules.
e.g.
angular.element(document.body).injector().modules
If you're debugging, I discovered you can get the list by:
Find or add code to invoke run() from any module with any body, say:
angular.module('myModule')
.run(function() {})
Put a breakpoint on the .run, and step into angular.run(). There's an object called "modules" in scope that has all the modules as properties, by name.
This may work with other module methods too, or be accessible from code; I haven't tried very hard to understand the larger picture.
Improving solution
(function(angular) {
var orig = angular.module;
angular.modules = [];
angular.modules.select = function(query) {
var cache = [], reg = new RegExp(query || '.*');
for(var i=0,l=this.length;i< l;i++){
var item = this[i];
if(reg.test(item)){
cache.push(item)
}
}
return cache;
}
angular.module = function() {
var args = Array.prototype.slice.call(arguments);
if (arguments.length > 1) {
angular.modules.push(arguments[0]);
}
return orig.apply(null, args);
}
})(angular);
Now you can select modules:
angular.modules.select('app.modules.*')
Creating modules tree:
var app = angular.module('app.module.users', ['ui.router'...]);
var app = angular.module('app.module.users.edit', ['app.modules.users']);
Your main module app (concat submodules)
angular.module('app', ['ui.bootstrap', 'app.services', 'app.config']
.concat(angular.modules.select('app.module.*')));
in addition to #dfsq answer you can get list of modules with it dependencies
var AngularModules = (function (angular) {
function AngularModules() {
extendAngularModule();
angular.element(document).ready(function () {
getModulesDependencies();
});
}
var extendAngularModule = function () {
var orig = angular.module;
angular.modules = [];
angular.module = function () {
var args = Array.prototype.slice.call(arguments);
var modules = [];
if (arguments.length > 1) {
modules.push(arguments[0]);
}
for (var i = 0; i < modules.length; i++) {
angular.modules.push({
'module': modules[i]
});
}
return orig.apply(null, args);
};
};
var getModulesDependencies = function () {
for (var i = 0; i < angular.modules.length; i++) {
var module = angular.module(angular.modules[i].module);
angular.modules[i].dependencies = module && module.hasOwnProperty('requires') ? module.requires : [];
}
};
return AngularModules;
})(angular);
Usage:
var modules = new AngularModules();
There is a similar question with better answers here https://stackoverflow.com/a/19412176/132610, a summary of what they proposed is:
var app = angular.module('app', []);
# app.service(/**your injections*/) etc
# to access to the list of services + injections
app._invokeQueue #has following form:
[
[
'$provide',
'service',
Arguments[
'serviceName',
[
'$dependency1',
'$dependency2',
function(){}
],
]
]
]
This involves poking at implementation details that may change over time, but you can try this.
Load the page fully.
Set a breakpoint inside angular.module().
Call angular.module() from the console.
When you hit the breakpoint execute print out the modules dictionary console.dir(modules) or if you want to copy it into another editor window.prompt('', JSON.stringify(modules))
This works because behind the scenes angular builds a dictionary of the loaded modules called modules. You also want to wait until it's finished loading all the modules so they're in the dictionary.

AngularJS nested functions in services and dependency injection?

Ok I have two modules which depend upon each other both modules have services, directives, ctrl's etc, now my question is how do i get values assigned in the nested function of the second module's service in the controller of the first service, I have added the dependencies to the first controller but i can't see to get at the nested functions variables to then manipulate them in the ctrl of the first module here's the code(considerably cut down):
angular.module("mainapp", [
"dateSheet",
"bookingApp"
]).controller("AppCtrl", [
"$scope",
"$attrs",
"Booking",
function (scope, source, attributes, AppDataLoader, booking, Booking) {
//HERE I NEED TO BE ABLE TO DO SOMETHING LIKE THIS
var getdaiyrate = function(){
var dumpDailyRates = scope.Booking.getalldates.getrates.dailyPrice
console.log(dumpDailyRates);
}
}
]);
angular.module("bookingApp", ["bookingApp.services",]);
angular.module("bookingApp.services").service("Booking", [
function(){
function getRate(source, dateSheet, dateSheetCtrl, expect, $$childTail, appData) {
var dateValue = $("Date", source).text() || "";
if (!dateValue) {
return null;
}
var dailyPrice = $("DailyPrice", source).text() || "";
var weeklyPrice = $("WeeklyPrice", source).text() || "";
var monthlyPrice = $("MonthlyPrice", source).text() || "";
var isAvailable = $("IsAvailable", source).text() === "1";
var minimumStay = Number($("MinimumStay", source).text());
if (isNaN(minimumStay)) {
minimumStay = DEFAULT_MINIMUM_STAY;
}
return {
date: new Date(dateValue),
dailyPrice: dailyPrice,
weeklyPrice: weeklyPrice,
monthlyPrice: monthlyPrice,
reserved: !isAvailable,
minimumStay: minimumStay
};
}
return {
getalldates: function(source, $scope){
return getRate(source, scope);
}
};
}
]);
The above doesn't work what am i doing wrong....
Could someone please send me in the direction of a decent tutorial that deals with a end to end app using various modules and dependencies??
Chris
You need to inject the service module into the module that you want to use it in. So the first line becomes
angular.module("mainapp", ["dateSheet","bookingApp","bookingApp.services"])
Also i don't see the creation of bookingApp.services so this may also be required
angular.module("bookingApp.services",[]);
and the invocation would be something like this
var dumpDailyRates = Booking.getalldates(sourceParameter, $scope);

Resources