Angularjs use same ng-app multiple times on page - angularjs

I'm dealing with a content management system that needs to "inject" a reusable component into a page.
I want to inject the following component (html and javascript).
<script type="text/javascript">
if(typeof angular == 'undefined') {
document.write(unescape("%3Cscript type='text/javascript' src='/resources/scripts/lib/angular.min.js'%3E%3C/script%3E"));
document.write(unescape("%3Cscript type='text/javascript' src='/resources/scripts/pricing/app.js'%3E%3C/script%3E")); }
}
</script>
<div ng-app="pricing" ng-controller="PriceController as pc" ng-init="pc.getPrices('myprod', 'PER')">
Some text {{ pc.prices.msg["startdat tarief"] | jsDate }} .
More text {{ pc.prices.msg["einddat product"] | jsDate }}.
</div>
The component must be able to be injected multiple times on the page.
The problem is that the controller works fine, but only for the first injection.
This probably has something to do with that I am using the same app multiple times.
I am fairly new to angular.
How can I inject the same component multiple times?
Note that I am not able to init the app on a higher level. Because this would require the content manager to edit all pages, we juist want to inject a HTML component with javascript (i.e. the code snippet).

If you are able to add a unique ID to the module div you can manually bootstrap your angular app as follows:
function bootstrapAngular(id) {
angular.bootstrap(document.getElementById('module-' + id), ['app']);
}
angular.module('app', []).controller('sample', function ($scope) {
$scope.foo = 'bar';
});
bootstrapAngular(1);
bootstrapAngular(2);
bootstrapAngular(3);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="module-1">
<div ng-controller="sample">
{{ foo }}
</div>
</div>
<div id="module-2">
<div ng-controller="sample">
{{ foo }}
</div>
</div>
<div id="module-3">
<div ng-controller="sample">
{{ foo }}
</div>
</div>

You cannot have multiple ng-App directives on a single page. From the Angular.js documentation:
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.
If placing ng-App higher in the tree isn't an option, you will have to re-write the components so that each component gets a unique angular.module() and when the component is injected, it will need to fire angular.bootstrap().

Related

Why run method of this module isn't run? [duplicate]

I'm trying to add two angular apps / modules to one page.
In the fiddles below you can see that always only the first module, referenced in the html code, will work correctly, whereas the second is not recognized by angular.
In this fiddle we can only execute the doSearch2 method, whereas in this fiddle only the doSearch method works correctly.
I'm looking for the way how to correctly place two angular modules into one 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
I created an alternative directive that doesn't have ngApp's limitations. It's called ngModule. This is what you code would look like when you use it:
<!DOCTYPE html>
<html>
<head>
<script src="angular.js"></script>
<script src="angular.ng-modules.js"></script>
<script>
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
</script>
</head>
<body>
<div ng-modules="MyModuleA, MyModuleB">
<h1>Module A, B</h1>
<div ng-controller="MyControllerA">
{{name}}
</div>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
<div ng-module="MyModuleB">
<h1>Just Module B</h1>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
</body>
</html>
You can get the source code at:
http://www.simplygoodcode.com/2014/04/angularjs-getting-around-ngapp-limitations-with-ngmodule/
It's essentially the same code used internally by AngularJS without the limitations.
Why do you want to use multiple [ng-app] ? Since Angular is resumed by using modules, you can use an app that use multiple dependencies.
Javascript:
// setter syntax -> initializing other module for demonstration
angular.module('otherModule', []);
angular.module('app', ['otherModule'])
.controller('AppController', function () {
// ...do something
});
// getter syntax
angular.module('otherModule')
.controller('OtherController', function () {
// ...do something
});
HTML:
<div ng-app="app">
<div ng-controller="AppController">...</div>
<div ng-controller="OtherController">...</div>
</div>
EDIT
Keep in mind that if you want to use controller inside controller you have to use the controllerAs syntax, like so:
<div ng-app="app">
<div ng-controller="AppController as app">
<div ng-controller="OtherController as other">...</div>
</div>
</div>
You can bootstrap multiple angular applications, but:
1) You need to manually bootstrap them
2) You should not use "document" as the root, but the node where the angular interface is contained to:
var todoRootNode = jQuery('[ng-controller=TodoController]');
angular.bootstrap(todoRootNode, ['TodoApp']);
This would be safe.
Manual bootstrapping both the modules will work. Look at this
<!-- IN HTML -->
<div id="dvFirst">
<div ng-controller="FirstController">
<p>1: {{ desc }}</p>
</div>
</div>
<div id="dvSecond">
<div ng-controller="SecondController ">
<p>2: {{ desc }}</p>
</div>
</div>
// 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 the link to the Plunker
http://plnkr.co/edit/1SdZ4QpPfuHtdBjTKJIu?p=preview
NOTE: In html, there is no ng-app. id has been used instead.
I made a POC for an Angular application using multiple modules and router-outlets to nest sub apps in a single page app.
You can get the source code at: https://github.com/AhmedBahet/ng-sub-apps
Hope this will help

