I'm new to Angular, but it seems to be the "Angular way" to avoid DOM manipulation whenever possible. I am not sure if I'm taking the right approach.
I have three buttons on the bottom (next,back, and home). These get enabled and disabled by way of the actions of other controllers. For example if a text box is NULL the next button will be disabled until something is entered. I have the following:
<div ng-controller="nextBackController" class="navbar navbar-app navbar-absolute-bottom">
<div class="btn-group justified">
<i class="fa fa-arrow-left fa-navbar" ng-class="{'btn-disabled' : btnBack}"></i> Back
<a id="btnHome" ng-click="handleClick()" href="#/" class="btn btn-navbar btn-icon-only btn-bounded" ng-class="{'btn-disabled' : btnHome}"><i class="fa fa-home fa-navbar"></i> Home</a>
Next <i class="fa fa-arrow-right fa-navbar" ng-class="{'btn-disabled' : btnNext}"></i>
</div>
</div>
And now for the JS:
app.service("nxtBackClick", function($rootScope, bottomButtonWatcher) {
this.btn = "";
this.broadcast = function(btn) {
//Toggle the Next button as an example
bottomButtonWatcher.btnNext = ~bottomButtonWatcher.btnNext;
//set the btn that is clicked
this.btn = btn;
$rootScope.$broadcast('bottomBtnClick');
};
});
app.service("bottomButtonWatcher", function($rootScope) {
//Set all these to disabled
this.btnHome = true;
this.btnNext = false;
this.btnBack = true;
});
And now the controller:
//####################### - Next Back Controller ########################
app.controller("nextBackController",
function($scope, nxtBackClick, bottomButtonWatcher){
$scope.btnHome = bottomButtonWatcher.btnHome;
$scope.btnNext = bottomButtonWatcher.btnNext;
$scope.btnBack = bottomButtonWatcher.btnBack;
$scope.handleClick = function(btn) {
nxtBackClick.broadcast(btn);
};
}
);
Having both a class attribute and an ng-class attribute can be problematic. They should both be condensed into a single ng-class attribute. You are already passing an object, so just add another property to it. Setting classes to always be shown is accomplished by setting their property to true.
<div ng-controller="nextBackController" class="navbar navbar-app navbar-absolute-bottom">
<div class="btn-group justified">
<a href="#/" class="btn btn-navbar btn-icon-only btn-bounded">
<i ng-class="{'fa fa-arrow-left fa-navbar': true, 'btn-disabled' : btnBack}"></i>
Back
</a>
<a id="btnHome" ng-click="handleClick()" href="#/" ng-class="{'btn btn-navbar btn-icon-only btn-bounded':true, 'btn-disabled' : btnHome}">
<i class="fa fa-home fa-navbar"></i>
Home
</a>
<a href="#/second" class="btn btn-navbar btn-icon-only">
Next
<i class="" ng-class="{'fa fa-arrow-right fa-navbar':true, 'btn-disabled' : btnNext}"></i>
</a>
</div>
</div>
You're using $rootScope.$broadcast but nobody is listening, you need to have a scope watcher in your controller that actually controls the buttons:
//####################### - Next Back Controller ########################
app.controller("nextBackController",
function($scope, nxtBackClick, bottomButtonWatcher){
$scope.btnHome = bottomButtonWatcher.btnHome;
$scope.btnNext = bottomButtonWatcher.btnNext;
$scope.btnBack = bottomButtonWatcher.btnBack;
$scope.handleClick = function(btn) {
nxtBackClick.broadcast(btn);
};
$scope.$watch('bottomBtnClick', function(buttonVal){
$scope[buttonVal] = true;
});
}
);
Related
i tried to make responsive navigation with vue js, when i tried to add '#click' to toggle burger bar or close icon, but it's adding the 'close' icon instead every time i click it.
here's the code
template section
<template>
// ----
<button #click="toggleButton">
<i v-if="!toggle" class="fas fa-bars"></i>
<i v-if="!toggleClose" class="fas fa-times"></i>
</button>
// ----
</template>
script section
<script>
export default {
name: "MultiNav",
data: function() {
return {
toggle: false,
toggleClose: true
};
},
methods: {
toggleButton() {
this.toggle = !this.toggle;
this.toggleClose = !this.toggleClose;
}
}
};
</script>
pict :
i try to use :
<button #click="toggle = ! toggle">
<i v-if="!toggle" class="fas fa-bars"></i>
<i v-if="toggle" class="fas fa-times"></i>
</button>
it still resulting the same result
You need to add a key to the icons.
<button #click="toggle = ! toggle">
<i v-if="!toggle" key="bars" class="fas fa-bars"></i>
<i v-if="toggle" key="times" class="fas fa-times"></i>
</button>
The key will tell Vue's algorithm that the icons are in fact different elements. Without that the virtual dom representation thinks that they are the same element.
Try switching the classes using condition
<button #click="toggle = ! toggle">
<i class="fas" :class="toggle?'fa-times':'fa-bars'"></i>
</button>
I just encounter a problem I have written a directive but its not getting update, I dont know why, in console it does change but in directive it does not.
Here is my directive
mainControllers.directive('mallsproduct', function () {
return {
restrict: 'E',
scope: {
productInfo: '=info',
linkid: '=linkid'
},
templateUrl: 'directives/dashboard_product.html'
};
});
Here is my `html`
<div class="aa-properties-content-body mg-7" ng-controller="DashboardController as ctrl">
<ul class="aa-properties-nav aa-list-view">
<li style="border: 1px solid #ccc;margin-bottom: 25px;" ng-repeat="active_products in productInfo.items">
<article class="aa-properties-item mg-top-0-notimp">
<a class="aa-properties-item-img" href="#/product/{{active_products.id}}">
<img ng-if="active_products.photos[0].path" resize-image alt="img" class="" src="{{active_products.photos[0].path}}">
<img ng-if="!active_products.photos[0].path" resize-image class="" src="img/default_product.jpg" alt="">
</a>
<div class="aa-properties-item-content">
<div class="aa-properties-about padding-0-notimp">
<h5>{{active_products.name| limitTo : 10}}{{active_products.name.length > 10 ? '...' : ''}}</h5>
<p class="font-size-11-imp"><i class="fa fa-building-o" aria-hidden="true"></i> {{active_products.mall.name| limitTo : 10}}{{active_products.mall.name.length > 10 ? '...' : ''}}</p>
<p class="font-size-11-imp"><i class="fa fa-map-marker" aria-hidden="true"></i> {{active_products.mall.address| limitTo : 10}}{{active_products.mall.address.length > 10 ? '...' : ''}}</p>
<p class="font-size-11-imp"><i class="fa fa-phone" aria-hidden="true"></i> {{active_products.shop.telephone}}</p>
<p class="font-size-11-imp" ng-if="linkid == 3"><i class="fa fa-eye" aria-hidden="true"></i> {{active_products.views}}</p>
<div class="modal-demo">
<script type="text/ng-template" id="myModalContent.html">
<div ng-include src="'partials/update_product.html'"></div>
</script>
<div ng-controller="AddProductController">
<button ng-click="view_product(active_products.id)"><i class="fa fa-pencil" aria-hidden="true"></i></button>
<button ng-click="del_product(active_products.id)"><i class="fa fa-trash-o" aria-hidden="true"></i></button>
<button ng-if="linkid == 2" ng-init="status = 1" ng-click="reactivate_product(active_products.id, status)"><i class="fa fa-lock" aria-hidden="true"></i></button>
</div>
<div class="modal-parent">
</div>
</div>
</div>
</div>
</article>
</li>
</ul>
<div class="aa-title pad-top-30" ng-if="linkid == 1">
<p>Global page count for active product is {{global_pagecount}} and active product count from API is {{productInfo._meta.pageCount}}</p>
<h3 ng-if="global_pagecount < productInfo._meta.pageCount" class="text-align-center color-feroz cursor-pointer" ng-click="load_more(global_pagecount, linkid)">{{$root.translated_labels.dashboard.load_more}}</h3>
</div>
<div class="aa-title pad-top-30" ng-if="linkid == 3">
<p>Global page count for most viewed is {{global_pagecount_mostv}} and most viewed count from API is {{productInfo._meta.pageCount}}</p>
<h3 ng-if="global_pagecount_mostv < productInfo._meta.pageCount" class="text-align-center color-feroz cursor-pointer" ng-click="load_more(global_pagecount_mostv, linkid)">{{$root.translated_labels.dashboard.load_more}}</h3>
</div>
</div>
I am including directive in dashboard partial like this
<div class="active tab-pane" ng-if="linkid === '1'">
<malls-product info="active_products" linkid="linkid"></malls-product>
</div>
<!--Active products list ends here -->
<!-- Get Inactive Products -->
<div class="active tab-pane" ng-if="linkid === '2'" >
<malls-product info="$root.inactive_products" linkid="linkid"></malls-product>
</div>
<!--Get Inactive products ends here -->
<div class="active tab-pane" ng-if="linkid === '3'" >
<malls-product info="$root.mostviewed_products" linkid="linkid"></malls-product>
</div>
<!-- View Profile-->
and This is the api which does show the result in console.
$scope.global_pagecount = 1;
$scope.active_product = function () {
$http.get($rootScope.baseurl + 'abc?&page=' + $scope.global_pagecount,
{headers:
{'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': $rootScope.keyword_auth_token, 'Accept-Language': $cookies.get('type')}
})
.success(function (data) {
//$scope.active_product_pageCount = data._meta.pageCount;
if ($scope.global_pagecount === 1) //I know for sure the first page of pagination is 1
{
$scope.active_products = data;
}
if ($scope.global_pagecount > 1) // If user click load more global count gets incremented and new results push in active_producst
{
/* for loading new results Pagination Applied */
for (var times = data.items.length - 1; times >= 0; times--) {
$scope.active_products.items.push(data.items[times]);
}
}
console.log($scope.active_products);
})
.error(function (data) {
// console.log(data);
});
};
What is the issue, why it is not getting update, If I use rootscopethen it works fine, obviously it has too, but not with $scope.
Note : when scope.global_pagecount value is equal to 2 i get new results but not in directive only in console. By default scope.global_pagecount has value equal to 1.
You don't use your directive correctly. You define it as:
mainControllers.directive('mallsproduct'
Which means you should use it as:
<mallsproduct ..>
Or define your directive camelcased:
mainControllers.directive('mallsProduct'
Then you can use it as you do now:
<malls-product ..>
This is because of the Isolated scope doesn’t know anything about its parent scope. You just created a directive with an isolated scope.
To access any parent scope data, we need to pass the scope data to our directive explicitly. This is achieved by setting properties on the scope object in the DDO.
Another important thing is that, these properties also MUST be set as the attributes of the directive html element.
<div class="full-row" ng-repeat="row in pendingRequests | orderBy: '-modificationDate' | partition:3">
<div class="one-third" ng-repeat="request in row track by request.id">
<div class="incoming_request" ng-class="actionClass(request)">
<div class="request_comments">
<hr>
<p><span>Recipients:</span></p>
<div class="comments">
<p ng-repeat="comment in request.comments track by comment.id" class="dont-break-out">
<span class="author" ng-bind="comment.userName | employeeName">:</span>
{{comment.text}}
<span ng-if="comment.status == State.APPROVED" class="approval success" ng-click="changestatus(comment, request)"><i class="fa fa-check"></i></span>
<span ng-if="comment.status == State.REJECTED" class="approval error" ng-click="changestatus(comment, request)"><i class="fa fa-times"></i></span>
<span ng-if="comment.status == State.PENDING" class="approval" ng-click="changestatus(comment, request)" title="{{showApproveTooltip(comment)?'Click to approve the request on behalf of this user':''}}"><i class="fa fa-clock-o"></i></span>
</p>
</div>
</div>
<div class="request_resolve">
<hr>
<div class="textarea-holder">
<textarea placeholder="Your comment..." ng-model="request.newComment" ng-model-options="{ updateOn: 'default blur'}"></textarea>
</div>
<div class="button-container">
<button ng-click="approve(request);" ng-disabled="request.isProcessing" class="btn btn-primary" am-hide-request-resolve-div>Confirm<i ng-hide="request.isProcessing" class="fa fa-check"></i><span ng-show="request.isProcessing" class="spinner no-hover"><a><i class="fa-li fa fa-spinner fa-spin"></i></a></span></button>
<button ng-click="reject(request);" ng-disabled="request.isProcessing" class="btn btn-default pull-right" am-hide-request-resolve-div>Reject <i class="fa fa-times"></i></button>
</div>
</div>
Here is peace of code. As You may see there are many ng-repeats. My pendingRequests collection very often is updated from server. After 3 or more updates when I click on some button nothing is happend on UI.
Details :
On approve click I change status of one comment.
$scope.approve = function (request) {
var currentUserComment = request.comments.filter(function(comm) {
return comm.userId == user.id && comm.status == "Pending";
})[0];
currentUserComment.status = State.APPROVED; // change comments Status
currentUserComment.text = request.newComment;
delete request.newComment;
if (!currentUserComment) {
request.isProcessing = false;
return;
}
Comments.update(currentUserComment, function() {
// $rootScope.$broadcast('daysUpdated');
});
request.isProcessing = false;
};
This must show this span <span ng-if="comment.status == State.APPROVED" class="approval success" ng-click="changestatus(comment, request)"><i class="fa fa-check"></i></span> , cause now status is equal to State.APPROVED. But nothing happens.
After some research I think it all because ng-repeat and collection updates.
ng-repeat creates a child scope for each item, and so the scopes in play might look like this:
bookCtrl scope = { tags: [ 'foo', 'bar', 'baz' ] }
ng-repeat child scopes: { tag: 'foo' }, { tag: 'bar' }, { tag: 'baz' }
So when I update comment.status for some request Angular don't know in what scope it exists.
Am I right? And how can I solve my problem (after changing comment status show correct span)?
More Simple code :
<div ng-repeat="request in requests">
<div ng-repeat="comment in request.comments">
<span ng-if="comment.status == State.APPROVED" class="approval success"><i class="fa fa-check"></i></span>
<span ng-if="comment.status == State.REJECTED" class="approval error"><i class="fa fa-times"></i></span>
<span ng-if="comment.status == State.PENDING" class="approval"><i class="fa fa-clock-o"></i></span>
</div>
<div>
<button ng-click="approve(request)">
Approve
</button>
</div>
</div>
And approve function :
var user = LoggeInUser(); // some user who is loggedIn now
$scope.approve = function(request){
var currentUserComment = request.comments.filter(function(comm) {
return comm.userId == user.id && comm.status == "Pending";
})[0];
currentUserComment.status = State.APPROVED; // change comments Status
Comments.update(currentUserComment, function() { // send PUT request to API
// $rootScope.$broadcast('daysUpdated');
});
}
You may find your solution in moving the functions to $scope.functions.functionName. I believe from reviewing this that you are running into scoping issues, as you alluded to in your statement.
JS
$scope.functions.approve = function () { ... }
HTML
functions.approve(request)
You also might have a look at using controller as, sometimes that can help:
https://docs.angularjs.org/api/ng/directive/ngController
here is my problem. I need on click to change button icon and on another click to go to original state, like +,- and span description like home/back
i dont know what to do. Pleas help.
Here is html...
<div class="navbar-left left">
<button class="toggle btn mr2 white " >
<i class="fa fa-home fa-2x "></i>
<span class="mobile-hide ">Home</a>
</button>
</div>
and bad js
if ($('.toggle').hasClass('fa fa-home fa-2x')) {
$('.toggle').toggleClass('fa fa-home ').html('<span class="mobile-hide ">Home</a>');
} else($('.toggle').hasClass('fa fa-home ')) {
$('.toggle').toggleClass('fa fa-home fa-2x ').html('<span class="mobile-hide ">Back</a>');
}
toggleMenu();
});
You can
jQuery(function($){
$('.toggle').click(function () {
var $i = $(this).find('i').toggleClass('fa-2x');
$(this).find('span.mobile-hide').html($i.hasClass('fa-2x') ? 'Home' : 'Back')
})
})
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="navbar-left left">
<button class="toggle btn mr2 white" >
<i class="fa fa-home fa-2x"></i>
<span class="mobile-hide">Home</span>
</button>
</div>
You can only use .hasClass() to check for a single class Name.
In order to check for many class names I guess is better get the class attribute string and check with .indexOf()
if ($('.toggle').attr("class").indexOf('fa fa-home fa-2x') != -1) {
The same happens with toggleClass. You have to break it down:
$('.toggle').toggleClass('fa').toggleClass('fa-home').toggleClass('fa-2x')
You might be able to achieve this effect with the ::after pseudo-element. Just change the content property as appropriate based on some class on the button.
A simplified example:
$(function () {
$('#toggle').click(function () {
$(this).toggleClass('on');
});
});
.icon::after {
content: '+';
}
.text::after {
content: 'Home';
}
.on .icon::after {
content: '-'
}
.on .text::after {
content: 'Back'
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<button id="toggle">
<i class="icon"></i>
<span class="text"></span>
</button>
I have a table, and each row is generated using ng-repeat. One of the columns has a refresh icon, which when clicked carries out a task. However while this task is being carried out, I'd like the icon to spin; see here.
The issue I'm having is that when I click one of the icons, they all spin, rather than the one on just that row.
<td class="text-center">
<i ng-hide="refreshUserSpin" ng-click="refreshUser($index, user.id)" class="fa fa-refresh pointer"></i>
<i ng-show="refreshUserSpin" class="fa fa-refresh fa-spin "></i>
</td>
So in my controller, I set the refreshUserSpin to false:
app.controller('usersController', function($scope, Users) {
$scope.refreshUserSpin = false;
$scope.refreshUser = function(index, community_id) {
$scope.refreshUserSpin = true;
Users.refreshUser(community_id)
.success(function(data) {
$scope.users[index] = data.json;
$scope.refreshUserSpin = false;
});
};
});
Now this sends all of the icons into a spin. What is the correct way to handle this, so it only does it for that row.
I've tried doing things like:
<i ng-show="refreshUserSpin.$index" class="fa fa-refresh fa-spin "></i>
Then doing $scope.refreshUserSpin.index = true;, but this didn't seem to work.
Any ideas?
Try this
HTML
<td class="text-center">
<i ng-hide="refreshUserSpin[$index]" ng-click="refreshUser($index, user.id)" class="fa fa-refresh pointer"></i>
<i ng-show="refreshUserSpin[$index]" class="fa fa-refresh fa-spin "></i>
</td>
JS
app.controller('usersController', function($scope, Users) {
$scope.refreshUserSpin = {};
$scope.refreshUser = function(index, community_id) {
$scope.refreshUserSpin[index] = true;
Users.refreshUser(community_id)
.success(function(data) {
$scope.users[index] = data.json;
$scope.refreshUserSpin[index] = false;
});
};
});