ng-repeat inserting empty anchor tags - angularjs

I'm trying to create a menu using angular. A menu item can have children requiring another ng-repeat to print the sub nav items. I'm noticing some strange behavior when attempting to insert an anchor tag within the 2nd ng-repeat.
Link to fiddle: http://jsfiddle.net/npU7t/
<li ng-repeat="sub_menu_item in menu_item.sub_menu">
<a href="">
{{ sub_menu_item.title }}
</a>
</li>
With
{
title: 'menu item with children',
sub_menu: [
{
title: '<-- empty anchor tag???'
}
]
}
Results in
<li ng-repeat="sub_menu_item in menu_item.sub_menu" class="ng-scope">
<-- empty anchor tag???
</li>
Where the did duplicate / empty anchor tag come from? How can I prevent it from being created?
Appreciate the help!

This isn't a bug with Angular, but rather how you have your markup.
UPDATE:
The issue is actually the nested <a> tag, not the <ul> tag.
<a href="">
<span class="title">{{ menu_item.title }}</span>
<ul class="sub-menu" ng-if="menu_item.sub_menu">
<li ng-repeat="sub_menu_item in menu_item.sub_menu">
<a href="">
{{ sub_menu_item.title }}
</a>
</li>
</ul>
</a>
In fact, if you remove Angular from the equation altogether, you will see that the extraneous <a> tag is still added to the DOM: http://jsfiddle.net/jwcarroll/cXkj4/
If you get rid of the nested <a> tag, then the extra element will disappear.
<a href="">
<span class="title">{{ menu_item.title }}</span>
</a>
<ul class="sub-menu" ng-if="menu_item.sub_menu">
<li ng-repeat="sub_menu_item in menu_item.sub_menu">
<a href="">
{{ sub_menu_item.title }}
</a>
</li>
</ul>
In both HTML 4.01, and HTML 5, having a nested <a> tag is a no no.
The simplest possible recreation of the problem I could come up with is this bit of markup:
<a href="">Outer
<p>Blah
Inner
</p>
</a>
Because you can't nest <a> elements within each other, the browser is doing it's best to recreate your intent while keeping the DOM clean. What you end up with is this:
Outer
<p>
Blah
Inner
</p>
This makes sense when you realize what the browser is trying to do. The browser is trying to do three things:
Keep Outer, Blah and Inner text elements inside hyperlinks
Contain Blah and <a>Inner</a> inside a single <p> tag
Ensure no <a> tags are nested within each other
The only sensible way to accomplish all three of these is to wrap both Outer and Blah text elements in separate <a> tags in a way that isn't nested. This is the closest approximation to the original intent without breaking the DOCTYPE rules.
I hope this helps.

Very strange. It doesn't appear with any tag besides <a> (like <p> or <div>). It looks like an outright bug to me - I'd submit a proper bug report.

Related

angularjs showing a tab if condition is met in ng-repeat

Is it possible to split this ng-repeat loop in angularjs? I have a set of tabs and the data is found in workspace in workspaces. I want to basically check a condition ng-if "savedSettings=='account'", show specific tab. ng-if"savedSettings=='shelf'" show tab. How can I work this conditional render in?
There are four tabs found in this workspace.
<li ng-repeat="workspace in workspaces" class="workspace.activeclass">
<a id="workspace.tabid" href="{{workspace.hrefid}}" data-toggle="tab"> {{workspace.name}}
</a>
</li>
Use the ng-show directive:
<li ng-repeat="workspace in workspaces"
class="workspace.activeclass"
ng-show="workspace.tabid == savedSetting">
<a id="workspace.tabid" href="{{workspace.hrefid}}" data-toggle="tab"> {{workspace.name}}
</a>
</li>
For more information, see
AngularJS ng-show Directive API Reference

ng-class not working in one situation. What are some possible cause of this?

