Dynamically creating an angular view - javascript

I'm making an in game UI using awesomium, at some points the game loads up and executes a chunk of javascript which is meant to create arbitrary new UI elements. e.g.
jQuery(document.body).append('<span class="game-status-alert">You Lose!</span>');
That works nicely, the problem comes when I want to create some slightly more advanced UI elements, specifically using angular. For example something like:
function ChatBoxControl($scope) { /* Stuff */ }
jQuery(document.body).append(
'<div ng-controller="ChatBoxControl"><div ng-repeat="line in chat"><span>{{line}}</span></div></div>'
);
Not surprisingly, this does not create a new angular view. It simply adds that html to the document and never binds to the ChatBoxControl.
How can I achieve what I'm trying to do here?

You should $compile dynamically added angular content.
Something like:
jQuery(document.body).append(
$compile(
'<div ng-controller="ChatBoxControl"><div ng-repeat="line in chat"><span>{{line}}</span></div></div>'
)(scope)
);
scope for any element you can get using something like:
var scope = angular.element('#dynamicContent').scope();
Also you should get $compile that can be injected in other controller.
See also: AngularJS + JQuery : How to get dynamic content working in angularjs

You might want to use ng-include combined with ng-repeat.
Here is an simple example: http://plunker.no.de/edit/IxB3wO?live=preview
<div ng-repeat="dom in domList" ng-include="dom"></div>
Parent $scope will keep the list of partials loaded into the view.
And ng-repeat + ng-include will iterate over and display partials according
to the list.
When it is the right timing, you can append the partial into the dom list. e.g.
$scope.domList.push("chatbox.html");
(BTW, putting DOM manipulation into controller is not the angular way.)

Related

angularJS executing directive in memory and geting innerHTML

I have created a directive in angularJS as <print-note print-data='printData' id='notePrintDiv'></print-note> this directive will take some object and create a formatted html for printing, but I don't want to show the formatted html in my main html I want the formatted html for printout. so I was hopping if there is any way in angularJS where in I just create the element and pass the scope object to it like angular.element("<print-note print-data='printData' id='notePrintDiv'></print-note>"); or any other way and get its innerHTML.
P.S. I can also achieve the same with making outer html of directive template as display: none but that seems to be a bit hacky way.
The $compile service should be able to do this. Inject it in your controller where you have access to the scope (with printData).
var element = $compile('<print-note print-data="printData" id="notePrintDiv"></print-note>')($scope);
I had to achieve samething in a AngularJS app - I kept the directive for Print-Button and I kept id for the div or HTML block that was targeted to be printed and kept that div/html ng-show=false. I think it's one of the right way to get the required task done.

Angularjs: is this the right place to use a directive?

I've recently switched from jQuery to Angularjs and I am in the process of re-coding some pagination logic for the links ("Next", "Previous", etc.) that were written in jQuery-style Javascript previously.
Each link has an ngIf condition (for example, the "Previous" link won't show if you're on page 1) plus an ngClick event, which essentially updates a scope variable called $scope.pagination.position that determines which results are displayed in the table.
My original code was something like this (simplified for clarity):
Template
<a ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>
Controller
$scope.pagination = {
first: function() {
this.position = 0;
}
}
Then I learned more about directives, and how most DOM elements that aren't static HTML should be created using a directive. So I switched each link (since each has it's own display rules and behaviour on clicks) to its own directive, like so:
Template
<a pagination-first></a>
Directive
app.directive('paginationFirst', function() {
return {
link: function(scope,el,attr) {
scope.pagination.first = function() {
scope.pagination.position = 0;
}
},
replace: true,
template: '<a pagination-first ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>'
}
});
I'll cut straight to the chase : am I doing directives wrong? All that's happened, from my perspective, is I've flipped from having logic in my template to having a template in my logic, and I've defined the click event function in the directive rather than in the controller.
Is this even an appropriate time to be using a directive?
I'd like to learn best practices, so I'd love to know if I've missed the point and if the original templated-based ngIf and controller function approach was fine, even with longer and more complex ngIf conditions than the one shown.
If I want to add specific behaviors to a dom or dom list then I normally create a directive. As per angular js perspective the dom manipulation should only be done through directive (For me it is the best place, sometime I have to disobey this due to my lack of knowledge ). I specially found directive use full while creating a widget. In one of my project there was a part where a section is dedicated to display an image and also upload the image. I just use the directive on the top div, with the help of link function I attached the event handlers to various child dom. And as my project doesnot require an isolated scope (as this widget was all used in a single project and the outer scope was under my control) . So it worked like a charm. I cerarted the directive once. And used that widget through rest of the project as it's behavior and design (of the widget ) was same through out the project. For the pagination widget you can create a directive. Take the directive attibutes value as the input of the pagination parameters. Like calling script, limit offset. Container identifier to update the content. Then you can solely concentrate on the pagianation behavior. But from my experience (as I am also not so experienced in angular js), sometimes it becomes a little hectic to develop a directive and and use that throughout the project. As in some places we need to modify the behavior of the directive. And for this it may breaks elsewhere. But I know as I learn more I will be more efficient to handle this kind of situation. Hope my experience will help you.