AngularJS + UI Kit

I'm new to angularJS, When i use UIKit only its working fine (Yii2). When i use with angularJS the Javascript events like(tab, slider, dropdown, etc) not working.. I don't know what i have missed or is the any dependency i have to add it work this. This is my app.js code
var app = angular.module('nApp', [
'ui.router', //
'ngSanitize', // sanitize HTML
'ngAnimate', // CSS and JavaScript ng-animate
'ngRoute', // $routeProvider
'toaster', // toasterProvider
'mgcrea.ngStrap', // bs-navbar, data-match-route directives
]);
And my AppAsset.php file is like this,
public $css = [
'uikit/css/uikit.gradient.min.css',
];
public $js = [
'js/app.js',
'uikit/js/uikit.min.js',
];
public $depends = [
'yii\web\YiiAsset',
'frontend\assets\AngularAsset',
];
In AngularAsset.php I have loaded angular related files(css & js).
Please help me..
the reason you are having problems with integrating uikit components into your angularjs project is because uikit components like accordion or slider in your case, are instantiated the moment they are loaded. What this means is that if you place the slider.js file in the header of the first page you load, and your slider is in another html file not loaded yet, say users.html, then the slider.js file will not attach the component to your element in users.html.
If that doesn't clear things up, let me show you what I mean.
This is your index.html (the first html page you load).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!--loading uikit components in here will not work on other html files -->
</head>
<body data-ng-app="nApp">
<nav></nav>
<div class="container" data-ng-view>
<!--This is where the other html files will load in, like users.html -->
</div>
<footer></footer>
</body>
</html>
And this is users.html with an accordion component.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.25.0/js/components/accordion.min.js"></script>
<!--This accordion will work-->
<div class="uk-accordion" data-uk-accordion="{collapse:false}">
<h3 class="uk-accordion-title">Title 1</h3>
<div class="uk-accordion-content">content one</div>
<h3 class="uk-accordion-title">Title 2</h3>
<div class="uk-accordion-content">content two</div>
<h3 class="uk-accordion-title">Title 3</h3>
<div class="uk-accordion-content">content three</div>
</div>
To elaborate, the accordion will work in users.html because the javascript file accordion.js is instantiated when users.html is loaded and so the component can attach to the respected element.
I hope this clears things up for you.
An other way is to say at UIkit, to make an 'watcher' on the dom
if you put data-uk-observe on the div.
Ui kit will be in place at this moment
Observe the DOM to auto-init new added components, e.g via AJAX.
If you inject dynamic HTML markup into the DOM via JavaScript, just add the data-uk-observe attribute to one of the parent elements to auto-initialize UIKit JavaScript components.
Usage
<div data-uk-observe>
<!-- inject your dynamic html here -->
</div>
Observe an element via JavaScript
UIkit.domObserve('#element', function(element) { /* apply on dom change within element */ })
Since AngularJS renders the HTML after the digest cycle, you have to re-render the UI component. Let's say for example for have the UIKit accordion:
<div id="myAccordion"
data-uk-accordion="{ showfirst: false }"
class="uk-accordion"
data-uk-observe>
Then, in the callback method from the controller for re-rendering the UI component you have to invoke:
UIkit.domObserve('#myAccordion', function (element) {
UIkit.component.boot('accordion');
UIkit.init(element);
});

Angularjs | how to get an attribute value from element in which controller is defined

