I have a scenario where I need to dynamically load an Angular JS application. I have based the code on this:-
https://stackoverflow.com/a/15252490/1545858
Now, I have code that works really well with angular js 1.1.5, but in 1.2.1, no such luck.
Here is the JS code:-
$("#startMeUp").click(function() {
// Make module Foo
angular.module('Foo', []);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function($scope) {
$scope.data = {};
$scope.data.name = 'KDawg';
$scope.destroy = function() {
$scope.$destroy();
$('#Ctrl').remove();
};
$scope.$on("$destroy", function () {
console.log("EXTERMINATE");
});
});
// Load an element that uses controller Ctrl
$('<div ng-controller="Ctrl" id="Ctrl"> ' +
'<input type="text" ng-model="data.name"></input>' +
'{{data.name}}' +
'<button ng-click="destroy()">Destroy Me</button></div>').appendTo('#container');
// Bootstrap with Foo
angular.bootstrap($('#Foo'), ['Foo']);
});
And here is the HTML:-
<button id="startMeUp">Start Me Up!</button>
<div id="Foo">
<div id="container">
</div>
</div>
Now, if you start and destroy and start again with angular js 1.1.5, everything works fine, but in angular js 1.2.1 it does not work in on the second start. Any thought on how to make it work in 1.2.1?
Here is the js fiddle:-
http://jsfiddle.net/Y9wj2/
As charlietfl says, you don't need to bootstrap more than once. In fact, using angular.js 1.2.1, the error generated that breaks everything is telling you exactly that:
[ng:btstrpd] App Already Bootstrapped with this Element ''
You should think carefully about whether you really need this controller to be dynamic. If you can just use something like ng-include to load the extra content then you will have a much easier time and no need to worry about compiling the content.
If you find you really do need to take this HTML and load it from outside of angular context then you can use the $compile service. Bootstrap the app once somewhere first, preferably using ng-app and grab the injector.
var injector = angular.bootstrap($('#Foo'), ['Foo']);
or
<div id="Foo" ng-app="Foo"></div>
var injector = $('#Foo').injector();
Now you can insert the HTML however you like and then compile and link it using
injector.invoke(['$compile', '$rootScope', function($compile, $rootScope) {
$compile(insertedJqLiteNode)($rootScope);
});
Related
I am using goldenlayout with angualrJS. I am facing below exception:
Error: ng:btstrpd App Already Bootstrapped with this Element
on execution of this line of code
myGoldenLayout.on('initialised', function () {
angular.bootstrap(angular.element('#layoutContainer')[0], ['app']);
});
The reason is, I have already ng-app in my HTML so how can I register golden layout when I already have ng-app?
https://github.com/codecapers/golden-layout-simple-angular-example/issues/1
Well, the official Golden Layout docs recommend using manual bootstrap, but if you want to keep using ng-app, then you have to make sure that your components (templates) are compiled by Angular (via $compile). Here's an example of how to do that:
angular.module('someApp') // your main module name here
.run(function ($compile, $rootScope) {
myLayout.registerComponent('template', function( container, state ){
var templateHtml = $('#' + state.templateId).html();
var compiledHtml = $compile(templateHtml)($rootScope);
container.getElement().html(compiledHtml);
});
myLayout.on( 'initialised', function() {
$rootScope.$digest(); // Golden Layout is done, let Angular know about it
});
});
// somewhere...
myLayout.init();
Basically, the main difference from the example in the repository you provided is that instead of just appending raw HTML, we $compile it with Angular, so now it knows to set up bindings and keep the html updated.
This should allow you to keep using ng-app instead of manual bootstrap.
I'm using angularJS 1.4.9 and ui bootstrap 1.0.3.
I would like to decor uibDatepicker directive but the decorator function is never executed and there is no error or warning message in the console.
See this plunker
var uib = angular.module('ui.bootstrap');
uib.config(function($provide) {
$provide.decorator('uibDatepickerDirective', function($delegate) {
alert('never logs this');
return $delegate;
});
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function() {
var main = this;
main.welcome = 'hello you';
});
Thanks for your help
The decorator function is never executed because you never actually use this directive you are trying to decorate.
Decorator is going to be invoked only when the directive is requested by real element in template. This is because services and directives are lazily configured (providers) so provider will not be active unless the service or directive is requested.
Here is an example of it being called when you put directive tag in template. Try adding this line in your code:
<uib-datepicker ng-model="dt" class="well well-sm"></uib-datepicker>
Demo: http://plnkr.co/edit/p4Hcgz83wv9d1WOl6eGo?p=preview
I am very new in angularJs please guide me ,i have created custom directive called testDirective in separate file and my controller and config files are in separate files i want to use my custom directive in my html page but while loading the application that custom element is loading but not able to fetch content of directive i guess m missing some sort of mapping .kindly help me
//this is testDirective.js
var app=angular.module('mytodoApp');
app.directive('testDirective',function(){
return {
restrict:'E',
template:'<h2>This is comes from directive</h2>'
};
});
var app=angular.module('mytodoApp');
app.controller('MainCtrl', function ($scope) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
});
< test-directive>
< /test-directive>
You have to bootstrap the Angular app, otherwise the HTML page does not know that you are using AngularJS and what you app has.
Bootstrapping is done by using ng-app="moduleName".
<div ng-app="mytodoApp">
<test-directive></test-directive>
</div>
As said by #JBNizet, you should add your custom directive code, by adding it trough the script tag in your HTML code.
Since browsers can only load 5 to 10 scripts at the same time, it is nessesary in production to concatenate your files with Grunt or Gulp.
I am new to Angular JS. What I am doing is to bind ui-sref on JQuery loaded data.
All the JQuery plugins and rest of Angular is working perfectly fine. What I have for now looks like:
app.controller("FeedController", ['$scope', '$http', '$compile', function($scope, $http, $compile) {
var feed = this;
feed.years = [];
feed.getYears = function() {
$http.get('/timeline/years').success(function(data) {
feed.years = data;
});
};
feed.getYears();
$scope.$watch('sliderWrapper', function() {
applyTreemap(); // jquery treemap layout plugin
applyKnob(); // jquery knob plugin
});
// I was trying to compile externally loaded DOM by that plugin here.
// Didn't figure out how to do it.
$scope.refresh = function() {
// #slider is main content wrapper
$compile( $("#slider").html())($scope);
};
}]);
Please don't suggest to use AngularJS instead of JQuery. Actually this is a Treemap Layout plugin and already integrated into existing website.
Okay so $compile works as in my code but there are some problems I faced. Here's one. Consider the following code.
<div id="slider">
<div ng-repeat="slide in slides">
<!-- html loaded by jquery ajax will go here -->
</div>
</div>
In angular I was doing
$compile( $("#slider").html())($scope);
So, I was compiling html of #slider in angular and it already has angular bindings besides ajax loaded content. So angular compiler will re-render them and you will run into problems.
So keep in mind that you never $compile html that already has angular bindings.
So I solved my problem by putting
href="#/path/to/state"
instead of doing
ui-sref="home.child()"
into ajax loaded conent.
Sometimes you know something and its not in your mind when you are stuck. :-D
I have a tabbed navigtion in my webapp that looks like this
Now I want to Change the directive each time the user clicks on one of the Navigation points. My Idea was to init the page with the first template.
$scope.currentDirective = $compile('<div order-Sale></div>');
Then when the user clicks on a tab, I wanted to change and compile the content again with a new directive in it. But for some reason this is not working. How would you proceed in order to archive this dynamic content loading? I really want to only load the content on necessary need and not just to show or hide it. I think using directives is the right way to go for it, but I'm a but stuck at the implementation... Someone any pointer ? (I don't want to use any jQuery)
What I tried [Edit]:
The controller.js
app.controller('pageController',['$scope','$compile', function($scope, $compile){
var templates = ['<div first-template></div>','<div second-template></div>'];
$scope.currentTemplate = $compile(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $compile(templates[id]);
};
}]);
The HTML
<div ng-controller="pageController">
<li>
<a ng-click="changeTemplate('1')">Change Template</a>
</li>
{{currentTemplate}}
</div>
UPDATE
$compile returns a linking function not a value, you cannot just bind it to your template with interpolation.
You should use ngBindHtml instead of regular bindings ( ngBind & {{ }} ).
ngBindHtml does compiling, linking and watching all out-of-the-box.
With ng-bind-html-unsafe removed, how do I inject HTML?
Here is a plunker
app.controller('pageController',['$scope','$compile','$sce', function($scope, $compile, $sce){
var templates = ['<div>first-template</div>','<div>second-template</div>'];
$scope.currentTemplate = $sce.trustAsHtml(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $sce.trustAsHtml(templates[id]);
};
}]);
The markup:
<div ng-controller="pageController">
<button ng-click="changeTemplate('1')">Change Template</button>
<div ng-bind-html="currentTemplate"></div>
</div>
For more robust dynamic content loading you have two good alternatives:
ngRoute from angular team.
ui-router from angular-ui team.
If you want to change and compile the content again, well that's exactly what ng-view/ ui-view directives already do for you.
Why not just use a directive:
You probably need to load a different template (html partial) for each tab.
You probably need to change the url based on the tab (and vice versa)
You probably need to instantiate a different controller for each tab.
ngRoute and ui-router come with their own directives.
You can implement your own route module if you want but that's more than just a directive.