Angularjs ng-show method - angularjs

I have this angular application where I want to display some data based on permissions so on my main controller I'm fetching some data from the server to see which roles are assigned to current user.
The problem is that the method used to decide if an element should be shown or hidden is running an async call to the server to see the roles.
<div class="menu">
<a ng-show="$root.hasRole(['admin', 'tech'])" ng-cloak>
<i class="grid layout icon"></i> Users
</a>
....
</div>
And the function hasRole:
controller('MainCtrl', [
'$scope',
'$rootScope',
'WhoAmI',
function($scope, $rootScope, WhoAmI) {
$rootScope.whoAmI = false;
$scope.init = function() {
WhoAmI.whoAmI(function(err, res) {
$rootScope.whoAmI = res.user;
});
};
$rootScope.hasRole = function(roles) {
if(!$rootScope.whoAmI){
return false;
}
_.forEach($rootScope.whoAmI.roles, function(v, k) {
if (v.name in roles) {
return true;
}
});
return false;
}
}
])
As you would expect the elements render before the call return of that init function and they remain hidden. I attached that method hasRole to the $rootScope because I want it available through out the application.
Is there a way to pause the rendering until that $rootScope.whoAmI is not false any more or is there a better way of handling this?

The problem is with the inner return true statement. It does return true, but from the inner function; the hasRole() function still returns false. So write it as:
$rootScope.hasRole = function(roles) {
var result = false;
if(!$rootScope.whoAmI){
return false;
}
_.forEach($rootScope.whoAmI.roles, function(v, k) {
if (v.name in roles) {
result = true;
return false; // we have the result, stop iterating
}
});
return result;
};

This is wrong way:
ng-show="$root.hasRole(['admin', 'tech'])"
You need to change it to: ng-show="hasRole(['admin', 'tech'])"
<div class="menu">
<a ng-show="hasRole(['admin', 'tech'])" ng-cloak>
<i class="grid layout icon"></i> Users
</a>
....
</div>

Related

How to trigger the click event inside ng-repeat of angularjs?

I am doing the ng-repeat for showing the project list in my app. on click on the individual project I need to show the list of surveys inside the project. But when the page get loaded for first time I want to trigger the click event of first project which is default.
Below is the html code for that list project.
projectlist.html
<ul class="sidebar-nav">
<li class="sidebar-brand" >
<a>
Projects
</a>
</li>
<li ng-repeat="obj in allProjects track by $id(obj)">
<a ui-sref="survey.surveyList({id: obj.ProjectID})" ng-click="getProjSurveys(obj.ProjectID)" data-id="{{obj.ProjectID}}">{{obj.ProjectName}}<span class="projectsetting"><img src="./images/settings.png"/></span></a>
</li>
</ul>
ctrl.js
require('../styles/survey/survey.less');
angular.module("adminsuite").controller("surveyController",['getAllSurveyService','$timeout','AuthenticationService', '$scope','$rootScope', '$state', '$stateParams', function(getAllSurveyService,$timeout, AuthenticationService,$scope,$rootScope,$state,$stateParams){
$rootScope.header = "survey";
$scope.hoverIn = function(){
this.hoverEdit = true;
};
$scope.hoverOut = function(){
this.hoverEdit = false;
};
$rootScope.surveySelected = false;
console.log($('.checked').length);
console.log($('.checkbox').length);
if($state.current.name != 'login'){
$rootScope.bgGround = 'bgBlank';
$rootScope.footerbgGround = 'bgFooter';
}
$scope.supported = false;
$scope.wapLink = 'http://apidev.1pt.mobi/i/interview?uid=188A80CF0D61CA60D2F5_495F23E0FD4B9120D1E7&surveyid=20903';
$scope.success = function ($event) {
$scope.supported = true;
console.log($(".copied"));
};
$scope.fail = function (err) {
console.error('Error!', err);
};
getAllSurveyService.surveyList().then(
function( data ) {
$scope.allProjects = data;
$scope.allSurveys = data[0].Surveys;
if($scope.allSurveys.ErrorMessage){
AuthenticationService.ClearCredentials();
}
}
);
$scope.getReportDetails = function(){
return $http.get('http://localhost:8395/api/UserSurvey/GetAllSurveys', {
headers: { 'Content-Type': 'application/json','SessionID':$rootScope.token}
})
};
$scope.getProjSurveys = function(projId){
var i;
for(i in $scope.allProjects){
if(projId == $scope.allProjects[i].ProjectID){
$scope.allSurveys = $scope.allProjects[i].Surveys;
}
}
angular.element(document.querySelectorAll(".surveymodule")).removeClass('toggled');
};
$scope.toggleClass = function($event){
angular.element($event.target).toggleClass("checked");
$rootScope.selectedItems = angular.element(document.querySelectorAll(".checked")).length;
angular.element($event.target).parent().toggleClass("active");
if(angular.element(document.querySelectorAll(".checked")).length < 1){
$rootScope.global.showNewHeader= false;
};
};
}]);
on-click of the projects I am able to show data in the ui-view but for the first time on load of the page unable to trigger click event for the first project. I tried with the above code inside the controller using angular.element and trigger('click'), but it is not working for me. Please help.
This is actually angularjs issue. JQuery's trigger click event not getting triggered in angular's ng-repeat.
Check this : https://github.com/angular/angular.js/issues/3470
The problem is you are using ui-sref along with ng-click. Here the state is changing before the function completes and either reloading the controller (if both states has same controller) or changing the controller. Hence ng-click finction is not running.
remove ui-sref and use $state.go() to change the state inside ng-click
Sample code here:
HTML
<ul class="sidebar-nav">
<li class="sidebar-brand" >
<a>
Projects
</a>
</li>
<li ng-repeat="obj in allProjects track by $id(obj)">
<a ng-click="getProjSurveys(obj.ProjectID)" data-id="{{obj.ProjectID}}">{{obj.ProjectName}}<span class="projectsetting"><img src="./images/settings.png"/></span></a>
</li>
</ul>
JS:
$scope.getProjSurveys(objId){
// your codes here
$state.go('survey.surveyList({'id': objId})')
}
Instead of trying to interact with the DOM you should do something like this:
getAllSurveyService.surveyList().then(
function( data ) {
$scope.allProjects = data;
$scope.allSurveys = data[0].Surveys;
$scope.getProjSurveys($scope.allProjects[0].ProjectID)
if($scope.allSurveys.ErrorMessage){
AuthenticationService.ClearCredentials();
// $state.go('login');
}
});

