I have an AngularJS 1.7 project currently being built with Gulp, and I'm looking into switching it over to Webpack. After some initial poking around, I'm finding it difficult to find and load all the source files, due to the fact that it's written in a style from before CommonJS and ES6 modules. My hunch is that it's a common (but old) style of Angular, but I'm new to it so I'm not sure.
The Question: Is there a Webpack plugin or tool or strategy for building Angular 1 projects? I'm hoping there's a plugin that is smart enough to pull in all the template files, controllers, directives, services, and so forth without needing to do a lot of configuration.
Specifically, I'm looking for "Use this 1 tool that's exactly what you need", as opposed to "Follow these 20 steps". It's not that I'm lazy, just that I'm confident I can figure out all the required steps, but before I go that route I want to make sure I'm not missing some plugin that does it all for me. And, I don't want to waste anyone else's time writing up the steps.
Reference Info
File Structure
- src
- components
- hometile
-- hometile.css
-- hometile.drct.js
-- hometile.tpl.html
- services
- capabilities.fct.js
Note the way the templates are put in the same directory as the component controller.
Here's a snippet from the routes file showing how a template is referenced:
app.routes.js
$routeProvider.
when('/home', {
templateUrl: 'sections/home/home.tpl.html',
controller: 'HomeController as home',
resolve: {
commonData: function(MyService) {
return MyService.getCommonData();
}
}
})
Note the use of templateUrl and it's a string path. The actual file is a template, and as far as I can tell, the existing Gulp builder uses a glob to find all the .tpl templates and then sends them to nghtml2js to combine into a single .js
I'm hoping there's something similar that's been written for Webpack that takes a standard Angular 1 project structure and builds all the required outputs.
I'm not sure it's really the full answer, but the import-glob npm package made a big difference. I was able to create a single import.js that then imports everything I need.
import.js
import './services/**/*.js';
import './directives/**/*.js';
import './controllers/**/*.js';
I'm also updating it a little with regards to importing the templates:
import template from './sections/home/home.tpl.html';
$routeProvider.
when('/home', {
templateUrl: template,
controller: 'HomeController as home',
resolve: {
commonData: function(MyService) {
return MyService.getCommonData();
}
}
})
By importing everything this way, Webpack is able to find all the source files.
Related
The main use case I've seen for using webpack on css is with Angular2 or React where you have components that you can require the stylesheets onto. However, I'm using AngularJS so I don't know where I should logically be importing these stylesheets, does anybody know the best practice for this?
Concretely, given:
a stylesheet "loginBox.css"
a template "login.html"
a controller LoginController in "login.controller.js"
and
a route configuration
routeProvider.when('/login', {
controller: 'LoginController',
controllerAs: 'vm',
templateUrl: '/static/templates/authentication/login.html'
})$
How do I get "loginBox.css" to apply to "login.html" and only "login.html"?
To apply css to only one component you have to use shadow dom, however angularjs 1.5.x does not use shadow dom. But from your specification you have 2 options:
1. Load css file in router or via custom directive.
As suggested how-to-include-view-partial-specific-styling-in-angularjs you caninclude your custom css files using either custom directive or by using angular-css module (mode info about this module). I'd suggest to use the second option (angular-css module). Another example how to use angular-css module. However you will have to use specific styles to apply only to /static/templates/authentication/login.html file.
2. Load css files using module local scope.
Another way is to break you application to specific ES6 modules (don't get this mixed up with angular's modules) and include them in main ES6 module. To understand ES6 modules follow this link. And then you can use local scope with css-loader css-loader#local-scope. This option might be harder to make it work, because you will have to change your build process and split application to seperate modules, but you will get option to apply stylesheets to only one module.
To be clear the best sollution might be the first one, no need to change your build process and you can just add one NG module and you are good to go. No need to talk about that you can then load CSS files to your directives or components. But as you stated you need to apply your changes only to one particular .html file, so going for second option might more suit your needs.
When building less modular code requiring stylesheets in your entry file makes sense
After reading hundreds of lines about browserify vs webpack and several how to of both I decided to go for webpack. The main reason it's because I liked the idea of bundling everything into a js file.
I have an angular project already working and I want to refactor it for webpack. The problem? My project is using angular 1.4.7, ng-animate and plain javascript (ES5) and all the tutorials and manuals are for ES6. I don't want to refactor my project so much. What's the way to go? I would like an example of each angular module : factory, directive, controller and so on. Many thanks
I typically have a feature.module.js file which has my module definition and requires all of the directives / services contained within the module. Also has the external dependancies.
/* module.js */
angular.module('my.module',['dependancy1', 'dependancy2']);
//External libraries
require('./dependancy1.module.js');
require('./dependancy2.module.js');
//Internal components
require('./thing.directive');
require('./thing.service';
/* service.js */
angular.module('my.module')
.directive('yourDir', function myDir(){...});
I'm dealing with the same problem now. And I found something that works (work in progress, but at least I can see progress). My steps:
Install yeoman
Run this angular-webpack generator. Select 'ES5' when asked (the other option is 'ES2015', which I guess is the same that 'ES6')
Start modifying the automatically generated boilerplate with your Angular code
Yes, you still need to learn about gulp and sass, but at least you can run a simple AngularJS app using the old ES5 syntax, and start modifying it.
I'm probably blogging about this. So, I'll update this answer then.
I tend to do this:
app.js:
require('/any/angular/deps');
var subModule = require('/some/sub/module');
var app = angular.module('myApp', []);
// pass the app module in sub modules to allow them to define their own config
subModule.configure(app);
/subModule/module.js:
var someSubDirective = require('./subDir/directive');
export function configure(app) {
someSubDirective.configure(app);
}
/subModule/subDir/directive.js:
export function configure(app) {
app.directive('myDir', myDir);
}
function myDir() {
}
My idea is to let all sub modules handle their own configuration, so declaring config or constant, factories or providers. Letting this then bubble up to the app.js. This means its really easy to delete a folder from your structure, because it is one line removal from it's parent module.
This also makes relevant file paths a lot shorter and easier to handle.
We are developing our Mobile part of our meteor app as a package. Since we have mobile and desktop apps, we have a common core package, and the desktop and mobile packages are separate.
So we pretty much have an Angular/Ionic/Cordova/Meteor app inside a package.
The problem we are facing is how do I specify a path to the template. Normally when developing an ionic mobile app normally outside a package it works like
state('manage.prefs', {
url: '/prefs',
templateUrl: 'client/views/manage-prefs.html',
controller: 'ManagePrefsCtrl'
})
You see templateUrl? That is the path to the .html template that angular uses. Now normally if this was not developed inside a package the route would be relative to root meteor directory.
Since we are developing this as part of a package, the path is not working. We are using api.addFiles to add each template. But how do we get a path to the template? We have tried various paths but none seem to work. Is it possible to get a path to a template inside a package?
We tried api.addAsset which does provide a path but hot reload breaks and it caches all the files so you have to hard reset to make changes appear.
Short answer:
templateUrl: '/packages/package-name/path-inside-package/manage-prefs.html',
Long answer:
There is now a new tutorial step for that:
http://angular-meteor.com/tutorials/angular1/mobile-support-and-packages-isolation
My particular question is with Foundation for Apps. Webpack has been great so far if I'm working with CommonJS code or within the context of the application that I'm writing, but I'm struggling to bring in Foundation for Apps, which contains its own HTML templates, SVG icons, JavaScript, and SCSS.
I've included FfA's JavaScript by requiring the concatenated source (which isn't ideal but it works). I can also include SCSS in a more clean way since Webpack's sass loader seems to know what to do with #import statements.
The key problems arise when FfA's source (Angular) requests HTML from a path that doesn't exist post-Webpack.
For example, a request will be made to get /components/modal/modal.html but the only thing in my public directory is bundle.js. How can I load all of the HTML templates and replace any occurrences of templateUrl paths in FfA's source with require statements that resolve to the path of the loaded HTML? Am I going down the right path or is there a better approach?
F4A is using the built-in Front Router plugin which simplifies the creation of routes in AngularJS by allowing you to define them directly in your view templates. As shown here it is meant to be used in the most basic sense and for rapid prototyping. For example, even if it implements Angular's UI-Router and its states, you can't use their resolve property.
I'm not familiar with Webpack and at which level it generates the required templates but if you can't generate them as Front Router's expected markdowns before being generated by gulp, then you may disable the plugin and implement your custom $stateProvider instance by leaving F4A out of it and solve it within AngularJs :
.state('yourstate', {
url: '/yoururl',
templateUrl: 'templates/yourTemplate.html',
//
// or maybe something like :
// -- this is guessing. i'm not familiar with webpack :) --
//
// templateUrl: function (wabpack) {
// return '/partials/' + wabpack.path + '.html';
// }
//
controller: 'YourController',
resolve: {
// you may also use resolve here for extra logic before
// loading your template
}
About how to disable F4A Front Rooter and how to implement your own $stateProvider instance you may check this :
Foundation for Apps : How to replace the default Dynamic Routing plugin?
I am using Angular UI router. I have many template calls like this:
var access = {
name: 'access',
templateUrl: 'app/access/partials/a1.html',
};
The page served by Access depends on the :content.
Is there a way that I could combine all these HTML files into one and pre-load the files so every action didn't involve another request? I realized I can put my HTML directly inside the template but can I combine the HTML from multiple templates into one file and have it all pre-loaded so it's ready when needed?
Is there a way that I could combine all these HTML files into one and pre-load the files so every action didn't involve another request?
Yes, by using html2js;
or, to be exact, a grunt/gulp plugin called, respectively, grunt-html2js / gulp-html2js
HOW IT WORKS
According to this grunt-html2js tutorial:
" html2js is a plugin (...) that parses all template files in your application and creates a javascript file that populates the $templateCache.
This is very useful trick to reduce the number of request your app needs to make to start the application.
It can also minify the html snippets saving some bandwitdh as well."
----------
According to the grunt-html2s Github repository:
"html2js converts a group of templates to JavaScript and assembles them into an Angular module that primes the cache directly when the module is loaded.
You can concatenate this module with your main application code so that Angular does not need to make any additional server requests to initialize the application."
----------
And according to me :)
What I like about html2js is that you don't need to change any of your angular code, but just configure gruntfile.js / gulpfile.js.
Your templateUrl files will then automagically be available in $templateCache.
So it does exactly what you wished for:
Combine all your templates into a module;
Write the JavaScript source for the module;
All you need to do is to specify the module as a dependency in your code.
Once the module is loaded, all your templates are available in the cache: no request needed!
EXAMPLES OF GRUNT / GULP CONFIG
grunt.initConfig({
html2js: {
options: {
base: 'app',
module: 'templates',
htmlmin: {
... many available options: see GitHub repo ...
}
},
main: {
src: ['app/**/*.html'],
dest: 'templates.js'
},
},
})
then just use the templates module as a dependency:
angular.module('myApp', [ 'templates']);
----------
Here is an example from the gulp-html2js GitHub repository:
gulp.task('scripts', function() {
gulp.src('fixtures/*.html')
.pipe(html2js({
outputModuleName: 'template-test',
useStrict: true
}))
.pipe(concat('template.js'))
.pipe(gulp.dest('./'))
})
TESTING
What's more, html2js is also a very good tool to test directives that use the templateUrl property.
FOR MORE INFORMATION
I came accross html2js after reading Joel Hooks' (Egghead instructor) book about automation, which I strongly recommend.
Watch a dedicated video about html2js in the pro section of the EggHead website.
Testing directives using the templateUrl property with Karma and karma-ng-html2js-preprocessor
NOTE
Technically, you could just use ng-html2js, without using Gulp or Grub; you could then use a command line like the following:
ng-html2js inputFile [outputFile] [-m moduleName] [--module-var ngModule]
However, as you would need to run the above command for each templateUrl file, Gulp/Grub automation seems to be a more efficient way to go.
DISCLAIMER
I have no particular interest or shares in the book/websites/tools I mentioned.
Note: I should also add, you also add this file to your html page:
<script src="path/to/templates.js"></script>
Why not use ng-include in your main html file to load all html's at once.
Something like this...
<div ng-repeat="template in templates">
<ng-include src="template.url"></ng-include>
</div>
So here templates is an array of objects which contains all the url's the other templates which you want to load at once in your main.html file.
Easy. Create them all as ng-template blocks:
<script type="text/ng-template" id="app/access/partials/a1.html">
Content of the template.
</script>
There is already a grunt-angular-templates grunt plugin specific to this angular task only. i have been using it for a while now. this plugin automatically caches your HTML templates using $templateCache in one file which you can add in your application.
grunt-angular-templates - https://www.npmjs.com/package/grunt-angular-templates
Use,
yourApp.run(['$templateCache', function($templateCache) {
$templateCache.removeAll();
}])
Your problem pointed in this site too,
http://javahow.net/questions/27321315/using-angulars-ui-router-how-can-we-make-sure-the-new-version-of-the-html-part