New to Angular. I feel like I'm missing something obvious: Shouldn't I easily be able to run to separate AngularJs apps (modules) in the same html page? Something like this:
<section ng-app="HelloWorldApp" ng-controller="HelloWorldController">
Hello {{name}}!
</section>
<br />
<section ng-app="MyNameIsApp" ng-controller="MyNameIsController">
My Name is {{FirstName}} {{LastName}}!
</section>
Javascript:
var HelloWorldApp = angular.module('HelloWorldApp', []);
HelloWorldApp.controller('HelloWorldController', function($scope) {
$scope.name = 'World';
});
var MyNameIsApp = angular.module('MyNameIsApp', []);
MyNameIsApp.controller('MyNameIsController', function($scope) {
$scope.FirstName = 'John';
$scope.LastName = 'Smith';
});
This only runs the first module, while the second doesn't appear to do anything. I want to do this so that I can build reusable, encapsulated directives for multiple pages that don't have to name their modules the same thing.
Live Example: http://plnkr.co/edit/cE6i3ouKz8SeQeA5h3VJ
We ended up building small hierarchy of modules, however my original question can done, with just a bit of work (see below).
It is possible, but it requires a little bit coding by hand. You need to bootstrap the angular apps on your own. Don't worry, it is not that complicated
Do not add ng-app attributes in your HTML
Make sure you can fetch the DOM elements holding the app
When DOM is loaded you need to start the apps on your own: angular.bootstrap( domElement, ['AppName']);
Fork of you plunker which works: http://plnkr.co/edit/c5zWOMoah2jHYhR5Cktp
According to the Angular docs for ngApp:
Use this directive to auto-bootstrap an application. Only one
directive can be used per HTML document. The directive designates the
root of the application and is typically placed at the root of the
page.
Seems it's by design.
You can specify any nested apps in the module def of the main one.
angular.module("myapp", ['statusapp', 'tickerapp']).controller(....
and in a separate file, you have the other apps defined. We're using a template engine which hides some of this, but you'll end up with HTML that contains nested ng-apps and javascript for each one that defines the module/controller. The code above is the trick to getting more than one bootstrapped.
Related
Need help in knowing how to use AngularJS in Salesforce Lightning and VisualForce pages?
I do know that it has to be used using static resource but need step by step details in using it.
Well, when talking about AngularJS, I believe you are talking about the version 1.x, because the version 2.0, which is currently 6.0 needs to be used in a different way compared to the earlier version.
For Angular 1.x, I am listing down the steps here:
The very first step is to download the latest version of AngularJS. You can get that here.
https://angularjs.org/
Zip the AngularJS file and create a Static Resource, so that you can reference it in your pages. In case, you want to know how to create a static resource in Salesforce, refer SF documentation.
https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_resources_create.htm
(You also have an option of directly referencing the CDN file in your VF pages, however, it is recommended that you should go for Static resources.)
Now, to get AngularJS in action, you have to do something called as “bootstrapping” your app.
This can be done in 2 ways:
Use “ng-app” directive. (NOTE:You can only have one ng-app directive in your HTML document. If more than one ng-app directive appears, the first appearance will be used.)
And the second way is manual bootstrapping. You can refer to this blog to know more about it.
http://blogs.quovantis.com/process-to-use-manual-bootstrapping-in-angularjs-with-examples/
You can use the following code to see manual bootstrapping in action.
<apex:page showHeader="true" sidebar="true">
<apex:includeScript value="{!URLFOR(<your-static-resource-name>,'angular.min.js')}">
<div id="demo" ng-controller="demoAngularController">
{{testVar}}
</div>
<script>
var demoModule = angular.module('demo', []);
demoModule.controller('demoAngularController', function ($scope) {
$scope.testVar = 'Prem';
});
angular.bootstrap(document.getElementById("demo"),['demo']);
</script>
Make sure you replace the static resource name with actual name that you created in the system.
I have added a code snippet as well.
var app = angular.module('demo', []);
app.controller('demoAngularController',function MyController($scope) {
$scope.testVar = 'Prem';
});
angular.bootstrap(document.getElementById("demo"),['demo']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="demo">
<div ng-controller='demoAngularController'>
<h1>Hello, {{ testVar }}!</h1>
</div>
</div>
For Angular 2.0
In case, you want to learn the usage of Angular2.0 (or Angular6.0 as known currently), you may look at this tutuorial and let me know your feedback.
https://premjpal.wordpress.com/2018/09/06/getting-started-with-angular6-in-salesforce/
Happy coding!
Is there a way to define an angular module inside another module ? I have a template in my web application which is called for almost every page of the application. In the template definition I set the ng-app. So for this ng-app I can declare the modules I need in all pages of the application (or almost every page). Now there are some modules I want to add only on specific pages. The problem is that in those pages I already have the ng-app of the template.
So is there a way to keep the ng-app as some kind of root ng-app which declared the modules I need everywhere and then add specific modules inside specific pages too ?
That means is it possible to do something like this:
<div ng-app="rootApp">
<div ng-app="specificApp">
...
</div>
</div>
The rootApp contains the module that are declared in my template, that are use in all the pages, and the specifiApp contains the modules I need only in one specific page.
Thanks !
[EDIT] Bootstrap attempt:
var reportHolidaysByEmployeeApp = angular.module('reportHolidaysByEmployeeApp', ['fitnetApp', 'ui.bootstrap']);
angular.bootstrap(document.getElementById("reportHolidaysByEmployeeApp"), ['reportHolidaysByEmployeeApp']);
reportHolidaysByEmployeeApp.controller('ReportHolidaysByEmployeeCtrl', function($scope, $filter, $timeout) {
fitnetApp is the global Module I load on the html tag in every page
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. --
http://docs.angularjs.org/api/ng.directive:ngApp
See also
https://groups.google.com/d/msg/angular/lhbrIG5aBX4/4hYnzq2eGZwJ
http://docs.angularjs.org/api/angular.bootstrap
If you are having separate controllers for your pages[views] , add dependency in your controller module to [rootApp] or [spcificApp] as your page needs.
$routeProvider.when('/view1',{
template:
controller:view1controller
})
if u need rootApp as dependency in view1 page
in your controller module
angular.module('GlobalCtrl',['rootApp'])
.controller('view1controller')
'
You cannot have two ng-app in a single web page.
If you need to add dependency module on specific page use
angular.module('reportHolidaysByEmployeeApp').requires.push('thirdpartymodule');
This will dynamically inject dependency in your already running angular application.
if you have the following:
<html ng-app="outerApp">
<head ng-app="innerApp"></head>
<script>
var outerApp = angular.module("outerApp", []);
var ACtrl = outerApp.controller("ACtrl", function($scope){console.log($scope.name);});
var BCtrl = outerApp.controller("BCtrl",function($scope){console.log($scope.name});
var CCtrl = innerApp.controller.("CCtrl", function($scope){ console.log($scope.name);});
var innerApp = angular.module("innerApp", []);
</scope>
Is this ok? is angular a global variable that will work for declaring modules out of both innerApp and outerApp? Also are there limits to number of ng-app's on a page? And do both ACtrl, and BCtrl have reference to the same $scope?
Thanks
This won't work because:
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.
See documentation
$rootscope,$scope are conceptually global variables, whom you can implement to achieve as global variables for sharing data between the modules,directives,controllers,views.
you should read conceptually DI(Dependency Injection) and how the conceptual framework implements in angular. you can inject the dependencies.
angular.module('modulename',[]);
[] is an array in which you define that module is dependant on the other module. in Other words.
Angular framework concepts works the injectable way DI(Dependency Inject).
however i strongly suggest you, what you trying to achieve is the right path is, you should make a simple custom directive and inject it as a dependency in the angular app.
This question already has answers here:
How to run two separate Angular js apps in the same page
(3 answers)
Closed 8 years ago.
I am trying to get a second AngularJS App and Controller working but it seems the first one runs but the second never does. It seems to work if I make a single app with 2 controllers. Any insights would be appreciated. Thanks!
JS
var app1 = angular.module("myapp1", []);
app1.controller('Ctrl1', function($scope){
$scope.variable = "Ctrl1 Working";
});
var app2 = angular.module("myapp2", []);
app2.controller('Ctrl2', function($scope){
$scope.variable = "Ctrl2 Working";
});
HTML
<div ng-app="myapp1" ng-controller="Ctrl1">
<p>{{variable}}</p>
</div>
<div ng-app="myapp2" ng-controller="Ctrl2">
<p>{{variable}}</p>
</div>
JSFiddler: http://jsfiddle.net/paullyvenne/8Ekv5/8/
The main problem here is that AngularJS can't boostrap several applications in just one HTML file.
Here i let you a link to AngularJS site that explain that.
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.
Instead of have several ng-app that mean several AngularJS Application, you can design a structure of multiples modules organized by feature. If you wanna know about it please see the links below:
Coderwall
clintberry.com
Henriquat.re
Background: Let's suppose for the sake of argument that you have 100,000 views (partials). Let's also suppose you have accompanying view-scoped controllers, and potentially view-scoped services and filters as well. Try to envision an aggregating application that hosts 100,000 disparate small applications.
Issue: When you have "partials" that require accompanying controllers, the typical solution is to do something like this:
$routeProvider.when('/app1', {
templateUrl: 'partials/view1.html',
controller: 'controller1'
});
The controller is typically loaded from index.html via:
<script src="js/directives/Controller1.js"></script>
The problem with this approach is that it doesn't scale. There are solutions out there for dynamically loading controllers, but they still require adding touch points in various config.
Ideal Solution: Ideally - again for very small applications whose numbers are in the 000's, the controller could be loaded dynamically, and from within the partial itself. This would alleviate the need to manage several files and several configuration touch points (not to mention network requests), and keep each partial very well contained.
It would look something like this:
In router:
$routeProvider.when('/apps/:appId', {
templateUrl: 'partials/app-frame.html',
controller: 'AppCtrl'
});
In containing html (app-frame) include the relatively disparate "mini app":
<h1>Currently hosting {{appId}}</h1><hr>
<div class="ng-include: appUrl"></div>
In partial resolved with appUrl, define controller and markup in one:
<script>
myApp.controller('controller1', ['$scope', function ($scope) {
$scope.foo = "bar";
}]);
</script>
<div ng-controller="controller1">
{{foo}}
</div>
For cases like this, where there are a lot of partials and a 1-1 mapping for controller and view, it can make sense to couple the two for development efficiencies and maintenance. It's a lot cleaner than using several files and additional configuration touch points.
The problem is, this doesn't work. It could be as simple as forcing the script to load prior to applying the directive... but not sure how to do that?
Here are some similar explanations of the problem:
https://groups.google.com/forum/#!topic/angular/H4haaMePJU0
Loading Partial Page With Angular and Compile The Controller
Igor from the AngularJS team says:
I see.. 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.
But I don't know what he means by "use jquery" ... JQuery is already loaded into the application from index.html (and prior to angularjs), but it sounds like I need to do something specifically within the partial itself.
You cannot add new controllers through module('app').controller(name, function() { .. }) after AngularJS bootstrap. In order make it work you should use $controllerProvider.register(name, function() { .. }).
You can override the original controller registering function in following way to be able to add controllers pre and pos bootstrap:
var app = angular.module('app', [
'ui.router'
]);
app.config(function($controllerProvider) {
app.controller = function (name, controller) {
$controllerProvider.register(name, controller);
};
});