Why angularjs ignore ng-if? - angularjs

i try use ng-if, but angular no compile him. Look in test code:
<ul class="tmmenu-admin-tabs-builder-panel-answer">
<li class="tmmenu-admin-tabs-builder-panel-portlet" ng-repeat="answer in answers">
<div>
<span ng-repeat="question in questions">
<label ng-repeat="answer in question.answers">
<input type="checkbox">{{question.id}}.{{answer.id+1}}
<span ng-if="test()">ddd</span>
</label>
</span>
</div>
<textarea>{{answer.text}}</textarea>
</li>
</ul>
and function test:
$scope.test = function(){
console.log('d');
return false;
}
if run this page, we can see "ddd" in "Li" despite to test return false.
Next step, i replaced at "ng-if" "ng-show" :
<ul class="tmmenu-admin-tabs-builder-panel-answer">
...
<span ng-show="test()">ddd</span>
...
</ul>
And its working, "ddd" hide. Why ng-if not working where working ng-show?

The ngIf directive removes or recreates a portion of the DOM tree based on an {expression}. If the expression assigned to ngIf evaluates to a false value then the element is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
The ngShow directive shows or hides the given HTML element based on the expression provided to the ngShow attribute. The element is shown or hidden by removing or adding the ng-hide CSS class onto the element. The .ng-hide CSS class is predefined in AngularJS and sets the display style to none (using an !important flag). For CSP mode please add angular-csp.css to your html file (see ngCsp).

Could be a scope problem, is there something in a child scope that might override the value for test? ng-if works fine for me (plunker). I assume that everything else is working fine, are you seeing 'd' in the console to show that your function is getting executed?
Try changing your tag to this to help debug and you should see that it is a function:
<span ng-if="test()">test is: {{test.toString()}}</span>

Related

ng-keyup firing but not reading properly when used with an ng-if inside a directive template

I have a directive and it works fine in a way such that when I type something the search() scope function inside my directive fires and sets $scope.query with the input text.
here is the directive template
<div class="container">
<div class="system-filter-header">
<div class="no-gutter">
<div class="system-search-wrapper search-wrapper-width">
<i ng-click="search($evt)" class="fa fa-search"></i>
<input type="text" ng-keyup=search($evt) class="search pull-left suggesstions-styles"
ng-model="query" ng-attr-placeholder="Search...">
</div>
</div>
</div>
</div>
here is the scope function which gets triggered
$scope.search = function() {
console.log($scope.query.length)
}
But when I used an ng-if="true" in first line of template (true used for generalizing only, I want to do a different conditional check inside ng-if) such that,
<div class="container" ng-if="true">
still the search gets triggered but the console.log gives always 0 and it doesn't seem to update the $scope.query value as it stays as $scope.query = ''
throughout the typing.
EDIT
Here is a an example codepen with almost similar behaviour. The problem is with the searchBox directive and I have added ng-if=true to the template but searching doesn't work. When I remove the ng-if searching works fine.
Any reason for this?
Rule of thumb in AngularJS: your ng-model should always include a dot. Otherwise AngularJS directives that create child scopes (like ng-if or ng-repeat) will create a duplicate property on that child scope instead of the parent scope. Following the controllerAs convention completely mitigates this behavior.

How to access nested elements in AngularJS

I have a list of items (divs) with a button. When I click on one of this buttons I can access the element using
$event.currentTarget
that returns some thing like
<div ng-click="myFunc()">
<i class="someclass"></i>
<span>bla bla</span>
</div>
how can I access and elements to modify attributes like class?
Don't. Use existing directives like ng-class or ng-if, etc in your html templates.
<div ng-click="clicked = true">
<span ng-class="{'someclass': clicked}">bla bla</span>
</div>
See stackblitz
Although you can get the html dom element and edit it you should only do this as a last option and other angularjs supported methods have failed or are not supported.
template
<div ng-click="myFunc($event)">
<i class="someclass"></i>
<span>bla bla</span>
</div>
controller
$scope.myFunc(event) {
var elem = angluar.element(event.currentTarget);
elem.children(".someclass").removeClass("someclass")
}

