How to: Angular Tree? - angularjs

Hi all you experts out there.
My testing area: http://plnkr.co/edit/ddJT1e4a8L5NTSIVNTk7
I am trying to visualize hierarchical data in a tree-form with Angular, even though i'm using some samples to aid me in my quest (like http://jsfiddle.net/alalonde/NZum5/ and http://jsfiddle.net/brendanowen/uXbn6/8/) i fail.
As soon as i place the recursive element ng-include inside the ng-repeat in side the template it self, the memory usage of its browser-window goes through the roof and effectively hangs the browser. But the available tree-sample i could find are doing just that.
What am i missing?

You need to use the same variable name in the template. The current node is called node in the controller then child in the template.
This cause the template to render the same node over again.
It works fine if you use the same variable name :
<li ng-repeat="node in node.children" ng-include="'node.html'"></li>
See it in action here : http://plnkr.co/edit/mjfdSEDcMK8kGCRjS6V6?p=preview

If anyone here wants to avoid having the extra ng-repeat outside of the template (where it kind of includes stuff from the template anyway), here's a fiddle showing how to do it:
http://jsbin.com/hokupe/1/edit
Also here's a blog post and a 10-15 minutes video on how it works:
http://gurustop.net/blog/2014/07/15/angularjs-using-templates-ng-include-create-infinite-tree/
Sample Code:
<script type="text/ng-template" id="treeLevel.html">
<ul>
<li ng-repeat="item in items">
<input type="checkbox"
name="itemSelection"
ng-model="item._Selected" />
{{item.text}}
<div ng-include=" 'treeLevel.html'"
onload="items = item.children">
</div>
</li>
</ul>
</script>
<div ng-include=" 'treeLevel.html' "
onload="items = sourceItems">
</div>

Related

cannot get angular-marked to work against $scope variable

I have a controller that basically assigns text to a $scope variable like this;
$scope['Model'] = ["markdown text 1", "markdown text 2"];
And then I try to use marked on it within a view, like this;
<div ng-repeat="n in Model">
<div marked="n"></div>
</div>
or
<div marked>
<div ng-repeat="n in Model">
{{n}}
</div>
</div>
I just get {{n}} as the output, verbatim. Marked never runs, never does anything to it. I'm completely baffled. I know the text is fine.
I have tried all of the examples and nothing seems to work. It does work if I put in static, hard-coded text between <marked> directives - but nothing dynamic.
The only way I've been able to make anything work is to forcefully use the marked(n) function within the controller - which is far less than ideal and certainly not what I'm wanting to do.
After a lot of trying, I think that the way angular-ui-router is involved may play a part. Here is the HTML structure;
index.html
<div class="content-body">
<ui-view />
</div>
content.html
<ui-view />
entry.html
<div ng-repeat="m in model">
<div marked="m"></div>
</div>
I think I've got it.
<div ng-repeat="m in model">
<div marked="m"></div>
</div>
Works as per this fiddle: https://jsfiddle.net/jorgthuijls/q244srfh/
See, ng-repeat creates its own scope. So, you can bind the m variable to the marked directive.
I got it to work with angular-ui-router too: https://jsfiddle.net/jorgthuijls/ck8by0ze/

Why does dynamically creating togglable tabs cause duplicate ngSwitchWhen divs in AngularJS?

