angular js - scope.apply not working as expected - angularjs

I am using angular material 1 and want to hide sidenave when window size not satisfy "gt-sm". Below is the controller code.
.controller('mainpageController',mainpageAction);
function mainpageAction($scope,$mdMedia,$window){
//state variable for mainpage.
$scope.sideNavVisibility = $mdMedia('gt-sm');
/* function definition for mainpage; bind this function using ng-click when user click on side menu */
$scope.showSideNav = function(defaultDisplay){
$scope.sideNavVisibility = true;
};
angular.element($window).bind('resize', function(){
console.log($mdMedia('gt-sm'));
$scope.$apply(function() {
$scope.sideNavVisibility = $mdMedia('gt-sm');
});
});
}
Below is the HTML template where I am using it.
<body md-theme="mainpage" ng-controller="mainpageController">
<div layout="row" layout-fill="true">
<md-sidenav class="md-sidenav-left" md-is-locked-open="{{sideNavVisibility}}" flex="none">
In console I am getting the correct value. When screen side is greate than 960px it is logging "true" value else "false". But still the change is not getting applied to view.
Here basically I want to show sidenav if screen size is more than 960px i.e. "gt-sm" or when not greater than "gt-sm" then when user click on side menu icon.

Remove the double curly brackets {{ }} from the expression furnished to the md-is-locked-open attribute:
<body md-theme="mainpage" ng-controller="mainpageController">
<div layout="row" layout-fill="true">
<md-sidenav class="md-sidenav-left"
<!-- REMOVE double curly brackets
md-is-locked-open="{{sideNavVisibility}}"
-->
md-is-locked-open="sideNavVisibility"
flex="none">
Double curly brackets converts the boolean value false to the string "false". In JavaScript the string "false" is truthy instead of falsy. In fact any string with a length greater than zero is truthy. This is one of the quirks of JavaScript.
For more information, see MDN JavaScript Reference - falsy

To show/hide an element based on a condition in Angular you can simply use ng-show or ng-hide.
<md-sidenav ng-show="sideNavVisibility" class="md-sidenav-left" flex="none">

Related

md-tabs: setting variable for md-selected not working when calling function from inside ui-router

I have a ui-router template containing md-tabs from which some tabs are static, others are created dynamically using ng-repeat iterating through a given array of the data model.
Inside those dynamically created tabs there are three buttons to do the following
move tab left by one position
move tab right by one position
The move tab buttons call a function inside the same controller. The $index value of the currently shown tabs as well as the desired direction (-1 for moving left, 1 for moving right) are passed to the function.
So the HTML-snippet for the view looks as follows:
<md-content flex layout-padding>
<md-card>
<md-tabs md-dynamic-height md-border-bottom md-autoselect md-swipe-content md-selected="selectedTab">
<md-tab id="{{ 'tab' + $index }}" data-ng-repeat="tab in tabs track by $index">
<md-tab-label>Tab {{ $index + 1 }}</md-tab-label>
<md-tab-body>
<md-card-title>
<md-card-title-text>
<div flex layout="row">
<div flex><span class="md-headline">Tab {{ $index + 1 }}</span></div>
<div flex align="right">
<md-button ng-click="moveTab(-1, $index)">Move tab left</md-button>
<md-button ng-click="moveTab(1, $index)">Move tab right</md-button>
</div>
</div>
</md-card-title-text>
</md-card-title>
<md-card-content>
<p>This is tab {{ $index + 1 }}</p>
</md-card-content>
</md-tab-body>
</md-tab>
</md-tabs>
</md-card>
</md-content>
The function moving the tabs is implemented in MainController as:
$scope.moveTab = function(direction, TabIdx) {
var staticTabs = 3
var arr = $scope.tabs
var fromIdx = sectorIdx
var toIdx = tabIdx + direction
// correct `toIdx` in order to prevent index overflow for first/last array element
if (toIdx < 0) {
toIdx = arr.length - 1
} else if (toIdx > arr.length-1) {
toIdx = 0
}
else {
; // nothing to do here since `toIdx` does not need to be corrected
}
var tab = arr.splice(fromIdx, 1)[0]
arr.splice(toIdx, 0, tab)
$scope.selectedTab = staticTabs + toIdx
console.log($scope.selectedTab)
}
After clicking the desired button to move a tab, the data of the according tab neigbour is displayed as desired.
This shows that maniupulating the $scope.tabs array works correctly. Additionally the log-message shows that even the new value for $scope.selectedTab is calculated correctly.
However, the new tab is not selected in the view.
This confuses me hence both variables $scope.tabs and $scope.selectedTab are defined in the same controller and should be part of the same $scope instance.
In addition, the manipulated $scope.tabs array is used in other views and shows the tab data in the new order, whereas the new value for $scope.selectedTab does not seem to be available and the shown tab does not change.
This was a problem of scope inheritance since data-bindings using other datatypes than objects do not have two-way-binding when inheriting the parent scope.
See the docs:
Scope inheritance is normally straightforward, and you often don't
even need to know it is happening... until you try 2-way data binding
(i.e., form elements, ng-model) to a primitive (e.g., number, string,
boolean) defined on the parent scope from inside the child scope. It
doesn't work the way most people expect it should work. What happens
is that the child scope gets its own property that hides/shadows the
parent property of the same name. This is not something AngularJS is
doing – this is how JavaScript prototypal inheritance works. New
AngularJS developers often do not realize that ng-repeat, ng-switch,
ng-view and ng-include all create new child scopes, so the problem
often shows up when these directives are involved. [...]
This issue with primitives can be easily avoided by following the
"best practice" of always have a '.' in your ng-models [...]
After changing
selectedTab
to
selectedTab.inputTab
in both the given HTML- and JS-snippets everything works as expected.

