angularjs - inserting $compile-d html - angularjs

(Note that you don't need to know about datatables for this one..)
I'm creating a directive to handle DataTables. What I'd like to do is have an actions column with two icons - edit and delete. Those icons should response to an ng-click.
Datatables allow you do this by providing a callback for a given column defintion (mRender). This callback returns an html string for that cell value, which is inserted into the DOM instead of the actual cell value.
Checkout this plunker. The two important functions are highlighted:
renderActionIcon - my implementation of the callback mentioned above. It generates the HTML string that I'd like in the cell.
registerNewHtmlWithAngular - function where I ostensibly let angular know about the ng-clicks that I need to register for that column.
What should go in registerNewHtmlWithAngular?
If $compile the html, angular adds the appropriate click event listeners and gives me back an element, but since the Datatables function expects HTML, those registered elements will not be added to the DOM.
Any ideas? Thanks folks!

After a day of keyboard banging:
There does not seem to be a way to $compile into html and have it be usable in the DOM. It has to be an element that's inserted into the DOM, so that event listeners are not lost.
The way I solved the problem in this particular scenario is to just insert the HTML, and run $compile on the new HTML later.
Datatables makes this easy with the fnCreatedRow callback, which, after a row is rendered, passes back a row's Element and lets you do whatever you want with it. In this case I ran $compile over the row and returned it back to Datatables. See the rowCompiler function in the updated plunker.

Because datatables uses innerHTML = mRender(...) to insert an html (so it anyway will convert anything to string and you will lose the results of $compile function. I guess there is no way to do it with mRender without hacks - for instance it is possible to slightly change datatables so it will use $.append instead of innerHTML. But this is not a good practice so in our project we use usual jquery bindings instead of ng-click.

Related

Include $compiled code through ng-bind-html

I have a directive that takes "previewHTML" as a $scope variable input. I simply want to insert this HTML into a div in my directive template.
I've been working on this problem for a week trying methods from using ng-bind-html, {{}}, $sce, $compile and everything I could think of inbetween; I'm at a loss of why this is so painfully difficult.
The closest I've gotten is to create a scope using $rootScope.$new(true), then attach the variables I need "newScope.value = 'myvalue'", then use $compile to compile the preview HTML and in the cloneAttachFn (which I assume is a callback for when it's finished compiling) I set the previewHTML scope variable, which is included as '< ... ng-bind-html="previewHTML">'
The html without the scope applied comes out fine, but the bindings aren't set. The weird thing is that in the object they are set, but in the outputted HTML they are not; meaning the element has been created, but the bindings haven't been set yet. Unfortunately, Angular won't take a jquery HTML object in ng-bind-html, even though it returns a jquery object in order to maintain the bindings in the HTML.
I'm going to have to use a timeout for now... but does anyone know how to do this very rudimentary thing of including html, that has binding, in a directive template (it has to come from a $scope variable, or at least be generated outside of the directive by the user of the directive)? [is it even async? The documentation is frustratingly unclear]
(Honestly, I'm tens of thousands of lines and a year in and the more I use Angular the more I'd rather use plain JS)

AngularJS - Find Element by Attribute Value

I'm trying to use AngularJS to query the DOM of my view. I need to get all of the elements that have the attribute 'data-placement' with the value of 'top'. In jQuery, I would do this:
var elements = $('[data-placement="top"]');
However, I don't know how to do it with AngularJS. Can someone tell me how to do this?
Thank you!
In AngularJS you won't do direct DOM manipulation from the controller, you should create a directive to to that. Inside the directive you can use JQuery as you wish.
Anyway you I think you can use angular.element() with a JQlite selector, here's the documentation of angular.element.
Example:
// find('#id')
angular.element(document.querySelector('#id'))
From your question I understand that you wanted to find elements with atrributes requiring certain condition in that case we can use $document.
You can find more info about it here.
So coming to your requirement, you can find it by.
$document.find("[attribute-name='attribute-value')");
That will return the element object wrapped in jQuery or jqLite. But to use it you need to inject $document into your controller. So you continue angular related operations with it.
Another approach to this is angular.element.
angular.element.find("[attribute-name='attribute-value')");
But this will return element with wrapped raw DOM element or HTML string as a jQuery element. you can find more info about it here
I prefer to use $document as Angular operations can be done using the first method.

Angular directive failing silently (directive rendered too soon?)

I have an Angular app with a custom element directive (<uber-table>). <uber-table> is supposed to take a collection of objects, render them into a <table> and add some functionality (click row to toggle the underlying object as selected, search box for live filtering, action links on each row with customized click callback(s) for each object). I created a Plunker with the relevant code. Please note that Plunker is giving an error about map (Object [object Object] has no method 'map'), but locally I am not getting any errors.
The post-link function's element parameter is not the <uber-table> element as I expected. Instead it is the template's <div class="uber-table"> element. This is preventing me from extracting data from <uber-table>. What am I doing wrong? Any help will be much appreciated.
Here's a rundown on some of the issues.
First main issue is you want existing content already within the uber-table markup to exist, as well as a new template. Unless told otherwise the existing content ( columns) in this case will be overwritten. In order to include existing content in a directive that has a template, you need to set transclude:true then identify within template where this existing content needs to be placed using ng-transclude on element that will be parent of the content.
Here's demo with transclude fixed
New problems arise now where you are trying to use jQuery to loop over columns and return angular attrs => column.attrs . This throws undefined error.
I haven't tried to unravel this enough to sort out the columns issues yet. They should likely be handled by directive of their own
UPDATE: here's slightly revised error free version using jQuery to parse column count. I'm afraid am still confused a bit by structure of this. I don't see need to use jQuery to parse colunms, this could be converted to directive or pass column definitions into main directive from controller scope
After trying several things and looking at the documentation again, I finally got it working. The solution was to put the post-link function inside the compile function. Also I had to update my isolated scope to use =, set replace to true and set transclude to 'element'.
I updated Plunker if anybody wants to see the changes. The Plunker version isn't working, but since it is working locally, I'm not going to spend too much time on it.

How to force attribute directive to wait for element directive to evaluate?

I have two custom directives define in my project:
<include-partial> - element directive which gets template via $http service, compiles it and inserts into DOM
on-show="someFn()" - attribute directive, which should call some function when the element becomes visible
I'd like to combine the two, so that I could write code like this:
<include-partial on-show="init()">
This, however won't work, as the content of <include-partial> is fetched asynchronously, so on-show has no DOM to attach to.
Do you have any ideas how to tackle this? How could I force on-show to wait until the <include-partial> is done compiling the DOM?
Thx in advance!
In your case "on-show" is never trigger because "include-aprtial" tag don't changes it's visibility. Try to hide "include-partial" in link function and show on content load.

AngularJS - ng-bind-html-unsafe and ng-model Problems

I have the following line in my html:
<div ng-bind-html-unsafe="departmentConfig" class="control-group"></div>
and I use a $resource get to retrieve the HTML, assign the HTML to $scope.departmentConfig, and then the view is updated perfectly. The HTML that is assigned to $scope.departmentConfig contains form elements, with ng-model attributes in it, but when I change the values in these input elements, they don't update the $scope model at all.
This is what I have tried, based on a lot of other internet posts, and it isn't working:
$resource('resources/sources/departments/:mappedName', {
mappedName:departmentKey
}).get(function(departmentConfig) {
// This will call another function that will build a chunk of HTML
$scope.departmentConfig = $scope.buildDepartmentConfigHtml(departmentConfig);
// This is my feeble attempt to access the element, and bootstrap it to include the items in the $scope model.
var $departmentConfigContainer = $('#departmentConfig');
angular.bootstrap($departmentConfigContainer, ['sourcemanager']);
I have even seen some jsFiddle examples where this appears to be working, but mine isn't. Am I calling bootstrap too soon? I also tried putting a $watch on $scope.departmentConfig like this:
$scope.$watch('departmentConfig', function() {
var $departmentConfigContainer = $('#departmentConfig');
angular.bootstrap($departmentConfigContainer);
});
but it didn't work either. I bet there is an easy explanation to this, I just can't seem to get the input elements with ng-model that are loaded after page compile to get bound to the model. Any help is appreciated, this is the last piece of functionality I need to get working on my page. Let me know if you need more information about my configuration as well.
So, simply put, how can I force a section of the DOM to recompile after I know it has been loaded?
UPDATE
Here is a jsfiddle outlining what I would like to do: http://jsfiddle.net/j_snyder/ctyfg/. You will notice that property two and three don't update the model, and I am calling bootstrap on the outer div, hoping that will include those in the model binding. This is the first time I have posted to jsfiddle, please let me know if you can't see the example.
ng-bind-html is made for regular HTML, not compiling new angular elements.
You will have use the $compile service.
Here is how you would edit your current example to work: http://jsfiddle.net/andytjoslin/ctyfg/21/. But this way ends up being bad, because you have to do DOM manipulation in your controller.
You just need to create a directive that will basically do what you wanted ng-bind-html to do: http://jsfiddle.net/andytjoslin/ctyfg/22/

Resources