I'm using Bootstrap and AngularJS to dynamically create clickable tabs in HTML. More specifically, I'm using the ng-switch and ng-switch-when directives to control which tab's content is displayed at any given time.
PLEASE NOTE: Below you'll notice I use a controller for DOM manipulation. I realize the "AngularJS way" to perform DOM manipulation is with directives (and not controllers). I'm not one to deliberately violate good coding practices, but since I'm trying to understand this exact issue I've stumbled across while learning about controllers and directives, I'm asking you to only consider using a controller for now since that's what I believe may be contributing to the issue.
If you keep reading you'll notice I've already found a directive-based solution (i.e., the "AngularJS way"), but I'm specifically asking about the issue that occurs when using a controller and ng-switch-when.
The Problem:
The issue I'm facing is as follows. When I append div tags (containing a ng-switch-when directive) onto a container, and then compile these tags using $compile, somehow the content of each tag using ng-switch-when becomes duplicated. To keep this question from becoming too long, I've placed the idea behind what the code's expected behavior is in the comments of the first JSFiddle below, which demonstrates the issue.
JSFiddle: What I have now -- Uses controller for DOM manipulation ("non-AngularJS way"), results in unexpected duplication of tags using ng-switch-when.
An in-depth description of how to reproduce the duplication is as follows. Upon creating a new tab, it appears that the previously clicked tab's content (within the div containing ng-switch-when) is duplicated. You can see this happen by loading the above JSFiddle, right-clicking the Default text, selecting Inspect element, and then pressing Analysis > Create Tab in the tabbed pane. You'll see there are now two div tags with ng-switch-when="Default" instead of just one. If you continue to create new tabs, this pattern continues for view1, view2, and so on. Alternatively, just click Analysis > Create Tab 5 times and then look at the duplicates in Tabs 1-4. Either way, you'll see the previously selected tab's contents are doubled whenever a new tab is created.
Frustrated by this unexpected behavior, I went ahead and implemented a directive-based solution using AngularUI's Bootstrap components, which creates custom directives for tab components, to gain the desired functionality.
JSFiddle: What I want to happen in the above JSFiddle -- Uses directives for DOM manipulation ("AngularJS way").
However, as I've mentioned, I want to know why my first JSFiddle is not working. See below for formal question.
Points to consider:
The issue may be related to scope, the order the scripts are loaded, or possibly a logic error.
Perhaps it's something AngularJS simply cannot do. I'm aware that compound transclusion no longer works as of v1.2, and this issue may be related.
So my question is as follows:
Why is this duplication occurring? Is there a way to prevent/workaround the duplicates from being created? Remember, I wouldn't use this in practice, I'm just curious if this issue can be solved at all.
PLEASE NOTE: I am not looking to resolve this using jQuery, such as this jQuery solution. Although I'm aware AngularJS uses a lighter version of jQuery called jqLite under the hood, I'm looking for a "pure AngularJS" solution.
HTML:
<!-- Include CDNs (see JSFiddle above) -->
<!-- Wrap in HTML tags, include DOCTYPE -->
<body ng-app="myApp">
<div class="panel panel-default" ng-controller="myCtrl">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active">
<a class="h4">My App</a>
</li>
<li class="dropdown">
<a href="" class="dropdown-toggle h4" data-toggle="dropdown">
Analysis
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<button id="createTabLink" class="h4" ng-click="createTabClick()">Create Tab</button>
</li>
</ul>
</li>
</ul>
</div>
<div class="panel-body">
<div class="panel panel-default">
<div class="panel-heading">
<ul id="createTabDiv" class="nav nav-tabs"></ul>
</div>
<div class="panel-body">
<div id="viewWrapper" ng-switch="" on="viewSelected">
<div id="Default" ng-switch-when="Default">Default</div>
</div>
</div>
</div>
</div>
</div>
</body>
JS:
angular.module('myApp', []);
function myCtrl($scope, $compile) {
$scope.count = 0;
$scope.viewSelected = "Default";
$scope.tabClick = function($event) {
var tab = $event.target;
var viewName = tab.getAttribute("value");
$scope.viewSelected = viewName;
};
$scope.createTabClick = function() {
$scope.count++;
var viewName = "view" + $scope.count;
var createTabDiv = angular.element(document.querySelector('#createTabDiv'));
var viewWrapper = angular.element(document.querySelector('#viewWrapper'));
var $comp1 = createTabDiv.append('<li class="active"><a class="h4" value="' + viewName + '" ng-click="tabClick($event)">Tab ' + $scope.count + '</a></li>');
$compile($comp1)($scope);
var $comp2 = viewWrapper.append('<div id="' + viewName + '" ng-switch-when="' + viewName + '">' + viewName + '</div>');
$compile($comp2)($scope);
$scope.viewSelected = viewName;
};
}