Compile angular controller inserted by javascript dynamically?

Im working in an app that creates html dynamically. We started using angular so now we are trying to create html using angular.element() and defining a controller in it.
angular.element("<div ng-controller='myController'/>");
The problem is, that as it is created after the page renders (when an user clicks somewhere, for example), the html inserted is not working with angular, is like plain html. How can I solve it?
I use this way:
var template = angular.element(YOU_HTML_IN_STRING);
var linkFn = $compile(template);
var element = linkFn($scope);
angular.element(CSS_SELECTOR).html('').append(element);
If you don't have access to $compile. You can have a look here:
how can we use $compile outside a directive in Angularjs

How to add angular js module inside an existing application

I am trying to add an angular JS module inside an already existing code. I'm actually trying to add a new piece of code inside it. The workflow goes like this. When a button is clicked, the JS takes a template and adds it to the dom. In this added template, i would like to insert the new angular JS module. I am trying to atleast show hello world inside the module but haven't been successful.
Let's say when a button is clicked, i'm adding the below tag to the dom
<div patient-details ng-controller='patientDetailsCtrl' id='patientDetails'></div>
This is added through jquery or javascript. How do i make it work with the below directive
angular.module('patientDetailsModule',[])
.directive('patientDetails',function($compile){
return{
//restrict: 'E',
template: "<div>Hello World!!!{{name}}</div>"
};
});
You need to use $compile. $compile() will make sure all the Angular code in your html snippet will be compiled!
Here is an example.
http://plnkr.co/edit/7Wr3oAtvZ8cn31uFFEOs?p=preview
Send Message from nc-click to directive
Directive receives message, compiles html and appends new element
var template = "<div patient-details ng-controller='patientDetailsCtrl' id='patientDetails'>Element added</div>";
var newElement = angular.element(template);
$compile(newElement)(scope);
element.append(newElement);
// can also use element.replaceWith(newElement);

AngularJS and dynamic HTML

I've a HTML line with AngularJS directive that I am inserting dynamically through some jQuery controls that are being used since 2009. I cannot change this Control. However, I want to start using angular JS.
The generated HTML for a grid, column rating, is:
<div star-rating rating-value="1005" max="5"> </div>
What I want is to get this html compiled.
At present AngularJS is not able to see this dynamically generated HTML.
The only way I can think of is if I call a javascript function for each jQuery insert (The Grid control I am using can compile JavaScript function).
fnCompileDynamicHTML('<div star-rating rating-value=23 max=5></div>)
And function:
fnCompileDynamicHTML(html) {
return $compile(html)($scope) // or something?
}
I have followed what is mentioned here. However my application is complaining about:
App Already Bootstrapped with this Element.
Well it is not because it is a new object, and new inner HTML.
This is what I have in the loop of 100:
evt.row.cells[glbCandidateListColumnIndex.rank].innerHTML = '<div star-rating rating-value="' + rank + '" ></div>';
fnCompileDynamicAngularJS(evt.row.cells[glbCandidateListColumnIndex.rank]);
Please help.
You should either load Angular.js after the DOM compilation by your foreign library is done, calling angular.bootstrap (documented here), or you can force the compilation of that particular DOM node manually (harder!!!)
As an example:
angular.module('myApp', []).directive('myDirective', function () {});
angular.bootstrap(document.body, ['myApp']);
That should work already!

Categories