AngularJS - One controller on two sibling DOM elements

I'm trying to do a very simple thing. I'm displaying a list of values with Edit links beside them. Clicking the edit link reveals a form that lets you update the value.
(I've simplified the question so the items just have one field "name". My actual use case has more fields, but is canonically equivalent.)
I've run into something that looks like a bug in Angular, but given my limited experience with it, I'm not so sure. Here's how I'm trying to do it:
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
My controller looks like this:
app.controller('ItemController', function() {
this.isEditing = false;
this.startEditing = function() { this.isEditing = true; }
this.save = function() { this.isEditing = false; }
});
Clicking on Edit link calls the right controller function, and the first div hides. But the second div does not hide.
When I rearrange the code to look like this (essentially wrapping the two divs with a wrapper element), all is well.
<div ng-repeat="item in items" ng-controller="ItemCtrl as ctrl">
<div ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
</div>
Any idea what is technically wrong with the first version? Note that the <input> boxes do get populated with the right values from item.name.
PS: There's a reason why I'm trying to keep the two divs siblings: in my use case, they are actually implemented as two trs which are supposed to appear right below each other in a table.
It's not a bug from angular but it is quite logical.
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
If you see the above code you have injected controller only to the first div so obviously sibling div doesn't know what is ctrl or ItemCtrl until and unless you do as in you second way.
So if you want to achieve it as sibling, if you are using routing then add the controller attribute in your route path.
So that the controller will be active for that entire template and you can achieve what you want.
Hope it helps.
Thats because controller has its own scope. When you placed controller ONLY on your first div controllers' scope is limited to only this one div. When you wrapped both your divs inside another and place controller on wrapper, controllers' scope now is all that inside this wrapper. So in this case this works fine and this is not the angular bug

Hide <div> element when 'ng-controller' is also assigned [duplicate]

This question already has an answer here:
Can I put ng-if directive on an element which also binds to a controller
(1 answer)
Closed 7 years ago.
I'm trying to hide an entire <div> using ng-if, but my below code is not working, presumably because ng-if and ng-controller are assigned to the same <div>.
If I move the ng-if condition to any element within the parent <div> then it works fine, but I'd rather not do this because -
I'd have a superfluous DOM element visible, but with nothing inside of it.
I'd potentially have to include the same ng-if condition multiple times.
My question therefore is how can I hide the entire <div>, rather than just the elements within?
<div id="sidebar-recent" data-ng-controller="closestCtrl" data-ng-if="closestStation != false">
<h3>Closest Station</h3>
<ul class="stations-list" data-ng-model="closestStationsList">
<li class="station" data-ng-click="selectStation(recentStation)">
{{ closestStation.name }} ({{ (closestStation.code | uppercase) }})
</li>
</ul>
</div>
Here is the relevant JS -
var app = angular.module('myApp', [])
app.controller('closestCtrl', function($scope){
$scope.closestStation = false;
});
I suggest using ng-hide or ng-show instead.
The difference between those and ng-if is, that the latter removes the element from the DOM, and the former only hides the element using style rules.
Why do you want to remove the whole div? It won't work If you are using ng-if you are actually removing the element from DOM. So the controller scope will be also removed. I don't think it's possible
Simply add a another one div only for controller. It will solve your problem very easy... don't think too much..
Something like
<div data-ng-controller="closestCtrl">
<div id="sidebar-recent" data-ng-if="closestStation != false">
<h3>Closest Station</h3>
<ul class="stations-list" data-ng-model="closestStationsList">
<li class="station" data-ng-click="selectStation(recentStation)">
{{ closestStation.name }} ({{ (closestStation.code | uppercase) }})
</li>
</ul>
</div>
</div>

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.

Resources