I tried giving two ng-app in an application , when i gave two ng-app like
<body>
<div ng-app="A">
<div ng-controller="AC">
</div>
</div>
<div ng-app="B">
<div ng-controller="BC">
</div>
</div>
</body>
Then second ng-app does not work.
But when i change the scope of first ng-app="A" from div to body then both works fine like
<body ng-app="A">
<div>
<div ng-controller="AC">
</div>
</div>
<div ng-app="B">
<div ng-controller="BC">
</div>
</div>
</body>
Can anyone let me know why this behavior as i am quite new to angular.
I wanted to know why it worked as i didn't called angular.bootstrap on the second one.I tried searching but i didn't got how it is working when changing the scope of ng-app from div to body.
Find the fiddle for the same https://jsfiddle.net/maddyjolly2112/wyfd0djp/1/ and mind copying the js into the same .
Docs say: Don't use ngApp when instantiating multiple angular applications.
The reason you can't do this is laid out in the docs for the ngApp directive.
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead. AngularJS applications cannot be nested within each other.
But Bootstraping multiple Angular apps is possible...
To bootstrap multiple Angular apps, you have to reference each, and they logically can't be nested, or sharing an element; they need to be separate from each other. Because of this, you cannot use the directive, ngApp:
<html>
<head></head>
<body>
<script src="angular.js"></script>
<div id="appElementA"></div>
<div id="appElementB"></div>
<script>
var app = angular.module('A', [])
.controller('AB', function($scope) {
$scope.greeting = 'Welcome!';
});
var appElementA = document.getElementById('divAppA');
angular.bootstrap(appElementA, ['A']);
var bApp = angular.module('B', [])
.controller('BB', function($scope) {
$scope.greeting = 'Welcome to B app!';
});
var appElementB = document.getElementById('divAppB');
angular.bootstrap(appElementB, ['B']);
</script>
</body>
</html>
The above code would be how you'd do it for your apps. You'd then have to be sure you're assigning the controllers to the right angular application (app vs bApp, in the above example.)
But don't nest them!
You claim it 'works' when you nest them, but you should be aware that it doesn't work, it just doesn't crash hard. Don't have multiple angular applications nested. You'll encounter weird issues, especially if you have multiple variables named the same bound to the $rootScope.
But you can nest them without ill effects, right?
If you're intent on having two Angular apps nested; it's possible but extremely version specific and liable to break in weird ways. This Stack Overflow answer talks about it.
From Angular's official docs :
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead. AngularJS applications cannot be nested within each other.
Source : https://docs.angularjs.org/api/ng/directive/ngApp
If the question is : why the second example works and not the first, the answer is in the link above > Angular needs the first ngApp to be placed near the root element of the page (html or body).
As George mentioned, manual bootstrapping will work.
In html, use id instead of ng-app.
In script
var dvFirst = document.getElementById('dvFirst');
var dvSecond = document.getElementById('dvSecond');
angular.element(document).ready(function() {
angular.bootstrap(dvFirst, ['firstApp']);
angular.bootstrap(dvSecond, ['secondApp']);
});
Here is a working plunker
http://plnkr.co/edit/1SdZ4QpPfuHtdBjTKJIu?p=preview
Related
I want to show messages to the end user, just like Google, at the top center of the web panel.
I don't want to include the HTML and related script everywhere in every form and list and chart that I have. I want to centralize this messaging functionality into a service (in Angular JS term) that can be used everywhere.
And just like Google, I want to be able to show rich text in my messages, that is, I want to include links and probably other HTML stuff there. For example instead of showing Customer is defined, I want to show Customer is defined, <a href='#/customer/addPhone'>Now add a phone</a> to guide the user.
What I've done is to place the messages HTML in the root layout of my single paged application:
<div class="appMessages">
<span ng-show="message" ng-click="clearMessage()" ng-bind-html="message"></span>
</div>
and in our controllers, we inject the $rootScope and try to set the message property on it.
Yet I get no results. Can you guide me please?
As a general best practice I would avoid using $rootScope to pass the messages but rather use a dedicated service to update the message,
On your case the problem might be that you need to use angular $sce service to mark your html as trusted.
or load ng-santizemodule instead (which is a seperate module you need to load see offical doc)
That is needed because angular security requires you to explicitly check the html, if the source of your messages are from your code only, and not users inupts you can use the trustAsHtml as you know for sure it a safe html.
On your controller inject $sce, and bind it to your scope, and then use the $sce.trustAsHtml(value) function.
<div class="appMessages">
<span ng-show="message" ng-click="clearMessage()" ng-bind-html="$sce.trustAsHtml(message)"></span>
</div>
angular.module('app', [])
.component('message', {
controller: function($sce, messagService){
this.messagService = messagService;
this.$sce = $sce;
},
template: '{{$ctrl.message}}<div ng-bind-html="$ctrl.$sce.trustAsHtml($ctrl.messagService.message)"></div>'
})
.service('messagService', function(){
this.message = '';
this.updateMessage = function(message){
this.message = message;
}
})
.controller('mainCtrl', function($scope, messagService){
$scope.updateMessage = function () {
messagService.updateMessage('wow <b style="color:yellow;">shiny</b> message');
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-controller="mainCtrl" ng-app="app">
<message></message>
<button type="button" ng-click="updateMessage()"> update message</button>
</div>
in my index.html I have my <header> then <body> with some subelements and <footer> elements.
Rest of the code is in template for each page like templates/home.html, templates/login.html
For one of this page I need to replace:
<body class="page-homepage" ng-app="historyApp" id="body">
to
<body class="page-subpage" ng-app="historyApp">
Is there some nice way how to do it?
I find some ways with jquery like (take it as example):
var el = document.querySelector('#body');
el.classList.remove('page-homepage');
el.classList.add('page-subpage');
but I believe that many of you already had to deal with this is kind of problem.
Can someone suggest me how to do this in angular?
In my opinion, the best way to do this is to add a variable to your $rootScope
$rootScope.isSub = false;
And use ngClass directive on your body element :
<body ng-class="{'page-subpage':isSub,'page-homepage':!isSub}" ng-app="historyApp">
And for each controller specify the $rootScope.isSub value.
I've ran into a bit of a problem, wherein I've created a $scope.msg and it's printing to my console just fine, but it won't render itself on the DOM. I'm using Browserify to require angular and bundle my js.
index.html
<body ng-app="zeroApp" ng-controller="MainCtrl">
<div class="container">
<div class="item">
<h1>{{ msg }}</h1>
</div>
</div>
<script src="./js/app.js"></script>
</body>
app.js
(function() {
'use strict';
var angular = require('angular');
angular.module('zeroApp', [])
.controller('MainCtrl', ['$scope', function($scope) {
$scope.msg = "Hello Angular!";
console.log($scope.msg);
}]);
})();
Any reason why this isn't being exposed to the DOM and my <h1> element is empty?
Any help is appreciated. Thanks in advance!
Unfortunately I'm not an Angular expert so I can't explain the details, but the problem is that Angular wont detect that change, and thus it wont be propagated into the view. There are other ways around it, but one rather simple fix is to wrap the message into an extra object. Instead of using $scope.msg, try using $scope.msg.txt and it should work.
Hopefully someone with more knowledge of Angular's inner workings can clarify this further.
Here's another Fiddle to demonstrate: http://jsfiddle.net/29Luq8ns/1/
Notice I'm using $timeout in it. That's another way you could work around the problem. By changing $scope.msg inside a $timeout function, it will work, even without a delay parameter.
Figured it out. I was using Swig in my gulpfile.js to do render my HTML templates. The mustache templating language of Swig must have been conflicting with Angular's templating lang. Took it out of my build process and it works like a charm.
Thanks for all the help.
I'm trying to create some website about AngularJS in AngularJS ;-) I've added ng-app on body element, and now all my code snippets (which are all in <pre><code> ... </code></pre> tags) are parsed by angular (it adds ng-binding class for example). Is there a way to tell angular that given block is only code snippet and should't be parsed?
Yes, you can use the ngNonBindable directive
For example:
<div ng-non-bindable>Ignored: {{1 + 2}}</div>
In AngularJS, any inline javascript code that included in HTML templates doesn't work.
For Example:
main.html file:
<div ng-include="'/templates/script.html'"></div>
And script.html file:
<script type="text/javascript">
alert('yes');
</script>
When I open main page, I expect an alert message that say 'yes' but nothing happens. I think some security restrictions in the AngularJS is preventing inline scripts, but I couldn't find any workaround about that.
Note: I don't use jQuery or any other framework, only AngularJS 1.2.7.
jQlite does not support script tags. jQuery does, so the recommendation is to include jQuery if you need this functionality.
From Angular's Igor Minar in this discussion:
we looked into supporting script tags in jqlite, but what needs to be
done to get a cross-browser support involves a lot of black magic. For
this reason we decided that for now we are just going to recommend
that users use jquery along with angular in this particular case. It
doesn't make sense for us to rewrite one third of jquery to get this
working in jqlite.
Here's the related github issue jqLite should create elements in same way as jQuery where Igor sums up, before closing the issue, with this:
This is too much craziness for jqlite, so we are not going to do it.
Instead we are going to document that if you want have script elements
in ng:include or ng:view templates, you should use jquery.
demo plunker with jquery
Angular uses the $sanitize on ng-include directives which strips out scripts. A better approach for templates is to create a controller for that template.
It is better to use an individual controller for templates.
In template.html
<form role="form" ng-controller="LoginController">
<input type="text" placeholder="Email" ng-model="email">
<input type="password" placeholder="Password" ng-model="password">
<button class="btn btn-success" ng-click="login()">Sign in</button>
</form>
In the LoginController you can use whatever code you want
angular.module('myApp.controllers', []).
controller('LoginController', [function() {
alert('controller initialized');
}])
The event triggered when ng-include adds content is $includeContentLoaded. Your scripts should be included in this event:
For example (Plucker Demo):
function SettingsController($scope, $window) {
$scope.template={};
$scope.template.url = "demo.html";
$scope.$on('$includeContentLoaded', function(event){
$window.alert('content load');
});
}
Additionally you can set an init function using the onload attribute:
html
<div ng-include="template.url" onload="alertMe()" > </div>
</div>
Controller
$scope.alertMe=function(){
$window.alert('sending alert on load');
}
Include jQuery and change order of the angular.js and jquery.js. jQuery must be first, otherwise it does not work