repeat inside dynamic popover - angularjs and ui-bootstrap

I'm trying to show the user a list of items inside of a popover that is all inside an ng-repeat. I'm using angularjs as well as the ui-bootstrap package (http://angular-ui.github.io/bootstrap/).
HTML
<div ng-repeat='session in sessions'>
<p popover="{{session.items}}">view items</p>
</div>
This will show the array session.items for each session, which contains the information I want to show. However, this shows the brackets of the array as well.
Does anyone know a clean way to do this?
any help would be appreciated, thanks in advance
From ui-bootstrap site you can read
uib-popover - Takes text only and will escape any HTML provided for the popover body.
So if you provide session.items you will get string '[my array content]'. In my opinion you need to use uib-popover-template where your template would be like
<div ng-repeat='session in sessions'>
<p uib-popover-template="'urlToMyTemplateHere'">view items</p>
</div>
------ Template
<div ng-repeat="item in session.items" ng-bind="item"></div>
uib-popover-template takes an url to the template so you have to create file for it to be fetched or try this approach ( I don't really like it but just for testing )
<div ng-repeat='session in sessions'>
<p uib-popover-template="'iamabadapproachtemplate.html'">view items</p>
</div>
<script type="text/ng-template" id="iamabadapproachtemplate.html">
<div ng-repeat="item in session.items" ng-bind="item"></div>
</script>

Jasmine-jquery, testing generated GUI for (nested) directives

I recently started using jasmine-jquery (1.3.1) together with angular (1.0.6) and need an advice on testing GUI.
I have some view for controller, which has angular directives, like 'view.html':
<div id='main-div'>
<div my-directive objects-to-iterate='someScopeObjects'>
<span id='default-span'>some default text</span>
</div>
</div>
, and in JS a directive "myDirective" is defined, having template-by-url 'myDirective.html' which includes ng-repeat and makes some nested div (row) foreach in objectsToIterate.
I load fixtures for 'view.html' and 'myDirective.html'.
Now I would like to test that during user interaction there are really some elements (rows) in 'myDirective' repeated block.
Beginning was simple:
var div = $('div#main-div');
div = $compile(div)(scope);
scope.$digest();
expect(div).toBeVisible();
And now I'm stuck. Cannot get access to "generated" source of 'myDirective' element. If I use
div.find('whatever-selector-for-element-my-directive'),
I get
<div my-directive objects-to-iterate='someScopeObjects'>
<span id='default-span'>some default text</span>
</div>
If I try to compile+digest this html against scope, I still get same markup. How could I get "generated" source, looking like (pseudo)
<div id='my-directive-content'>
<div id='object-1'>blah</div>
<div id='object-2'>blah</div>
</div>
in order to check if there are N rows visible to user?
someScopeObjects in scope exist and are valid.
And my next step is actually testing same thing for directives nested inside of 'my-directive', so I somehow have to get access to the scope of 'my-directive'. How? :)
Thank you.

Angularjs list and detail view

this is my basic scenario. For a list of items (summary view) i want to show details view of item that got clicked on the same page.
I took this jsfiddle example and transformed it into this jsfiddle. If you look at the behavior it does work for first time but it is not consistent.
Maybe someone can help me with this, or suggest a better approach. I would like to have a different controller for managing the list and a different controller to handle the detail view.
One way of transforming the example (provided that you want to use ngSwitch) would be:
<ul ng-controller="ListController">
<li ng-repeat="item in items" ng-controller="ItemController">
<div ng-click="open(item)">{{item.content}}</div>
</li>
<hr>
<ng-switch on="anyItemOpen()">
<div ng-switch-when="true">
<div ng-controller="ItemController">
{{opened.name}}: overlay: tweet, share, pin
</div>
<a ng-click="close()">close</a>
</div>
</ng-switch>
</ul>
And here is the working jsFiddle: http://jsfiddle.net/pkozlowski_opensource/sJdzt/4/
Your jsFiddle didn't work since you were trying to reference item created in the ngRepeat scope (and thus not available outside of the ngRepeat).

Resources