Pass toggling Boolean from service to controller

I want to show/hide an element based on the Boolean value that is changing in my service. I want the change of this Boolean to happen in my service so multiple controllers can access the true or false value, but I am having trouble returning this value to one or more controllers. Currently I'm only able to pass one value which is false, although the value does show it's changing in my service. Here is an example of my controller...
angular.module('myApp')
.service('ThisService', function(){
function toggleDisplay(){
return displayElement = !displayElement;
}
});
.controller('ThisCtrl', function (thisService, $scope) {
function init(){
$scope.displayElement = ThisService.toggleDisplay();
}
$scope.toggleElement = function(){
$scope.displayElement = ThisService.toggleDisplay();
}
init();
});
My HTML...
<div ng-show="displayElement">Show hide me</div>
<button ng-click='toggleElement()'></button>
Can you please tell me how to return the true/false value to my controller correctly?
You can use a value and then toggle that in your service. However, your service definition is not valid, you have a semi-colon in the middle of your chain of modules and you define your service with the name "ThisService", but then you try to reference it in your controller as "thisService" (it's case sensitive).
JS:
angular.module("myApp", [])
.value("DisplayElement", { value: true })
.service("ThisService", function(DisplayElement) {
this.toggleDisplay = function() {
return DisplayElement.value = !DisplayElement.value;
}
})
.controller("ThisCtrl", function(ThisService, $scope) {
function init() {
$scope.displayElement = ThisService.toggleDisplay();
}
$scope.toggleElement = function() {
$scope.displayElement = ThisService.toggleDisplay();
}
init();
});
HTML:
<div ng-app="myApp">
<div ng-controller="ThisCtrl">
<div ng-show="displayElement">Show hide me</div>
<button ng-click="toggleElement()">Toggle Display</button>
</div>
</div>
jsFiddle
You could even eliminate the service and just access the value directly in your controller (you'd have to inject it first).

angularjs: scope value doesn't get updated in view

there are buttons in detail.html file:
<div ng-controller="test.views.detail">
<div data-ng-repeat="item in details" scroll>
<button ng-click="showDetails(item)">The details</button>
in detail.js file
angular.module('test')
.controller('test.views.detail', function($scope) {
$scope.detailsClicked = false;
$scope.showDetails = function(item){
$scope.detailsClicked = true;
}....
in formDetail.html code:
<div ng-controller="test.views.detail">
{{detailsClicked}}
<div ng-if="detailsClicked">...
Initially it shows false for detailsClicked, when I click on button it goes to showDetails function but value of $scope.detailsClicked never get updated! It is straight forward not sure why it doesn't work:(
This is because you're using the same controller at two places and expecting the scope object to be the same which it is not. Everytime you call ng-controller in your markup a new scope object will be created. If you want them to be based off the same data then use a service.
Here is an example
app.controller('test.views.detail', function($scope, detailsClicked) {
$scope.detailsClicked = detailsClicked;
$scope.showDetails = function(item){
$scope.detailsClicked.isClicked = true;
}
});
Create a factory/service which will retain the data, make sure the data is a
app.factory('detailsClicked', function(){
var data = {
isClicked: false
}
return data;
});

Isolating re-used controller scopes with AngularJS

I'm currently working on a project where I use a primary navigation to open tabs which lazy-load templates.
EDIT: to clarify, the number of tabs can vary on a user basis, so I cannot do this hard coded.
My way to try and accomplish this is by using a Tabs-controller with headers and the tab-items themselves:
<section class="tabs" ng-controller="Tabs">
<div class="tabs__header">
<div class="tabs__header__item is-hidden" ng-repeat="tab in tabs" data-item="{{tab.item}}" ng-if="!tab.header">
<a class="tabs__header__item__text" ng-href="#/{{tab.item}}" ng-click="tabClicked($event)" data-item="{{tab.item}}">{{tab.name}}</a>
x
</div>
</div>
<div class="tabs__container">
<div class="tabs__container__tab is-hidden" ng-repeat="tab in tabs" id="tab-{{ tab.item }}" ng-if="!tab.header" ng-controller="Tab" ng-include src="template.url"></div>
</div>
</section>
And the controllers would look like this:
Tabs.js:
function Tabs(scope, root) {
root.$on('/general/page/initdata', function(e, data) {
//set default elements
scope.tabs = data['nav-primary'];
//open dashboard
});
root.$on('/nav/primary/open', function(e, itemName) {
root.$emit('/tabs/activate', itemName);
_setActiveTab(itemName);
});
scope.tabClicked = function(e) {
e.preventDefault();
var name = e.target.getAttribute('data-item');
_setActiveTab(name);
};
function _setActiveTab(itemName) {
var collection = document.querySelectorAll('.tabs__header__item.js-is-active, .tabs__container__tab.js-is-active');
angular.element(collection).removeClass('js-is-active');
collection = document.querySelectorAll('#tab-'+itemName+', .tabs__header__item[data-item="'+itemName+'"]');
angular.element(collection).addClass('js-is-active').removeClass('is-hidden');
}
};
Tabs.$inject = ['$scope', '$rootScope'];
Tab.js
function Tab(scope, root) {
var loaded = false;
root.$on('/tabs/activate', function(e, itemName){
if(scope.tab.item === itemName && !loaded) {
loaded = true;
scope.template = {url: '/frontend/templates/'+itemName+'.html'};
}
});
};
Tab.$inject = ['$scope', '$rootScope'];
Now, the problem is that template.url is modified for each tab whereas I only want to modify it for one tab. It turns out scope is shared between the various Tab-instances. How can I circumvent this?
It turns out the problem wasn't with AngularJS. I had simply made an error with my js-is-active class, causing the wrong tab to be displayed.

Angularjs toggle image onclick

I'm trying to toggle a button image when a user clicks it. I prefer to use angularjs instead of jquery if possible. Right now I have a working version which toggles the image when clicked, the only problem is it changes ALL the images on click. How do I reduce the scope or pass in the src attribute for the img element?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage()"><img id="followbutton" ng-src="{{followBtnImgUrl}}" /></a>
</div>
</div>
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/img1'
$scope.toggleImage = function () {
if ($scope.followBtnImgUrl === '/img1.jpg') {
$scope.followBtnImgUrl = baseUrl + '/img2.jpg';
} else {
$scope.followBtnImgUrl = 'img1.jpg';
}
}
});
Can I pass in the img src attribute the function like toggleImage(this.img.src) or similar?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage(merchant)"><img id="followbutton" ng-src="{{merchant.imgUrl}}" />
</a>
</div>
</div>
.
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/sth.jpg'
$scope.merchants = [{imgUrl: "img1.jpg", name:"sdf"},
{imgUrl: "img2.jpg", name: "dfsd"}];
$scope.toggleImage = function(merchant) {
if(merchant.imgUrl === $scope.followBtnImgUrl) {
merchant.imgUrl = merchant.$backupUrl;
} else {
merchant.$backupUrl = merchant.imgUrl;
merchant.imgUrl = $scope.followBtnImgUrl;
}
};
});
What you want is a new scope for each followrow. As your code stands, there's only one scope that all of the followrows are referencing.
A simple answer is to create a new controller that you attach to each followrow:
<div class="followrow" ng-controller="ImageToggleCtrl">...</div>
And then move the image toggling logic to that new controller:
app.controller('ImageToggleCtrl', function($scope) {
$scope.followBtnImgUrl = '/img1';
$scope.toggleImage = function() { /* the toggling logic */ };
});
Now, a new scope will be instantiated for each row, and the images will toggle independently.
I just added two clickable images:
<div ng-app="FormApp" ng-controller="myController" max-width="1150px;" width="1150px;" >
<input ng-show="ShowDown" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactShow.png" ng-click="ShowHide()"/>
<input ng-show="ShowUp" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactHide.png" ng-click="ShowHide()" />
</div>
They toggle eachothers visibility. At page load one is visible, one is not, and both clickable images call the same function:
<script type="text/javascript">
var app = angular.module('FormApp', [])
app.controller('myController', function ($scope) {
$scope.ShowDown = true;
$scope.ShowUp = false;
$scope.ShowHide = function () {
$scope.ShowDown = $scope.ShowDown ? false : true;
$scope.ShowUp = $scope.ShowUp ? false : true;
}
});
</script>

Resources