I've only been working with angular for 2 weeks so fairly new to the framework.
I'm making an app that shows data through charts, and i want the data to be viewable in different chart types. The idea is that you can click a button and swap the chart type.
The way i've been doing this is by rendering the chart through a directive using templateURL. Unfortunatly i've been unable to make the templateURL variable. I've tried different things and this is how it looks atm:
main.html:
<chart charttype="{{chartType}}"></chart>
directive:
.directive("chart", function () {
return {
restrict: 'E',
templateUrl: function (element, attrs) {
return 'views/partials/' + attrs.charttype + '.html';
}
}
Controller:
$scope.chartType = "lineChart";
$scope.ChangeChart = function (chartType) {
$scope.chartType = chartType;
}
The code is supposed to render 3 different html files (lineChart.html, barChart.html & pieChart.html). however the {{chartType}} is simply parsed as a string
It works when i use:
<chart charttype="lineChart"></chart>
For whatever reason i can't get the charttype attribute to become variable.
I also realize this might be more of a rails way of fixing an angular problem (i'm used to rendering partials). So maybe this is not the way to do this in angular. I've thought about other ways to do this like hide/show the charts but this just seems ugly to me. I'm up for any suggestions though.
update 1:
so i'm trying to render it via ng-include in all the ways i can think of but it keeps giving me errors or doesn't show anything at all.
i've also tried putting the ng-include directly into my html file
<div ng-include="views/partials/lineChart.html"></div>
However in my browser i see it just comments this line out.
update 2:
I couldn't get ng-include to work with a variable template. So i've decided to solve the problem by removing the templates and using ng-hide in my html file instead.
Once the template url is set, it is not called again. Use ng-include with variable template
Something like
template: "<div ng-include=\"'views/partials/' + charttype + '.html'\"></div>"
Directive templateUrl parameter can't get variable value as argument, just static text. if you want, i can show solution with ng-include directive in directive template parameter.
I'm trying to nested some directives on the examples looks like I got everything I need but not work as I expect, but if I put the nested directive out of the parent directive it is recognized
here is a plunker:http://plnkr.co/edit/eZaYTHm274zWx8GPMAS0?p=preview
the main purpose of this nested directives is to share the data between controllers and build a set of fields dynamically.
Thanks in advance!
Resource: https://docs.angularjs.org/guide/directive
You have two problems with your code: the restrict property and the transclude/template properties. See the code excerpt below.
return {
restrict: 'AE', //You need to add "A" if you are using attributes
transclude: true, // You need to include the ng-transclude directive in your template
template: '<div> Some Html <div ng-transclude></div></div>',
...
};
I am just starting out with AngularJS. I decided to go with ng-BoilerPlate as a structure.
I am currently stuck on creating a small custom directive.
What I did was the following :
I created an html file for the directive under src/app/login/sso/facebook-button.html.
This contains just regular code you need for a directive.
<a href="#" class="btn btn-lg btn-block omb_btn-facebook">
<i class="fa fa-facebook visible-xs"></i>
<span class="hidden-xs">Facebook</span>
</a>
In this same directory I also created the javascript file needed for this directive "ssoDirective.js".
app.directive('facebookButton', function() {
return {
restrict : 'E',
templateUrl : 'facebook-button.html'
};
});
Finally I just used the template in a login.tpl.html file (which works no problem without this template).
<facebook-button><facebook-button>
Now when I grunt this code (just the normal grunt config of ng-boilerPlate) I get the following error in Chrome :
GET http://localhost:63342/app/login/sso/facebook-button.html 404 (Not Found)angular.js:8632
Error: [$compile:tpload] Failed to load template: /app/login/sso/facebook-button.html
I understand why this error is arrising. When I look into the build directory the facebook-button.html file just isn't there anywhere. Using grep I can also not find the contents of this file anywhere in this directory.
So grunt clearly skips this file while building.
So the question is. How do you create a very simple directive like this in ng-boilerPlate. Where do you place the .html for a directive so it gets included in the grunt build of ng-boilerPlate, and what templateUrl you specify so it gets found.
The templates in ngbp will be merged into one file during the build process (actually two separate files, templates-app.js for the app-modules and templates-common.js for common reusables). This process done by html2js task configured in Gruntfile.js.
In build.config.js you can specify which files where to merge. By default all files match src/common/**/*.tpl.html pattern goes to templates-common.js and files match src/app/**/*.tpl.html merged into templates-app.js. You can also add your own rules but don't forget to configure Gruntfile.js as well!
If you really want to use explicit template definition (so the template will not be merged into build/templates-app or build/templates-common.js) one solution is to use the template property on your directive and provide the template inline. A better way to achieve the same is to use $templateCache to include template.
Refering to the documentation of $templateCache
First you must configure the run() section of your module as the following:
app.run(function($templateCache) {
$templateCache.put('facebook-button.html', 'Content of facebook-button.html');
});
Now you can use the standard templateUrl way to include facebook-button.html into your directive!
app.directive('facebookButton', function() {
return {
restrict : 'E',
templateUrl : 'facebook-button.html'
};
});
You can get cleaner code if you separate the function and give just reference to run()
(function(){
'use strict'
angular
.module('app',[...])
.run(Template);
.directive('facebookButton',Directive);
function Directive(){
return {
restrict : 'E',
templateUrl : 'facebook-button.html'
};
}
function Template($templateCache) {
$templateCache.put('facebook-button.html',
'...'+
'Content of facebook-button.html'+
'...'
);
}
})();
I've created directive that with certain attrs has different HTML.
Directive looks like this:
<my-dir param='{{first-template}}'> <my-dir param='{{second-template}}'>
This directive is used several times on site with different params.
I can't use templateUrl: function (element, attrs) {} to load different HTML because it is loaded only once - at begginning of site render.
Is there a solution for that or should I construct new directives?
Here's the situation. I have a directive, that depends on a templateUrl.
The directive looks something like this:
angular.module('foo')
.directive('bar', function(){
return {
restrict: 'E',
replace: true,
templateUrl: '/foo/bar.html',
controller: 'fooController',
require: '^ngModel',
scope: {
onSuccess: '&'
}
};
});
This directive is part of one of my applications, and it's important that it stays part of the application. However, I'd also like to use the same directive in other projects, and currently I'm using bower to pull down the repository into my other projects. However, this directive will break because the templateUrl will be incorrect. Bower clones down my entire project, and the actual path of the template, at best, will need to be this in my other project:
/lib/public/foo/bar.html
How do other people manage this?
I've never liked using template strings for templating due to issues with maintainability. I prototype html very quickly so I opt for a grunt task that reads my files and processes them into a single $templateCache js file.
Solution
Grunt Angular Templates
Grunt build task to concatenate & register your AngularJS templates in
the $templateCache
// within my Gruntfile.js
grunt.initConfig({
ngtemplates: {
'angular-my-directives': {
src: 'views/**/*.html', // where my view files are
dest: 'src/templates.js' // single file of $templateCache
}
}
// ...
});
generates something like: ./src/templates.js which preserves my folder structure:
angular.module('angular-my-directives').run(['$templateCache', function($templateCache) {
$templateCache.put('views/directives/my-download.html',
"<form name=\"myDownloadForm\" ng-submit=\"submit()\" novalidate>\n" +
"</form>"
);
}]);
Now in my directive I can simply use templateUrl: 'views/directives/my-download.html' and it will use the $templateCache.
Finally I used grunt-contrib-concat to combine my files for easy loading.
Checkout "Grunt concat + uglify with sourcemaps" (or leave a better link in comments) to learn about how to concat + uglify (aka min) js files into a single "dist" file.
If working on your own custom bower package..
Be sure to commit the concatenated (aka built) files to the package repo so your package consumers can simply include my-concat-package.js or my-concat-package.min.js
Angular directives have templateUrl and template properties. template receives a HTML string. It's not a perfect solution, because you need to put HTML into your JS. But it is a common pattern for library creators to put the HTML string directly on template property, so they can wrap their module into a single file.
You may want make that templateUrl-to-template a build step of you lib.
Take a look on Angular Bootstrap Bower Repo.
One solution to this, and my personally preferred solution to this, is to put the template into $templateCache in a Module.run function. That way you never have to worry about the url referring to the wrong thing--you can give it any arbitrary identifying url you want--and it will never require an http request to fetch that template, to boot.
In your-directive project:
Create all template with xyz.tpl.html file name, and put into your-directive dir with all js code. All templateUrl looks like
templateUrl: '/template/my.tpl.html'
In app project:
Create a gulp/grunt task in your project for copy all *.tpl.html file into /template/ dir from bower_components (default).
e.g.:
// copy template files
gulp.task('copy-tpl', function() {
return gulp.src([
config.dirs.src.bower_components + '/**/*.tpl.html'
])
.pipe(flatten())
.pipe(gulp.dest(config.dirs.build.tpl));
});
Important!
The /template dir is a convention (as js, css, etc).
You can do this with gulp using gulp-angular-templatecache.
You directive would look like so:
angular.module('foo').directive('bar', function($templateCache){
return {
restrict: 'E',
replace: true,
template: '$templateCache.get('bar.html')',
controller: 'fooController',
require: '^ngModel',
scope: {
onSuccess: '&'
}
};
});
And your gulpfile.js would look like this:
var gulp = require('gulp'),
addTemplates = require('gulp-angular-templatecache');
gulp.task('default', function() {
gulp.src('templatesDir/**/*.html')
.pipe(addTemplates('templateFile.js', {module: 'foo' }))
.pipe(gulp.dest('dist'));
});
This produces the file 'templateFile.js' in your dist folder which then can be packaged with your directive file in your bower.json.
I already gave an answer using grunt to build your html files into $templateCache file, but I've since switched to gulp, so anyone looking to follow same process but using gulp, checkout gulp-angular-templatecache