- Use $compile service to compile the html contents that you dynamically loaded, AND at the same time, bind it to a scope.
- Would suggest you make a directive. You can pass in the url of partial using an attribute.
Particularly the directive's link property is like this:
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
See the answer to this question for details, it has provided a plunker demo.
Hope this helps.
EDIT
I would like write more in response to @Víťa Plšek - angular.cz's answer.
I once did something even more dynamic, which is to load javacsript in the partial!
Once upon a time, I made a one-page application based on angularjs. Each menu function is a partial dynamically loaded using ajax. Naturally, each menu function is a separate angular controller. To make the codes easier to manage, I did not merge all controllers' codes in one file and load them all at once from index.html. Instead, I let each partial specify its own controller's js code using a custom directive and do lazy-load.
So the main page of the one-page application use a custom directive to contain dynamically loaded partial:
<body>
...
<dynamic-partial src={{selected_partial_url}}>
</dynamic-partial>
...
</body>
, while each partial is stored in a separate html file. Its contents are like this:
<lazy-load-script src="js/controllers/dynamic-controller1.js" >
</lazy-load-script>
<div ng-controller="DynamicController1">
...
</div>
Now, there is one issue: If we compile the partial file all at once, there will be an error saying DynamicController1
is not defined. It is true. We have to loaded dynamic-controller1.js
first, and then compile the part <div ng-controller="DynamicController1"></div>
after the js file has finished loading.
So the codes of directive DynamicPartial
are like this:
"use strict";
angular.module('MyApp')
.directive('DynamicPartial', [ "$compile", function( $compile ) {
return {
restrict: 'E',
scope: {
src: '@'
},
link: function (scope, element, attr) {
console.log( "Linking web-terminal. src = " + scope.src );
scope.onLazyLoadDone = function(){
// compile the other html elements in the partial, and put them in <dynamic-partial> element.
element.html( $compile( scope.other_elements_html )(scope) );
}
attr.$observe( 'src', function( value ){ // fetch new partial as soon as 'src' attribute changes:
console.log( "'src' attribute changed. Fetching new partial: " + value );
if( "" == scope.src ) {
element.addClass("ng-hide");
return;
}
else
element.removeClass("ng-hide");
$.ajax({
dataType: 'text',
timeout: 5000,
url: scope.src
})
.done(function (data) {
console.log( "Successfully fetched terminal file, length = " + data.length);
// compile the <lazy-load-script> tag first:
var lazy_load_element = $('<div/>').append( $(data)).find( "lazy-load-script, [lazy-load-script]" );
if( lazy_load_element.length > 0 ) { // lazy-load-script element found:
// Here we pragmatically set the "on-loaded" attribute for the <lazy-load-script> element found in the loaded partial, so that we can get called when the javascript file specified by <lazy-load-script> element finishes loading.
lazy_load_element.attr("on-loaded", "onLazyLoadDone()");
$compile($('<div/>').append(lazy_load_element).html())(scope);
}
// Save the remaining DOM elements in the partial, which will be compiled after the lazy-load is done.
scope.other_elements_html = $('<div/>').append( $(data).filter(':not([lazy-load],lazy-load)')).html();
if( 0 == lazy_load_element.length )
scope.onLazyLoadDone();
})
.fail(function (jqXHR, textStatus, errorThrown) {
console.error("Failed fetching menu from server: " + errorThrown.message);
});
});
if( undefined == scope.src || 'not defined' == scope.src ) {
return;
}
}
};
}]);
And the codes of <lazy-load-script>
are as follows. The crux is to use an onLoaded
scope attribute to specify a callback to parent controller.
"use strict";
angular.module('SyncreonApp')
.directive('lazyLoad', [ "$ocLazyLoad", function( $ocLazyLoad ) {
return {
restrict: 'EA',
scope: {
src: '@',
onLoaded: '&'
},
link: function (scope, element, attr) {
//console.log( "Linking lazyLoadScript, url to load = " + scope.src );
if( undefined != scope.src && '' != scope.src ){
$ocLazyLoad.load( scope.$parent.$parent.getBasePath() + scope.src )
.then(function () {
//Javascript lazy load done, calling back to parent controller, so that parent will continue with compiling other parts of the partial.
scope.onLoaded();
});
}
else
console.error( "Illegal src attribute in directive lazyLoad");
}
};
}]);