I'm stuck. Cannot figured this out. This question is very simple to show, but I'm not really sure how to put it as a question, therefore I'll try my best.
First, here's the layout of my whole app (The problem lies in the Header.jsp):
<jsp:include page="../home/Header.jsp" />
<jsp:include page="../home/Modals.jsp" />
<div data-ng-view data-save-scroll-position data-position-relative-to-menu></div>
<jsp:include page="../home/Footer.jsp" />
The problem is very simple. I have the following data-ng-class in the data-ng-view section that change a tab to active if something is true (The problem is it won't work in one scenario even though it displayed true in the tab name):
<ul class="nav nav-tabs">
<li role="presentation" data-ng-class="tab.isSelected ? 'active' : ''" data-ng-repeat="tab in ctrl.tabs"
data-ng-click="ctrl.fetchBIReports(tab)">
</li>
</ul>
In the JSP that use data-ng-include for the above markup, there's a side nav to change to this page. Once clicked this side-nav, it highlighted the tab 'active' as expected (trying not to include the whole jsp):
<div class="side-navbar">
<ul>
<li class="{{ ctrl.navigate.path == 'bi/schedule' ? 'active-link' : 'normal-link'}}">
Schedule Reports
</li>
</ul>
</div>
<div class="content-right" data-ng-include="ctrl.navigate.path"></div>
content-right includes the JSP mentioned in the second markup.
So far, so good. Here's a demo of it working (including both side-navbar and content-right):
The problem is, in my Header.jsp, there's a nav bar that takes me to the same page. If it is clicked from a different page with different controller, then it works. But if I'm in the current controller and click that nav bar link, then data-ng-class does not take 'active' as its class. Here's the markup for the Header.jsp for that link:
<li class="dropdown" data-roles="['ROLE_ADMIN']">
<a href="#/bi" class="dropdown-toggle" data-toggle="dropdown" data-ng-click="ctrl.changeNavigation('bi/schedule')"
role="button" aria-haspopup="true" aria-expanded="false">BI Management<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>Schedule Reports</li>
</ul>
</li>
Here is the demo of it not working even though it is printing out true in the UI:
The only problem is with this UI. All the data are populated. Records are displayed for the correct tab. Even side nav-bar is displaying the correct active class.
Your syntax for ng-class is off a bit. The format is "{ '[class-name]': [expression that evaluates to true or false] }". You can have multiple class values separated by commas each with their own expression. When an expression is true, the corresponding class is applied to the element and when it is false the class is removed from the element. The way you have written it would almost work for the plain class attribute, but you would need to include the interpolation characters: {{ and }}. Here is a very simple example to illustrate ng-class.
angular.module('app', []);
.red {
color: #fff;
background-color: #e21d1d;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app">
<label>
<input type="checkbox" ng-model="applyRedClass" /> Apply 'red' class
</label>
<div ng-class="{'red': applyRedClass}">
This is a simple example of how to use ng-class.
</div>
</div>

parse syntax error ng-click from directive templateUrl

I'm starting out with some AngularJs practises and I would like to create dynamic pages based on a JSON file.
Upon loading the page I get the following error:
Error: [$parse:syntax] http://errors.angularjs.org/1.4.2/$parse/syntax?p0=%7B&p1=invalid%20key&p2=23&p3=SelectedPage.SetPage(%7B%7Bpage.Number%7D%7D)&p4=%7Bpage.Number%7D%7D)
at Error (native)
at http://localhost/AngularJs/scrpts/angular.min.js:6:416
at Object.q.throwError (http://localhost/AngularJs/scrpts/angular.min.js:209:32)
at Object.q.object (http://localhost/AngularJs/scrpts/angular.min.js:208:357)
at Object.q.primary (http://localhost/AngularJs/scrpts/angular.min.js:205:335)
at Object.q.unary (http://localhost/AngularJs/scrpts/angular.min.js:205:174)
at Object.q.multiplicative (http://localhost/AngularJs/scrpts/angular.min.js:204:434)
at Object.q.additive (http://localhost/AngularJs/scrpts/angular.min.js:204:261)
at Object.q.relational (http://localhost/AngularJs/scrpts/angular.min.js:204:96)
at Object.q.equality (http://localhost/AngularJs/scrpts/angular.min.js:203:425) <a ng-click="SelectedPage.SetPage({{page.Number}})" href="#">
the problem occurs in this templateUrl: navigation.html
<nav>
<ul>
<li ng-repeat='page in Pages'>
<a ng-click='SelectedPage.SetPage({{page.Number}})' href="#">{{page.Name}}</a>
</li>
</ul>
</nav>
My guess is that the {{ }} syntax doesn't get accepted here but i'm not sure.
the directive call is as follows:
.directive('navigationPart', function(){
return {
templateUrl: 'components/navigation.html'
};
})
I do get this element code in Html so in a way it does what i ask.
<nav>
<ul>
<!-- ngRepeat: page in Pages --><li ng-repeat="page in Pages" class="ng-scope">
<a ng-click="SelectedPage.SetPage(1)" href="#" class="ng-binding">Index</a>
</li><!-- end ngRepeat: page in Pages --><li ng-repeat="page in Pages" class="ng-scope">
<a ng-click="SelectedPage.SetPage(2)" href="#" class="ng-binding">Who</a>
</li><!-- end ngRepeat: page in Pages -->
</ul>
</nav>
I would like to know what i'm doing wrong (why the error gets thrown)...
Here in this line:
<a ng-click='SelectedPage.SetPage({{page.Number}})' href="#">
You need to change it to:
<a ng-click='SelectedPage.SetPage(page.Number)' href="#">
The reason is that ng-click expects an Angular expression, which is evaluated on click. Double curly braces do not represent a valid expression token and Angular complains about it, raising an error.
If you find this a bit confusing, remember that the double curly braces syntax ({{...}}) is used to bind the model values to the DOM, while a lot of built-in directives work a bit differently - you don't bind anything in there, but instead need to provide an expression that is then evaluated in the element's scope.
Try changing
<a ng-click='SelectedPage.SetPage({{page.Number}})' href="#">{{page.Name}}</a>
to
<a ng-click='SelectedPage.SetPage(page.Number)' href="#">{{page.Name}}</a>
In ng-click, you access the page variable directly, no need for the braces. In other words, your guess is right.

Bind different dom elements in Angular

I have this HTML
<div class="pageContent">
<aside class="left-cont">
<ul class="btn-menu" >
<li ng-repeat="tab in tabs" ><a class="ng-scope ng-binding" href="#" ng-click="show={{tab.id}}"> {{tab.name}}</a> </li>
</ul>
</aside>
<section class="main-content" ng-show="show === 1">
<h1> {tab.title}}</h1>
<p>{{tab.content}}</p>
</section>
<section class="main-content" ng-show="show === 2">
<h1> {{tab.title}}</h1>
<p>{{tab.content}}</p>
</section>
<section class="main-content" ng-show="show === 3">
<h1> {{tab.title}}</h1>
<p>{{tab.content}}</p>
</section>
</div>
And I have three problems with it:
1)ng-click="show={{tab.id}}"shows me the right id number in the dom, for instance, id=1 does show <a class="ng-scope ng-binding" href="#" ng-click="show=1">but I do get an error in my console - Error: [$parse:syntax] http://errors.angularjs.org/1.3.8/$parse/syntax?p0=%7B&p1=invalid%20key&p2=7&p3=show%3D%7B%7Btab.id%7D%7D&p4=%7Btab.id%7D%7D Why is that? I have tried to to write tab.id instead of {{tab.id}}, but it doesn't even fetch the element.
2) I want the section tags to be appended whenever there's a click on the a, but I can not do so since the section tags aren't wrapped in the li tags (when I do put them in the li they do fire, but that's not what I want). How do I bind these two different elements?
3) I want each section to append the content of its specific tab for the section that has - ng-show="show === 1" I want tab.title and tab.content of the tab that has the id=1, and so on..
How is that all possible?
Help is very much appreciated here
#HarishR is right: this is not the AngularJS way to do things. The imperative way of doing things would be to have an ng-click set the isActive property to true on an instance of tab.
Then the directive would would watch the isActive property and change class accordingly.
Since you probably don't want to reinvent the wheel, you could consider using Angular strap ( http://mgcrea.github.io/angular-strap/#/tabs#tabs ) or check out this code https://github.com/angular-ui/bootstrap/blob/master/src/tabs/tabs.js.

Angular JS Animation ng repeat two collections

I am trying to work out ng repeat animation when I add/remove items from two different arrays one after another but it doesn't seem to work really smoothly on chrome-it works on firefox. Here is the following example codes:
template.html
<ul>
<li ng-repeat='item1 in arr1' class="repeated-item">
<span>{{ item1 }}</span>
</li>
</ul>
index.html
<div>
<div ng-include='template.html'></div>
<ul>
<li ng-repeat='item2 in arr2' class="repeated-item">
<span>{{ item2 }}</span>
</ul>
</div>
I notice animation works quite well for outside repeat except ng-included template. Hope you guys can help to figure out this.
You didn't close the <div ng-include='template.html'> tag properly. Add the closing tag </div>.

Resources