Render UI Bootstrap directive generated from custom directive - angularjs

I'm trying a pretty unusual approach of rendering a UI Bootstrap tab via AngularJS.
What I'm trying to do is:
Custom directive -- (That creates a) --> UI Bootstrap Markup -- (That renders) --> A Tab
I know that two directive shouldn't edit the same DOM node, so I used
- Priority attribute
- Terminal attribute
To allow my directive to be the first ones that are compiled and only then the Bootstrap directive should work on the DOM node.
My initial markup is something like:
<mytabcontainer>
<mytab title="Tab">Content</mytab>
<mytabcontainer>
That will convert to this Bootstrap markup
<tabset>
<tab heading="Tab">Content</tab>
</tabset>
I correctly transform the custom markup to the Bootstrap one, but it doesn't get correctly displayed.
Here is a Fiddle that shows my current progress: Fiddle
Unfortunately this is the only approach I can use because of previous decisions so please don't just tell me to directly use Bootstrap markup

Related

Compiling angular editor directive

Some (advanced?) angular is making my head hurt.
The goal is to extend a WYSIWYG HTML editor to allow users to insert certain angular directives into arbitrary HTML content. I have chosen medium-editor and its angular-medium-editor wrapper (but I'm not wedded to that if there are better solutions).
This Plunk shows how the editor directive is instantiated and activated (using an editable attribute). The toolbar is customised to include a button which adds a custom directive around selected text: <my-custom-directive class="bg-info"> ... </my-custom-directive>. (For demonstration, the custom directive wraps (transcludes) its contents in a button which triggers an alert when clicked).
I'm having problems with (re-)compiling the editor's content so that the directives inside the editor compile. Using $compile(element.contents())(scope) throws ngTransclusion:orphan errors for directives which uses transclusion. (I understand this is due to angular already having made the transclusion by the time the editor's link function is called.)
I cannot refactor all potential custom directives to not use transclusion.
What pattern can I use to successfully compile arbitrary editor content (which may include many different directives), ideally whenever that content changes, or at least when the editing is finished? Is this one of the "fringe cases" where the use of $compile is justified? If so, how do I use it?
This question and answer made me realise that the way to do this is to $compile only the inserted element when it is inserted, rather than recompiling the whole section.
Handily, rangy's classApplier module allows for an onElementCreate callback which can be used to compile the custom directive as it is added.
Here's the working plunker.

Angular ng-cloak wait child directive template to load

My problem is that when I use ng-cloak in pages that include elements which make use of directives with template code, ng-cloak does not wait for this template code to load and the page is shown incrementally and not as a whole (page elements first and after a while template code pops out).
I have tried to make a custom ng-cloak directive so that it won't remove element's ng-cloak class while any child element contains ng-cloak class but with no success. I thought this one would be a common issue, but I have not managed to find a solution online. Any help appreciated!
I don't think ngCloack was designed to cloak your content until everything is loaded. It is designed to prevent your content to be rendered in its raw form, what with expressions and all.
However, according to the documentation, it might work on the body element, but I haven't verified it myself:
The directive can be applied to the <body> element, but the preferred
usage is to apply multiple ngCloak directives to small portions of the
page to permit progressive rendering of the browser view.

Angular UI Bootstrap Typeahead directive creates a different DOM element for every instance

I am trying to use the Angular UI Bootstrap Typeahead directive in a few tables. These tables could have 0 to thousands of entries in them and when I attach the typeahead directive to each <input> in my table rows, it creates a DOM element for each one!
If I inspect the DOM after the page loads, I can see hundreds of <!-- ngIf: isOpen() --> on the body of my HTML. The thing is, all of these typeaheads use the same source list to supply the typeahead with it's data, so techincally I should only need 1 typeahead element that is just re-used on each input in my tables.
Currently, I do not see a configuration attribute on the typeahead documentation that allows us to create 1 shared typeahead element. Is there any way to easily get this functionality to work without altering the angular bootstrap javascript myself or without building the typeahead directive from scratch?
EDIT
To make things even worst, I have pagination on my tables and if you change between "pages" of the tables, the directive re-creates more <!-- ngIf: isOpen() --> for every new instance of that page. Therefore, if the table starts out with 100 items per page out of 10 pages, if the user clicks on each page of the pagination at a time, they will end up with 1000 <!-- ngIf: isOpen() --> on the page.
Implement some kind of view/edit state transition on the cell that contains the typeahead directive.
Use something like an ng-if="row.isEditing" on the input with the typeahead directive. As-is, your table is going to create a DOM element for every row unless you tell it not to. When you click into the cell to edit, then change the edit mode state, and the DOM element for the typeahead will be loaded on-demand.
This plunker is (somewhat) like the approach I have in mind, but it implements a whole edit row instead: http://plnkr.co/edit/ivJQ0C
Notice in that example that the DOM element for the edit row does not exist until the condition of the ng-if is met.

How can I tell Angular not to compile child nodes when using $compile

The situation is I have an HTML structure somewhat similar to this:
<div class="dynamicDirectiveGoesHere">
<p>{{SomeExpressionThatDiffers}}</p>
</div>
I need to display a bootstrap http://angular-ui.github.io/bootstrap/ popover when the text within p has an ellipsis. That's why I'm adding the popover attribute dynamically. I can get the popover to display using $compile, but the problem is the text within {{ }} goes away. I can't use the template trick since I don't really know what the template will be since the popover will happen on several different child tags that have different templates. So that's why there is the need to only $compile what's in div, and not in the child element (p tag). Is this possible with angular?
You can add the property terminal to your directive and adjust the priority to fit your needs.
terminal: true prevents other directives from getting instantiated and is used by for example the ng-repeat and ng-if directive.
You can read more here https://docs.angularjs.org/api/ng/service/$compile (scroll down to terminal).

AngularStrap tabs load html fragment

I am currently working on an AngularJS project with Twitter Bootstrap, and am trying to shift my Bootstrap directives into Angular. I decided on AngularStrap as it provided support for Bootstrap-Select (which I wasn't sure was the same for AngularUI). The tabs example only covered static html though. Is there any way via AngularStrap or AngularJS to load html fragments dynamically, so that it is only called when the tab is clicked? My html fragments need to execute javascript as well.
My reason for doing so is two-fold. First is that each tab contains quite a lot of content, and I do not wish to load all the tabs at once, which will slow down the loading. The second reason is that I prefer a more modular approach to my source code and not put everything on the same html file.
You can use the ng-include directive to load html fragments.
Using the AngularStrap Tab's example you can switch out the static content with the url to retrieve the html fragment. Here is an example based on the AngularStrap Tab example with these key changes:
1) $scope.tabs now has a page property instead of content pointing to either template1.html, template2.html, or template3.html.
$scope.tabs = [
{title:'Home', page: 'template1.html'},
{title:'Profile', page: 'template2.html'},
{title:'About', page: 'template3.html'}
];
2) An ng-include is added to display the currently selected tab's page.
<div ng-include src="tabs[tabs.activeTab].page"></div>
Note: I have the ng-include outside of the ng-repeat so each tab's page contents won't be loaded (even if not displayed).

Resources