I'm still fighting with simple things in Angular. I have jQuery and Backbonejs background, so please do not yell on me. I try hard to understand differences
I have HTML in which from rails is given ID of project as data-project-id:
<div data-ng-controller="ProjectCtrl as ctrl" data-project-id="1" id="project_configuration">
Is there any chance to get access to this attribute? I need it to my API calls...
To access an elements attributes from a controller, inject $attrs into your controller function:
HTML
<div test="hello world" ng-controller="ctrl">
</div>
Script
app.controller('ctrl', function($attrs) {
alert($attrs.test); // alerts 'hello world'
});
In your example, if you want to get data-project-id:
$attrs.projectId
Or if you want to get id:
$attrs.id
Demo:
var app = angular.module('app',[]);
app.controller('ctrl', function($attrs) {
alert($attrs.test);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl" test="hello world">
</div>
In Angular world you should use directives to manipulate with DOM elements. Here a nice explanation how to get attribute value from custom directive (How to get evaluated attributes inside a custom directive).
But if you still want to get it's value from controller you are able to use jQuery as well $('#project_configuration').data('project-id')

AngularJS not getting activated when loading HTML from script

I have this scenario, I am loading part of HTML (which has AngularJS directives) dynamically via script and I see AngularJS is not getting activated.
here is the example I am looking at. Is there anyway I can tell AngularJS to start bind on document ready? Loading an aspx page containing this widget1 content via a iframe seems to work but I am trying to avoid iframe and use client side script.
Appreciate any help.
<body ng-app>
main content page
<br />
<!-- widget1 -->
<b>Widget1</b>
<div id="widget1">
<!-- load below div via jquery/or any method from a remote html file-->
<script type="text/javascript">
$("div#widget1").load("/widgetsfolder/widget1.htm");
</script>
</div>
widget.htm file has below content.
<div ng-controller="TodoCtrl">
Total todo items: {{getTotalItems()}}
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done" />
<span class="done-{{todo.done}}">{{todo.text}} </span></li>
</ul>
</div>
my controller code below.
`function TodoCtrl($scope) {
$scope.totalItems = 4;
debugger;
$scope.todos = [{
text: 'learn angularjs',
done: false
}, {
text: 'implement angularjs',
done: false
}, {
text: 'something else',
done: false
}, ];
$scope.getTotalItems = function () {
return $scope.todos.length;
}
}`
sample code here
http://jsfiddle.net/devs/RGfp4/
Apero's answer describes what is going on. I believe you are going to want to use ng-include. Your html would look something like this:
<body ng-app>
main content page
<br />
<!-- widget1 -->
<b>Widget1</b>
<div ng-include="'/widgetsfolder/widget1.htm'">
</div>
</body>
AngularJS evaluates the scope and renders the page after it is loaded.
Here, your js script loads the widget html but after Angular already compiled the scope etc.
I believe this will not work this way.
You can use angulars ngINclude to fetch outside documents, but I don't suggest it, it can be buggy. You can get the partials using either $http or $resource, this will fetch the data and compile the angular directives inside.
If you want to load the script using some other method, you can store the data as a string inside the controller and use a $compile directive in order to execute the angular code inside it.

Multiple angularjs applications for one page [duplicate]

I'm trying to add two angular apps / modules to one page.
In the fiddles below you can see that always only the first module, referenced in the html code, will work correctly, whereas the second is not recognized by angular.
In this fiddle we can only execute the doSearch2 method, whereas in this fiddle only the doSearch method works correctly.
I'm looking for the way how to correctly place two angular modules into one 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
I created an alternative directive that doesn't have ngApp's limitations. It's called ngModule. This is what you code would look like when you use it:
<!DOCTYPE html>
<html>
<head>
<script src="angular.js"></script>
<script src="angular.ng-modules.js"></script>
<script>
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
</script>
</head>
<body>
<div ng-modules="MyModuleA, MyModuleB">
<h1>Module A, B</h1>
<div ng-controller="MyControllerA">
{{name}}
</div>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
<div ng-module="MyModuleB">
<h1>Just Module B</h1>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
</body>
</html>
You can get the source code at:
http://www.simplygoodcode.com/2014/04/angularjs-getting-around-ngapp-limitations-with-ngmodule/
It's essentially the same code used internally by AngularJS without the limitations.
Why do you want to use multiple [ng-app] ? Since Angular is resumed by using modules, you can use an app that use multiple dependencies.
Javascript:
// setter syntax -> initializing other module for demonstration
angular.module('otherModule', []);
angular.module('app', ['otherModule'])
.controller('AppController', function () {
// ...do something
});
// getter syntax
angular.module('otherModule')
.controller('OtherController', function () {
// ...do something
});
HTML:
<div ng-app="app">
<div ng-controller="AppController">...</div>
<div ng-controller="OtherController">...</div>
</div>
EDIT
Keep in mind that if you want to use controller inside controller you have to use the controllerAs syntax, like so:
<div ng-app="app">
<div ng-controller="AppController as app">
<div ng-controller="OtherController as other">...</div>
</div>
</div>
You can bootstrap multiple angular applications, but:
1) You need to manually bootstrap them
2) You should not use "document" as the root, but the node where the angular interface is contained to:
var todoRootNode = jQuery('[ng-controller=TodoController]');
angular.bootstrap(todoRootNode, ['TodoApp']);
This would be safe.
Manual bootstrapping both the modules will work. Look at this
<!-- IN HTML -->
<div id="dvFirst">
<div ng-controller="FirstController">
<p>1: {{ desc }}</p>
</div>
</div>
<div id="dvSecond">
<div ng-controller="SecondController ">
<p>2: {{ desc }}</p>
</div>
</div>
// 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 the link to the Plunker
http://plnkr.co/edit/1SdZ4QpPfuHtdBjTKJIu?p=preview
NOTE: In html, there is no ng-app. id has been used instead.
I made a POC for an Angular application using multiple modules and router-outlets to nest sub apps in a single page app.
You can get the source code at: https://github.com/AhmedBahet/ng-sub-apps
Hope this will help

Resources