I am at a loss here.
I am looking to start adding tests for a site I am working on, using jasmine. I am unable to test any of my views though, because the templates being defined in the initialize functions for my views are pre-compiled handlebars templates that are being saved in an object.
My setup is so:
index.jade gets precompiled on my node server. This jade file had a bunch of
<script id="[name]" type="text/x-handlebars-template">
in it, with the snippets for each view. At the bottom of the index page is a jQuery $.each call:
var templates = {};
$("[type='text/x-handlebars-template']").each(function(index, div){
templates[div.id] = Handlebars.compile(div.innerHTML);
});
That loops through all these script tags, compiles them, and loads them into the templates object. I then save this object as an attribute of my main app model, to pass out to views as needed.
This works well for the site itself, but has proven a blocker for testing. When I try to load my views, they will not initialize as doing so requires the 'templates' object on the main model, with is populated from script tags pulled from the index. But index.html does not exist in my specRunner, short of me copy/pasting all the script tags into the spec runner each time I make a change.
I have no idea what the work-around here is. I'm sure it's something simple and I am too dense to see it, but I have been trying things out and looking up solutions for the better part of five hours, and have nothing to show for it.
I know I can compile the .jade file to .html with grunt, but past that I am unsure how to get the information from the html file to my SpecRunner.
So this was pretty complicated, but I got it working thanks to the magic that is grunt.
I set up grunt to use the grunt-contrib-jade and grunt-contrib-handlebars, which would auto compile my jade include pages to HBS, then compile those HBS files to JS.
jade: {
compile: {
options: {
pretty: true
},
files: [ {
cwd: "views/templates",
src: "**/*.jade",
dest: "spec/hbs",
expand: true,
ext: ".hbs"
} ]
}
},
handlebars: {
compile: {
options: {
namespace: "compTemplates",
},
files: [ {
expand: true,
cwd: 'spec/hbs',
src: '**/*.hbs',
dest: 'spec/helpers',
ext: '.js'
} ]
}
},
Each of these JS files would add their contents to the compTemplates object. These JS files were all pre-loaded into the Jasmine specRunner via Grunt, and the last helper (which I called 'z.js' to make sure it loaded last) included:
var templates = {};
for (var prop in window['compTemplates']) {
var name = prop.split('/')[3];
name = name.split('.')[0];
templates[name] = window['compTemplates'][prop];
}
And voila! I had a templates object available to Jasmine that included all the handlebars templates ready to load into my views!
Related
Why does this happen? When I compile the scripts using GULP the console will display errors, explaining that my directives and/or my controllers are not registered. Then to correct this error I create the app variable within the controller file and it then renders a new error, then I put the app variable declaration back and everything works fine.
This is my Gulp Script
var gulp = require('gulp'),
plugins = require('gulp-load-plugins')({
pattern: ['gulp-*', 'gulp.*'],
replaceString: /\bgulp[\-.]/
});
var path = {
jsFiles: "./js/**",
scriptFile: "scripts.min.js",
output: "dist/assets/"
};
var options = {
ie8: true,
warnings: true,
mangle: true
};
gulp.task('scripts', function (cb) {
return gulp.src(path.jsFiles)
.pipe(plugins.sourcemaps.init())
.pipe(plugins.jsdoc3(cb))
.pipe(plugins.concat(path.scriptFile))
.pipe(plugins.babel())
.pipe(plugins.ngAnnotate())
.pipe(plugins.uglify(options))
.pipe(plugins.sourcemaps.write("../../maps"))
.pipe(gulp.dest(path.output))
})
TLDR: MY Gulp task sometimes compiles the AngularJS directives and controllers out of order rendering my app declaration undefined.
When you pass globe to the
gulp.src
No ordered is guaranteed, so it is possible to get wrong order time to time. But gulp.src also accepts array of the pathes you need to include and this should guarantee the order
So, try to split your bundle and pass path to the angular.min.js as a first element like this:
gulp.src(['path/to/angular.min.js', 'path/to/your/code'])
You should sort angular files, and there are some libs that does that.
https://www.npmjs.com/package/gulp-angular-filesort is one of them.
I've been trying to find the best way to create a modular, scalable angular application. I really like the structure of projects like angular-boilerplate, angular-app, where all the related files are grouped together by feature for partials and directives.
project
|-- partial
| |-- partial.js
| |-- partial.html
| |-- partial.css
| |-- partial.spec.js
However, in all these examples, the template URL is loaded relative to the base url, not relative to the current file:
angular.module('account', [])
.config(function($stateProvider) {
$stateProvider.state('account', {
url: '/account',
templateUrl: 'main/account/account.tpl.html', // this is not very modular
controller: 'AccountCtrl',
});
})
This is not very modular, and could become difficult to maintain in large projects. I would need to remember to change the templateUrl path every time I moved any of these modules. It would be nice if there was some way to load the template relative to the current file like:
templateUrl: './account.tpl.html'
Is there any way to do something like this in angular?
The best way to do this now is using a module loader like browserify, webpack, or typescript. There are plenty of others as well. Since requires can be made from the relative location of the file, and the added bonus of being able to import templates via transforms or loaders like partialify, you don't even have to use template urls anymore. Just simply inline the Template via a require.
My old answered is still available below:
I wrote a post on exactly this subject and spoke on it at our local Angular Meetup. Most of us are now using it in production.
It is quite simple as long as your file structure is represented effectively in your modules. Here is a quick preview of the solution. Full article link follows.
var module = angular.module('myApp.things', []);
var all = angular.module('myApp.things.all', [
'myApp.things',
'things.someThing',
'things.someOtherThing',
'things.someOtherOtherThing',
]);
module.paths = {
root: '/path/to/this/thing/',
partials: '/path/to/this/thing/partials/',
sub: '/path/to/this/thing/sub/',
};
module.constant('THINGS_ROOT', module.paths.root);
module.constant('THINGS_PARTIALS', module.paths.partials);
module.constant('THINGS_SUB', module.paths.sub);
module.config(function(stateHelperProvider, THINGS_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things',
url: '/things',
templateUrl: THINGS_PARTIALS + 'things.html',
});
});
And then any sub modules or "relative" modules look like this:
var module = angular.module('things.someThing', ['myApp.things']);
var parent = angular.module('myApp.things');
module.paths = {
root: parent.paths.sub + '/someThing/',
sub: parent.paths.sub + '/someThing/sub/',
partials: parent.paths.sub + '/someThing/module/partials/',
};
module.constant('SOMETHING_ROOT', module.paths.root);
module.constant('SOMETHING_PARTIALS', module.paths.partials);
module.constant('SOMETHING_SUB', module.paths.sub);
module.config(function(stateHelperProvider, SOMETHING_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things.someThing',
url: "/someThing",
templateUrl: SOMETHING_PARTIALS + 'someThing.html',
});
});
Hope this helps!
Full Article: Relative AngularJS Modules
Cheers!
I think you'll eventually find that maintaining the paths relative to the js file will be harder, if even possible. When it comes time to ship, you are most likely going to want to concatenate all of your javascript files to one file, in which case you are going to want the templates to be relative to the baseUrl. Also, if you are fetching the templates via ajax, which Angular does by default unless you pre-package them in the $templateCache, you are definitely going to want them relative to the baseUrl, so the server knows where to find them once your js file has already been sent to the browser.
Perhaps the reason that you don't like having them relative to the baseUrl in development is because you aren't running a server locally? If that's the case, I would change that. It will make your life much easier, especially if you are going to work with routes. I would check out Grunt, it has a very simple server that you can run locally to mimic a production setup called Grunt Connect. You could also checkout a project like Yeoman, which provides a pre-packaged front end development environment using Grunt, so you don't have to spend a lot of time getting setup. The Angular Seed project is a good example of a Grunt setup for local development as well.
I've been chewing on this issue for a while now. I use gulp to package up my templates for production, but I was struggling to find a solution that I was happy with for development.
This is where I ended up. The snippet below allows any url to be rewired as you see fit.
angular.module('rm').config(function($httpProvider) {
//this map can be defined however you like
//one option is to loop through script tags and create it automatically
var templateMap = {
"relative.tpl.html":"/full/path/to/relative.tpl.html",
etc...
};
//register an http interceptor to transform your template urls
$httpProvider.interceptors.push(function () {
return {
'request': function (config) {
var url = config.url;
config.url = templateMap[url] || url;
return config;
}
};
});
});
Currenly it is possible to do what you want using systemJs modules loader.
import usersTemplate from './users.tpl';
var directive = {
templateUrl: usersTemplate.name
}
You can check good example here https://github.com/swimlane-contrib/angular1-systemjs-seed
I had been using templateUrl: './templateFile.tpl.html but updated something and it broke. So I threw this in there.
I've been using this in my Gruntfile.js html2js object:
html2js: {
/**
* These are the templates from `src/app`.
*/
app: {
options: {
base: '<%= conf.app %>',
rename: function( templateName ) {
return templateName.substr( templateName.lastIndexOf('/') );
}
},
src: [ '<%= conf.app %>/**/{,*/}<%= conf.templatePattern %>' ],
dest: '.tmp/templates/templates-app.js'
}
}
I know this can lead to conflicts, but that is a smaller problem to me than having to edit /path/to/modules/widgets/wigdetName/widgetTemplate.tpl.html in every file, every time, I include it in another project.
I have an AngularJS application and in the future, some developers in other teams will develop modules that will be installed as parts of it. So I defined the folder structure as below.
www/
index.html
app.js
modules/
modulesA/ -- will be copied when module A was installed
moduleA.js
moduleA.css
moduleA.partial.html
modulesB/ -- will be copied when module B was installed
moduleB.js
moduleB.css
moduleB.partial.html
Now I have a problem. When user installed module A, how to let AngularJS (and the application) load JS and CSS under its folder? Is there any library can load JS and CSS by folder so that I can put the code in index.html likes
<script src="/modules/**/*.js"></script>
<link src="/modules/**/*.css"/>
Otherwise, I have to add some placesholders in index.html and change the content when user installed a module, something like
<script src="/app.js"></script>
<!-- $$_JS_$$ -->
<link src="/app.css"/>
<!-- $$_CSS_$$ -->
AngularJS doesn't support what you want, but you could take a look at build tools such as Grunt or Gulp that let you "build" your application for you. In your case, these tools can look for CSS files and concatenate them into one single file. This way your index.html does not have to change if you ever add new modules.
GruntJS: http://gruntjs.com/
GulpJS: http://gulpjs.com/
Personally I use GulpJS, since it seems to be much faster & I found it easier to configure:
Included my configuration file below.
For example, the task "styles" will compile every css file it finds in the folders I specified, concatenate them, and drop them in the distribution folder.
Since there is an initial learning curve on how to use these tools, you can always integrate gulp or grunt at your own pace. For now you could let it build your css files & later expand it by concatenating JS as well and do various other tasks. In my opinion, its worth learning as it saves you so much time & effort.
var gulp = require("gulp");
var concat = require("gulp-concat");
var html2js = require("gulp-ng-html2js");
var sass = require("gulp-sass");
var clean = require("gulp-clean");
var streamqueue = require("streamqueue");
var ngDepOrder = require("gulp-ng-deporder");
var paths = {
"dist": "../server/staffing/static/",
"vendor": ['vendor/underscore/underscore.js',
'vendor/angular/angular.min.js',
'vendor/angular-route/angular-route.min.js',
'vendor/restangular/dist/restangular.min.js',
'vendor/angular-animate/angular-animate.min.js',
'vendor/angular-bootstrap/ui-bootstrap-0.7.0.min.js',
'vendor/angular-bootstrap/ui-bootstrap-tpls-0.7.0.min.js',
'vendor/angular-ui-router/release/angular-ui-router.min.js',
'vendor/angular-bootstrap-colorpicker/js/bootstrap-colorpicker-module.js',
'vendor/momentjs/min/moment.min.js'],
"scripts": ['app/**/*.js'],
"fonts": ['app-data/fonts/*.*'],
"templates": ['app/**/*.html'],
"styles": ['app/**/*.scss','vendor/angular-bootstrap-colorpicker/css/*.css']
}
gulp.task("watch", function () {
gulp.watch('app/**/*.js', ['scripts']);
gulp.watch('app/**/*.html', ['scripts'])
gulp.watch('app/**/*.scss', ['styles']);
})
gulp.task("default", ["clean"], function () {
gulp.start("scripts", "vendor", "styles", "fonts");
})
gulp.task("clean", function () {
return gulp.src(paths.dist, {read: false})
.pipe(clean({force: true}));
})
gulp.task("vendor", function () {
gulp.src(paths.vendor)
.pipe(concat("vendor.js"))
.pipe(gulp.dest(paths.dist + "js/"));
});
gulp.task("scripts", function () {
var stream = streamqueue({objectMode: true});
stream.queue(gulp.src(paths.scripts)
.pipe(ngDepOrder()));
stream.queue(gulp.src(paths.templates)
.pipe(html2js({moduleName: "templates"})));
return stream.done()
.pipe(concat("app.js"))
.pipe(gulp.dest(paths.dist + "js/"))
});
gulp.task("styles", function () {
gulp.src(paths.styles)
.pipe(sass())
.pipe(concat("staffing.css"))
.pipe(gulp.dest(paths.dist + "css/"))
})
gulp.task("fonts", function () {
gulp.src(paths.fonts).
pipe(gulp.dest(paths.dist + "fonts/"))
})
Check out the angular generator for Slush, it does what I think you want using gulp-bower-files and gulp-inject. You specify your app dependencies using bower, and these are collected and injected by gulp using gulp-inject, which then injects in your index.html the proper link/src/style tags that look very much like your own examples above. Modules' JS and CSS is also collected, minimized, concatenated and injected as well. It also compiles partials and injects those into $templateCache.
I have used it to automatically include dependencies from sub-folder modules/views using a project layout similar to yours.
Note that all your vendor dependencies will need to be bower packages that specify their dist files using the 'main' attribute in bower.json. Some packages do not do this properly, but it's easy to fork the package and add them yourself then point bower at your updated repo.
I'm using the Sencha Command Line 3 tools with a newly generated Sencha Touch 2 application.
Assuming my app.js file looks like this:
Ext.application({
name: "CA",
event_code: "test123",
launch: function() {
console.log("application launched!");
}
});
My views and object stores depend on generating a URL based on CA.app.event_code equaling "test123";
During development in the browser, everything works fine, CA.app returns the variables I need.
When I compile my application with sencha app build and try to run the minified version in the browser, I get an error like this:
Error evaluating http://localhost:8888/app.js with message: TypeError: Cannot read property 'event_code' of undefined localhost:11
I'm not entirely sure why this is happening or how I can fix it. I am open to any and all ideas or suggestions, any pointers in the right direction will be greatly appreciated.
Ran into the exact same issue. You have no access to the namespaced app within the views... really sucks that they let you in development and not when built. Anyway, I got around it by adding a static helper class and using that all over my app:
In /app/util/Helper.js:
Ext.define('MyApp.util.Helper', {
singleton: true,
alternateClassName: 'Helper',
config: {
foo: "bar",
bat: "baz"
},
staticFunction: function() {
// whatever you need to do...
}
});
Then in your view or controller:
Ext.define('MyApp.view.SomeView', {
...
requires: ['Events.util.Helper'],
...
someViewFunction: function() {
var someValue = Helper.staticFunction();
// and you can use Helper.foo or Helper.bat in here
}
});
For reference, here's some documentation on Sencha Singletons. And one important note: make sure that your Helper singleton is in it's own file! If it's small, you may be inclined to put it at the bottom of your app.js, and things will work at first, and the build process will work, but the code will not. Don't worry, the build process puts all of your JS code in one big, compressed file anyway.
I used the command sencha create jsb -a http://myserver/myapp/app.html -p apps.jsb3 -v
This created the file apps.jsb3, and when i opened this file, it contained all the definitions for Models and Stores (but, no controllers).
Then i followed with the command ;
sencha build -p apps.jsb3 -v -d . and this created 2 files. app.all.js which is a compressed form of the model, store and the app.js (and it also includes the definition of the controllers of the application as found in the app.js)
ex : ....controllers:["Person","Pers... likewise
and then all-classes.js has all the models, and stores (and again no controllers)
Then i included the following to my app.html file
<script type="text/javascript" src="extjs/ext.js"></script>
<script type="text/javascript" src="app-all.js"></script>
Finally, i copied the app-all.js, all-classes.js, app.html to another folder in the server. and this folder is called SERVERFOLDER2.
Now, when i type http://myserver/SERVERFOLDER2/app.html it says that "NetworkError: 404 Not Found - http://myserver/SERVERFOLDER2/app/controller/Person.js?_dc=1347636548640".
Why is this, and how can i resolve this ?
UPDATE APP.JS
Ext.Loader.setConfig({
enabled: true
});
Ext.require('Ext.container.Viewport' );
Ext.application({
requires: [
'MyApp.Names',
'Ext.window.MessageBox'
],
models: [
'PersonModel',
'SchoolModel'
],
stores: [
'PersonStore',
'SchoolStore' ,
'GenderStore
],
views: [
'UserPanel',
'SchoolViewPanel',
'UpdateSchoolWindow'
],
controllers: [
'SchoolController',
'PersonController',
'UserActionController',
],
name: 'MyApp',
refs:
[{
ref: 'viewport',
selector: 'viewport'
}],
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'card',
items: [
{
xtype: 'panel',
items: {
xtype: 'UserPanel'
}
}
]
});
},
userSuccess: function() {
var st = Ext.getStore('PersonStore');
st.on('load', this.onSuccess, this, {
single: true,
delay: 100
});
st.load();
},
onSuccess: function() {
this.getViewport().getLayout().setActiveItem(1);
}
});
The process you describe looks good to me.
The problem you should focus on is why the generated jsb3 file does't include the controller classes. All the steps that follow seem to work correctly, but the application does not fire up in the end because the packaging process does not include your controller class files.
Ext.Loader kicks in and tries to dynamically ('on demand') load the controller class file - the goal here is not to fix the path configuration for Ext.Loader to find the controller class, but rather to make sure the controller classes are included in the jsb3 file from step 1 in the first place.
I assume that your application starts up fine when launching it via http://myserver/myapp/app.html in the non-packaged state (with only ext-dev.js and no ext-all.js, app.all.js or whatsoever).
I further assume that you can observe messages on the console that say something along the lines of:
Synchronously loading 'app.controller.Person'. Consider adding Ext.require() above Ext.onReady
This means that your class dependencies are not configured correctly. The controller class is not picked up as dependency and therefore not pre-loaded before your application starts.
The packaging process (your first step) relies on the dependencies to generate the jsb3 file. It will not pick up dependencies loaded synchronously on demand.
Solution
The goal is to remove any synchronous on-demand loading when you start your application. If you are successful, the packaging process should be fixed as well.
As a quick workaround you could simply Ext.require your controller classes above your Ext.onReady or Ext.application call.
However, controller classes should be automatically added as dependency by the framework. If they are not, then either your configuration is faulty or there it is a bug in ExtJs.