angularjs directive that wraps & modifies arbitrary content which includes ng-repeat - angularjs

I am trying to build a directive that:
wraps arbitrary content
prepends anchor tags to all 'h4' elements found in that content
Here's a plunker of what I have so far.
Seems to work fine if wrapped content is static but:
entire wrapped content is repeated twice
is my invocation of transcludeFn effectively repeating work that ng-trasclude is doing? Do I need to replace ng-transclude with own impl?
'h4' tags generated by nested ng-repeat are not found by the directive
looks like 'clone' passed to cloneLinkFn in transcludeFn is pre-ng-repeat execution. thought that $compile(clone)(scope) would then produce post-ng-repeat version but that did not work either it seems.
I'd really appreciate a tip or explanation of what I am doing wrong.
thank you,
-nikita

Related

How to render html formatted content in ng-grid?

I have jsonData which consist of HTML content. I want to render that HTML in ng-grid. The content is not rendered however -- it only shows up in normal string format.
Below is the plnkr which shows all the code:
http://plnkr.co/edit/RlWyDqCUUR15dmLM7ePV?p=preview
There's a few things going on here:
Not related to your exact issue, but you've got nested single quotes in your firstName field. You need to do some escaping or your ng-click expression is going to break. So instead of ng-click='show('F:/cox/main.html')' you can do ng-click=\"show('F:/cox/main.html')\".
Also not related to your exact issue, but if you want to access properties on your controller's scope from inside UI-Grid you need to use appScope. The docs on it are here: http://ui-grid.info/docs/#/tutorial/305_appScope
The main problem with getting HTML provided from inside your app to render is Strict Contextual Escaping.
Strict Contextual Escaping (SCE) is enabled by default in Angular and escapes any arbitrary HTML to prevent things like XSS and clickjacking. In order to get your HTML to show up you need to trust it, and bind it with an HTML bind expression.
You can use the $sce service to trust the HTML. Just iterate over your rows and do $sce.trustAsHtml(row.firstName). (You can read more about SCE here: https://docs.angularjs.org/api/ng/service/$sce) Then you will need a custom cell template to bind the HTML. The simplest one looks like this:
<div class="ui-grid-cell-contents" ng-bind-html="COL_FIELD"></div>
COL_FIELD will get transformed by UI-Grid into the proper binding for your field. Now the problem is that your ng-click directive doesn't get compiled. You need to use a directive that will take your custom HTML and compile it. You could roll your own, or use something like this library to do it for you: https://github.com/incuna/angular-bind-html-compile
The main thing to keep in mind is being able to actually trust the source of your HTML. If you can't be sure then going about this another way (i.e. by not providing HTML inside your data set) would be better.
I've modified your plunker to show all this working together: http://plnkr.co/edit/MgLoeGBoRTi2fF3e6pia?p=preview

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)

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.

Dynamically creating an ng-switch inside a directive

I'm trying to create a new directive that selects among several children using an ng-switch. This example is not much more than creating an ng-switch inside a directive, but eventually the directive will have more display sugar and some automatic functions so it seemed that creating a directive was the right solution.
My progress so far is in this plunker:
http://plnkr.co/edit/yeCiIOCQswYJHyTozQUZ
The $compile I'm doing seems to be evaluating the switch, and determining that the value doesn't match any of the when clauses which shouldn't be true. You can see that by inspecting the elements in the rendered picker.
I'm also concerned that calling $compile at this stage seems to have thrown away the item list, so it seems like I'm barking up the wrong tree.
How do I get the transcluded content to re-evaluate within the current state?
Update
I think I was barking up the wrong tree. Mathew's answer got me started in the right direction, so it was a big help.
As far as I can tell trying to construct a directive (ng-switch) inside a directive is a bad idea. In the previous plunker when the compile happened the template was changed permanently. That means if I changed the which parameter it wouldn't update. That's what was smelling funny to me in the first place.
Here is a revised plunker:
http://plnkr.co/edit/WUVgdXjwedxO4356321s
In this case, there's a watch on the 'which' value that refires the transclusion. That function removes the previous entry (if any) and adds the new one. There's a couple added benefits.
First I removed the 'item' directive. There's no reason for it to exist, since I'm just looking at the class. Second, I used $animator to do the list manipulation. That means you can add ng-animate to the picker and get animation effects.
I hope that helps someone else looking at this question.
There were two problems with your code:
1) Your template had which being evaluated so your on was becoming the number 1, instead of the variable you wanted which is "which":
template: '<div><div ng-switch on="which"></div></div>',
2) When you used compile, you needed to pass in the $scope like so:
$compile(sel)($scope);
Here's an updated plnkr for you: http://plnkr.co/edit/Q6ViJBvkLwQRgUKYMfS9?p=preview

AngularJS Directive is not linking when using an input in the template to replace an input in the html with the type attribute specified

I'm having a really specific, strange issue.
We have to code our app to support IE8. We are wrapping the Angular-Bootstrap's Typeahead directive in a template to encapsulate a lookup widget with some extra functionality.
The issue we're having is that our directive template looks like this:
'<input ng-model="typeaheadValue" typeahead="xxx.code as formatXxx(xxx) for xxx in searchXxxs($viewValue)">'
Inside the link function in the directive we are simply calling replace: true and passing in some values to the scope.
This works beautifully in both IE8 and Chrome.
Now, the REALLY strange part is that, for IE8 only, if we say in the HTML in which we're using the directive:
<input type="text" search-directive>
It will not ever get into the link function. If i take the type="text" off everything works perfectly.
I created a simple JSFiddle to mimic what we're doing with a really basic test. Unfortunately for me the JSFiddle doesn't work in IE8 - but this is basically what we're doing. This can be found here: http://jsfiddle.net/lungsponge121/8xGuF/ (this is my first fiddle, not sure if it's editable or not)
After fighting with it for hours I've found the following: if i keep the html as (input type="text") and i replace the directive template input element with label or textarea it works fine, but when i use input it does not work at all. I also removed all of the typeahead code from the template and found that in IE8 it still doesn't work. The IE8 console did nothing for me and just gave me the standard illegal operation.undefined error.
I had somebody I work with help me debug my code and right now we're wondering if this is a bug. Has anybody else run into this - I'm thinking of submitting this to the Angular people as I can't find out how to get around this.
One thing jumped out at me: your directive is doing replace. The markup of the template you are replacing it with does not have the type="text", whereas the original markup does. I have noticed that replace does a sort of merge, and it may be getting confused when trying to merge or replace some HTML that has an attribute with a template that does not.
Interestingly, you don't really need to replace the original markup with a template at all. That is one use of directives, but not the most powerful in my opinion. Take "replace" off and remove the template entirely, if you're just trying to extend the typeahead anyway. In my own projects, for example, I might put several different directives attributes on a single element and they all add their own flavor of extra functionality to the existing element. All of them have a reference to the same $element, you just have to be careful that they don't conflict.
I wrote the rest of this before I looked at the fiddle first, sorry.
I'm on a chromebook so I can't test the IE8 thing, but if I recall IE is very picky about HTML attributes. Have you tried any of the following:
Alternate directive specification, as in data-search-directive
Make sure the directive has a property, as in search-directive="", even if you don't use it
Is the template cache really necessary? Have you tried this just by putting the template in a template property of the directive?
Is IE8 happy without "use strict"?
Have you seen the angular ui bootstrap alternative typeahead? At the very least, looking at what they did may give you ideas of how to do what you are trying to do.

Resources