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.
Related
I am using an API get call to fetch the HTML code from database and render the html.
The generated HTML is as below: -
<span (click)="triggerClick()" data-link-id="960" data-link="8" class="level_modal">individuals</span>
triggerClick() function is defined in the ts file but the same is not triggered with dynamically generated html code.
Reason
It's not going to work. The language you use to write Angular templates in is not HTML. It's a custom Angular syntax that purposely looks like HTML to make experience of writing code better. Those templates are then compiled into highly optimized JavaScript functions.
A string you get from your API is received during the run-time of your application, which means that Angular's compiler has already done its job (it has compiled the code which is why you can run your application at all). At this point, Angular's compiler is not available anymore by default. Including the compiler in the final bundle of your app is a performance issue because the compiler code has a large size footprint. This is the reason why AOT compilation (ahead of time) is the default and recommended way of publishing Angular apps for production.
Solution
Your API should not return HTML at all. The whole idea behind single page applications (SPA) is for them to consume and API which is language- and platform-agnostic. It simply works with data, which is today mostly encoded via JSON notation. It's the only language API is supposed to understand.
From what I can gather from the code example you've posted here, you're interested in data of this form:
{ linkId: 960,
link: 8,
text: individuals }
Create an API endpoint which returns data in this form, and then use Angular to loop over the data and create the template which you need.
<span *ngFor="let item of items"
(click)="triggerClick()"
[attr.data-link-id]="item.linkId"
[attr.data-link]="item.link"
class="level_modal">
{{ item.text }}
</span>
Of course, you probably do not need all those data- attributes anyway. This is because Angular application should focus on the model. Model is the source of truth which is used to create the template. If you need additional information about each item (such as link and linkId), just keep in the memory as an object. No need to encode it into HTML just to read it later again.
When you write
(click)="doSomething()"
You want to say
Launch the method doSomething when I click on this
But what you actually say is
Please Angular, compile this code so that when I click on this, it launches doSomething
Because as you may know, you're writing Typescript, not Javascript. So your code will be compiled to be served, thus transforming this into
onclick="doSomething()"
BUT WAIT, THERE'S MORE
When you compile your code, it's actually minified and uglified. So this will give you something along the lines of
onclick="a()"
And thus, making you lose any logic behind this.
So what's the solution ?
The solution is to bind the click event to a global function. There's two ways of doing this :
Function in your component (use it if you want to use logic from your component)
Global function (use it if you want this function to be called in several components)
I'll explain the first use-case, for the second I'll make it short : create a JS file as an asset, put your function in it, and call it on click.
For the second one, it's a bit tricky.
First, inject ngZone into your component : this allows you to handle code outside of Angular
constructor(private zone: NgZone) {}
Then, create a global function (I'll make it Typescript and linting compliant)
ngOnInit() {
this.zone.run(() => {
window['doSomething'] = () => {
console.log('Look mom, I did something !');
};
});
}
After this, delete the function once you leave the component
ngOnDestroy() { delete(window['doSomething']); }
Finally, change your HTML from this
(click)="doSomething()"
To this
onclick="window.doSomething()"
Quick Summary:
I need to allow two script files to handle different operations for the same angular app. One needs to initialize the app, the other needs to assign a templateCache object to a piece of JSON in localStorage.
Context:
I have several python files which compile/generate html and I have constructed an angular app with this emitted html for my site (which uses CGIs).
The basic construct of the site comes pieces of HTML, which fit together like so:
|------------Header---------------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|------------Footer---------------|
My Header creates the <head> tag, instantiates my ng-app and uses $templateCache to set up a template that I call from my Navigation code. I had to go with templateCache instead of ngView and ngRoute due to some limitations with how the CGIs emit the html, and the order in which this happens.
My "Navigation" python/html sets up my app with JS like so:
<script>
var responsiveCatalog = angular.module('responsiveCatalog', ['ngStorage']);
....controllers...
....config, etc....
</script>
This Navigation also includes my default templateCache object:
<div ng-include=" 'responsiveItems.html' "></div>
This is all working to show my first templateCache object in the Content section. However, I need to grab many pieces of information from the python generator for the "Content" section (a totally separate file from the "Navigation"), store this data as JSON in localstorage (hence the inclusion of the ngStorage module), and call that as my second templateCache option.
I am not actually sure that I can use two separate instances of Javascript to reference and influence the same Angular app. I know this seems like bad practice, but I am trying to prevent the need to tear down a huge piece of legacy architecture to influence the angular app from two Javascript files in harmony.
Thanks in advance.
You can do
angular.module('myAppName').controllers.... in different files, just make sure the myAppName the same. Bug I don't feel like it work with config, no time to test. If you need any communication between them, check $emit and $broadcast.
This reminds me of an old song:
"You see I've been reading samples of an app with no name..."
I'd better stop or I'll hear from Neil Young's lawyers.
I am studying AngularJS on W3Schools and in some of their examples the ng-app attribute is an empty string. In others it has a name for the app. It seems that when there is no name there is also no controller function defined in the client script and the application is automatically wired up from the HTML. But in those examples if I enter a name in the ng-app tag it breaks the page. So that raises two questions:
1 - What's the difference between having an ng-app tag with a name and one with an empty string? Does no name mean no controller function is defined?
2 - Is it bad to have an ng-app with no name? Should I always name my ng-app and load it with a controller function?
"...it felt good to be out from the rain."
Edit: Looking at the alternate question "Using ng-app without a value", it is similar, but it doesn't directly address my question. It asks: How to use ng-app without a value. I'm asking for someone to explain: What is the difference between using ng-app with a value and without a value.
Few points here:
Recommend the official docs because the author is the person knows the framework the best.
There are two ways to bootstrap the angular apps: automatically and manually
when you use with ng-app="appName", angularjs will automatically find the element with the tag 'ng-app' and initialize the application.
when you ignore the ng-app, you need to bootstrap the app yourself:
.
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
recommend always bootstrap app with either manual or auto.
===EDIT===
To be clearer :
The stackoverflow link given by #Animal2 has very good explanation. In a short, before angular version 1.3, you can use ng-app as DOM element attribute without any value, because angularjs will handle everything else for you
:
<div ng-app>
....
</div>
but after version 1.3 it is NOT recommended to use this syntax. Instead you can use the 'auto' way and 'manual' way mentioned earlier.
You can specify an AngularJS module to be used as the root module for the application. This module will be loaded into the $injector when the application is bootstrapped. It should contain the application code needed or have dependencies on other modules that will contain the code. See angular.module for more information.
https://docs.angularjs.org/api/ng/directive/ngApp
Someone has already asked this question
Using ng-app without a value
While it isn't exactly 'incorrect' to have an empty ng-app, it is not recommended and won't work for even a slightly complex app. Think of it as a way to show whip up something quickly.
If there is no ng-app specified, it only means that there is no module to assign controllers (and whatever else you want) to, but as you can see it does still work... for the time being.
During the debugging of my angularjs application, I found a lot of parse html events in the dev tools.
The timeline says that this event is invoked by jQuery.extend.buildFragment and it's hard for me to understand, what directive invokes parse html.
How can I detect, what exactly causes parse html events? Probably the reason could be in the ng-repeat, but I'm not sure.
These events slow down $scope.$apply as well.
Every partial html in angular will trigger Parse HTML event, like ng-includes, ng-repeats, directives, and $digest cycles.
Also using jQuery will give you a lot of overhead for initalize jquery instances. jQuery or jQlite buildFragment is called when you or directives or angular call element.html('something tags'), which in turn write to innerHTML which cause parse HTML event in browser and angular walk those children to find more directives and compile those until its complete.
To minimize those, you need to batch process those, but nature of angular will be hard to do directly. May be you can try to use one-time binding syntax :: in angular 1.3+ or make less watchers, so angular won't have to parse html again and again.
We have a legacy framework that we're looking to be able to do some major updates to soon. As part of this, we're working at bringing all our library files up to date. During this process, we upgraded angularjs from 1.0.7 to 1.3.13. Suddenly, we're getting a show-stopping error.
Error: [ng:areq] http://errors.angularjs.org/1.3.13/ng/areq?p0=MainCtrl&p1=not%20a%20function%2C%20got%20undefined
The question is: Why does this error happen when we upgrade to the newest library, and how am I supposed to do this now? I will add the most relevant lines of code below, and I can add more as requested, but it's kind of complex and I'm not sure how to break it down to something easy to stick into here.
The error occurs at angular.boostrap() The relevant code shows like so:
angular.module('app',['DataTools','ClientDataTools']);
angular.bootstrap(document, ['app']);
Main Ctrl is defined as such:
function MainCtrl($scope, $compile) {
The file structure is as such:
load.js
run.js
dataTools.js
load / controllers.js
load / config.js
load / ClientDataTools.js
load / libraries.html
The automatically generated html loads up along with the js code found in load.js. This first thing it does is load up libraries.html, which contains the imports for run.js, dataTools.js, controllers.js, and ClintDataTools.js. The code in load.js then calls a function defined in run.js. This function loads config.js and applies the settings there while going through and using jQuery to add angular tags to the form found in the automatically generated html. It then runs the angular.module() and angular.bootstrap() commands.
controllers.js holds the MainCtrl declaration. config.js is just a json string. dataTools.js and ClientDataTools.js holds the directives used by the angular - this error still happens even when removing the directive files, so I don't think they're part of it, but they are included here out of completeness of the issue..
And no, I can't just change the html in the form in the first place. I don't have access to it. It's automatically generated html to which we wish to add intelligent behavior like preventative data checking. It's an automatically generated form that we'd like to work a little more responsively. Please don't recommend just putting the angularjs markup into the html, and please don't ask why we can't touch the original html.
The way of declaring controller with
function MyCtrl() {}
has been deprecated - try declaring like
angular.module("app").controller("MyCtrl", function() {})