Is there a way to prevent evaluating template inside ng-if whose condition evaluates to false?

I have an ng-repeat that acts like a switch condition that tells which template to render. I'm using ng-if inside the ng-repeat to achieve this. The problem? It's still evaluating the templates inside ng-if even if the condition evaluates to false which causes the entire thing to be slow.
<div ng-repeat="i in ...">
<div ng-if="i == 'something1'">
if this is false, do not evaluate this entire stuff
.... some complex template goes here
</div>
<div ng-if="i == 'something2'">
if this is false, do not evaluate this entire stuff
.... another complex template goes here
</div>
</div>
If the template inside each of the ng-if is complex and there are 20 ng-if inside ng-repeat and only one ng-if evaluates to true then 19 other templates will be wasting computing resources.
What can I possible do to mitigate this without resorting to programmatic approach and maintaining two-way binding for the template that is rendered?
ng-if removes items from the HTML if the condition is false, so some rendering & evaluation may take place before that happens. If you nest ng-include inside of ng-if, the templates you load via ng-include are never evaluated.
Something like this:
<body ng-app="app">
<div ng-controller="myController as ctrl">
<div ng-if=true>
Hi
<div>{{ctrl.log(true)}}</div>
</div>
<div ng-if=false>
Bye
<ng-include src="foo.html"></ng-include>
<div>{{ctrl.log(false)}}</div>
</div>
</div>
<script>
angular.module('app', []).controller("myController",myController);
function myController($scope){
var ctrl = this;
ctrl.log=function(val) {
console.log(val);
return val;
}
};
</script>
</body>

AngularJs expression visible irritating

i have written angular js expression in razor for html helper but when i refresh the page after loading once it shows the expression, that is irritating.. what is the problem
here is the code
#Html.EditorFor(model => model.Questions.Question, new { htmlAttributes = new { ng_model = "quest" } })
{{quest}}
Instead of using curly binding expressions, you can use ngBind:
<span ng-bind="quest"></span>
Or you can use ngCloak:
The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.
<body ng-app="" ng-controller="ctrl" ng-cloak>
{{ quest }}
</body>

Angular JS use NG-Hide with function

I try to create an Angular JS function that is displaying or hiding a Div in case that a certain requirement is met.
I do have the problem now that the function is not properly called and both divs are either visible or not visible (In the test case div 1 should be shown and div 2 not).
testApp.controller('MyController', ['$scope','$http',
function ($scope,$http) {
$scope.checkValue = function(value){
if(value >= 1)
return true;
else
return false;
};
}]);
In the html file I try to hide the Divs using the following parameters
<div class="classa" ng-hide="requestsExisting({{profile.arrayA.length}})">
<div class="classb" ng-hide="requestsExisting({{profile.arrayB.length}})">
Is during the run time the {{profile.parameterA.length}}passed to the function or the actual value that is stored in this variables? (It's 1 for arrayA and 0 for ArrayB)
you don't need the "{{" sign.
just do
<div class="classa" ng-hide="requestsExisting(profile.arrayA.length)">
<div class="classb" ng-hide="requestsExisting(profile.arrayB.length)">
the double curly brace is to put the value of the object in the html
The double curly brace notation {{ }} to bind expressions to elements
is built-in Angular markup
I think that it should work just with this code
<div class="classa" ng-hide="requestsExisting(profile.arrayA.length)">
<div class="classb" ng-hide="requestsExisting(profile.arrayB.length)">
I think that you don't need to use {{}} inside the ng-hide directive

Why angularjs ignore ng-if?

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>

Resources