I'm writing an Angular directive that needs a template. Loading a template in Angular is done by either embedding the template itself in the source code with <script type="text/ng-template"> or by providing a url in the directive's configuration, like templateUrl: "/path/to/template.html".
The first way is completely unacceptable for a 3rd party directive, because people won't npm install my directive and then do include("node_modules/mydirective/template.html")(or equivalent), it just feels dirty.
The second way won't work for the same reason. The template would be located inside the node_modules directory, which is not publicly accesible after packing/deploying a website.
My idea is to allow the developer to just include the template the same way the directive itself is included:
<script type="text/javascript" src="/path/to/directive.js"></script>
<script type="text/ng-template" src="/path/to/directive.html"></script>
The problem is that the second line won't work (browser will complain about unexpected characters, I assume because it still tries to parse the fetched content as JS.
I found that using ng-include instead of script src actually fetches the template. The problem with that is that by the time the template is fetched, my directive has already ran the link function and crashed (because it couldn't find the template).
Is there a way I can either delay my directive until the template is fetched or provide any sane way of loading the template?
What about inline templating with just template, instead of templateUrl? If its a library, I feel it's the simplest way to bundle the directive and template for distribution
One way to delay evaluation of your directive is to write a wrapping directive that does the request for the template, then $compile and append the initial directive to the page in the request callback. Still, not so pretty.
Related
I'm trying to minimize js/css/html footrpint for the user and to load only the files really needed. I've utilized RequireJS for that.
For my templates I'm trying to implement someting similar to
using section in C# or ///< reference path='...' > in TypeScript
But somehow depending on my template content it does or doesn't instantiate depends-on directive depending on template I have:
Works:
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
Doesn't work:
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
<other-directive></other-directive>
Doesn't work:
<div>
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
</div>
I'm obviously missing the way Angular parses and processes templates.
Is there a way to achieve what I'm trying to do?
OK, the problem was that it didn't wait until all directive template depends on are loaded. To ensure dependencies are loaded, dependent code should be in callback passed to require function.
So what am I doing wrong? I included the ng-include and tried every variation and it is not including the file(it keeps returning a 404 in the console), the directory location is as follows:
-App
--Layout
---Partials
----Navigation.tpl.html(File)
--Layout.tpl.html(File)
And the ng-include is located in the layout.tpl.html file:
<div data-ng-include="'layout/partials/Navigation.tpl.html'"></div>
Please note that I am using webpack for this project(that shouldn't matter). I am calling the layout.tpl.html file as the base layout, and the partials are included inside the layout.tpl.html file. Also, I am using vs having a ng-app on the DOM:
angular.element(document).ready(() => {
angular.bootstrap(document, ["app"]);
});
I have worked with angular in the past, and I am at a loss when such a simple task is taking so long time. Also note, when I use the
$templateCache.put('..','..')
and put in html minified string from the navigation.tpl.html with the same directory, it works just fine (but if I use $templateCache.get() or require() from the template location, it doesn't work), but the HTML string is pulling from the cache and I want to be able to update one file vs having to use minified code.
Sorry in advance if I missed something, I am in a rush to get this done, and it should be the simplest thing that is just not working.
Take a look at https://github.com/WearyMonkey/ngtemplate-loader
You should preload your template with the correct key in the $templateCache by requiring it in your bundle.
require('ngtemplate?module=[xx]&relativeTo=/layout/partials/!./layout/partials/Navigation.tpl.html');
That way you can ask for 'Navigation.tpl.html' in ng-include or with templateUrl
I need to represent an image in time when it's needed without a spinner once controller is initialized.
I've found that Angular can cache templates by using $templateCache.
Can I use it in order to preload images when template has <img> tags and another question can it be used for DOM elements which were styled by CSS e.g.:
<div style="background-image:url(name.jpg)"></div>
Thx in advance.
Unless you render it directly through Angular, then, no. Angular only uses $templateCache for directive templates by default. If you wanted to do that you'd need to write your own directive that would look at $templateCache for those assets. That would involve writing your own img directly.
As for CSS, that's a definite no unless you parsed the CSS at runtime with Angular.
Angular does not intercept HTTP calls from outside of its framework.
We have a product that is a widget people load onto their site, which consists of a single JS file that also needs angular to run, so angular is bundled into the JS file.
However, if a site already is using and loading angular themselves, when they load our widget they get an error which kills everything with the following:
WARNING: Tried to load angular more than once
Which makes complete sense since angular was indeed loaded more than once.
What we'd like to do is either of the following:
In our script, rename / namespace angular so it does't conflict with
the host sites already loaded angular, or
Detect if angular is
already loaded, and if so don't load angular ourselves.
To show examples of our code would be difficult since it's spread over about 20 files etc, however it's based off the following angular seed project which uses requirejs to load everything, then we're compiling to a single file: https://github.com/tnajdek/angular-requirejs-seed
Would really appreciate any feedback / tips / solutions
NB This is not a duplicate of any "check if angular loaded correctly" type questions, angular is packaged inside our widget js, the issue comes when angular is also already loaded by the parent page. We need a way to rename angular inside our package.
I'd advise taking a look at this answer, it has to do with a chrome extension running in the same circumstance. The idea here is to separate your loading of angular from the website's, and it assumes that your widget will be loaded after the main content of the page has been loaded.
If you are loading in html content with an ng-app directive or ng-controller, wrap your html content in a container with ng-non-bindable as an attribute.
Angular looks immediately for an element with the ng-app attribute when you load in angular.js. If two ng-apps are present i.e., on your site, and the widget, it will cause errors. Defer the parsing with: window.name = "NG_DEFER_BOOTSTRAP!" + window.name; Then load in your script.
Once your script has loaded, set window.name to '' or whatever it was before.
Individually bootstrap (the term for angular finding an ng-app attribute) your html content using:
var appRoot = document.querySelector('#id');
angular.bootstrap(appRoot, ['angularModuleName']);
And that should do it... Caveat, I have no idea how this would work if your widget Angular is on a different version than the client website, and I've only made it work with extensions, which are a little bit different because they live in their own isolated 'worlds'.
That being said, I feel like this should get people going in the right direction when dealing with this problem.
Is there a way to prevent Angular from creating "helper" HTML comments? For example,
<div ng-include="myTemplate"></div>
Will transform into something like
<!-- ngInclude: 'hurr-durr.html' -->
<div ng-include="myTemplate"></div>
How do I stop this? I've looked into the Angular source, and I've seen these "helpers" are generated by an unconditional document.createComment inside almost every directive, so I guess there's no way to stop them all at once by using a config setting on a provider or something.
But maybe there is some custom Angular build without "helpers"?
I suppose I could write some Yeoman/Grunt task to remove/comment the .createComment-s from Angular's source whenever I scaffold a new project. Or maybe you guys know of a fiddle that already does that? And also, this raises my last question:
Are those comments somehow crucial to the Angular's normal functioning? And if I remove them, will it cause some kind of instability in my app? Should a just rewrite the CSS and "deal with it"?
The comments are crucial to how Angular handles certain elements. Removing them is not currently an option. What issues are you having with it?
You are able to remove the contents of these angular comments, as well as some of the classes angular attaches to elements (e.g ng-scope) by adding this config to your angular module:
myApp.config(['$compileProvider', function ($compileProvider)
{
$compileProvider.debugInfoEnabled(false);
}]);
According to the angular.js docs, it is actually good to do this in production and should result in a performance boost.
From Angular Doc:
Disabling Debug Data By default AngularJS attaches
information about binding and scopes to DOM nodes, and adds CSS
classes to data-bound elements:
As a result of ngBind, ngBindHtml or {{...}} interpolations, binding
data and CSS class ng-binding are attached to the corresponding
element.
Where the compiler has created a new scope, the scope and either
ng-scope or ng-isolated-scope CSS class are attached to the
corresponding element. These scope references can then be accessed via
element.scope() and element.isolateScope().
Tools like Protractor and Batarang need this information to run, but
you can disable this in production for a significant performance boost
with:
myApp.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
If you wish to debug an application with this information then you
should open up a debug console in the browser then call this method
directly in this console:
angular.reloadWithDebugInfo();
The page should reload and the debug information should now be
available.
For more see the docs pages on $compileProvider and
angular.reloadWithDebugInfo.