I wrote an angularjs directive to show and hide ajax spinners. The visibility of the spinner is toggled by show and hide buttons whose functionality is written inside the MainController. There is a variable inside the controller which is set to true and false based on the button click. This variable is passed to the directive using isolate scope. When I try to toggle one spinner, all the other spinners are also visible. How can I change my code to only toggle the particular spinner.
https://plnkr.co/edit/AFmBVbHaBPk66T7UjPC5?p=preview
// Code goes here
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
function showMe(){
mc.loading = true;
}
function hideMe(){
mc.loading = false;
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
$(element).show();
else
$(element).hide();
});
}
};
}
/* Styles go here */
.spinner {
position: relative;
/* [1] */
display: inline-block;
width: 1em;
/* [2] */
height: 1em;
/* [2] */
font-size: 32px;
/* [3] */
border-bottom: 1px solid;
/* [4] */
vertical-align: middle;
overflow: hidden;
/* [5] */
text-indent: 100%;
/* [5] */
-webkit-animation: 0.5s spinner linear infinite;
animation: 0.5s spinner linear infinite;
/**
* 1. Make the spinner a circle.
*/
/**
* The (optically) non-spinning part of the spinner.
*
* 1. Border around entire element fills in the rest of the ring.
* 2. Paler than the part that appears to spin.
*/
}
.spinner, .spinner:after {
border-radius: 100%;
/* [1] */
}
.spinner:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 1px solid;
/* [1] */
opacity: 0.5;
/* [2] */
}
/**
* Size variants (built by adjusting `font-size`).
*/
.spinner--small {
font-size: 16px;
}
.spinner--large {
font-size: 64px;
}
/**
* Color overrides.
*/
.spinner--light {
color: #fff;
}
.spinner--dark {
color: #333;
}
#-webkit-keyframes spinner {
to {
-webkit-transform: rotate(360deg);
}
}
#keyframes spinner {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading"></loading-directive>
<button ng-click="mc.showMe()">show</button>
<button ng-click="mc.hideMe()">hide</button>
</div>
</div>
</body>
</html>
If you want the spinners to have their own states, then they should be controlled by different variables.
In your example it is achievable by using an array to hold the variables
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading[i]"></loading-directive>
<button ng-click="mc.show(i)">show</button>
<button ng-click="mc.hide(i)">hide</button>
</div>
mc.loading = {};
function show(i){
mc.loading[i] = true;
}
function hide(i){
mc.loading[i] = false;
}
In a more real case example where you have some data and you use ng-repeat over them, you should assign the loading states inside the elements themselves.
This is a common technique to assign state to each items in ng-repeat
mc.fruits = [
{name:"apple"},
{name:"orange"},
{name:"starfruit"}
]
function load(fruit) { fruit.loading = true; }
function noLoad(fruit) { fruit.loading = false; }
<div ng-repeat="fruit in mc.fruits">
<loading-directive loading="fruit.loading"></loading-directive>
{{fruit.name}}
<button ng-click="mc.load(fruit)">show</button>
<button ng-click="mc.noLoad(fruit)">hide</button>
</div>
Working Plunkr: https://plnkr.co/edit/peGDxYJzKJgiHuPp4zmQ
You needed to define the isolated scope in the directive correctly. Essentially, your directive was still dependent on the controller as you were using the same variable mc.loading to determine the state of all directive instances.
By moving the deterministic variable $scope.loading as well as the buttons inside the directive, we are completely isolating each directive instance and making them all completely independent units.
HTML:
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive></loading-directive>
</div>
</div>
JS:
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{},
template: '<div><span ng-if="loading" class="spinner">Loading…</span>'
+ '<button ng-click="showMe()">show</button>'
+ '<button ng-click="hideMe()">hide</button></div>',
controller: function($scope) {
$scope.showMe = showMe;
$scope.hideMe = hideMe;
function showMe(){
$scope.loading = true;
}
function hideMe(){
$scope.loading = false;
}
}
};
}
The loading variable watched is common for all the directives used, hence when the model is changed the watch condition runs 5 times in your case, removing all the spinners.
I used the index to see what is being hidden or shown,
Updated fiddle: https://plnkr.co/edit/Jjfk6v7TJZHlQicM45ln?p=preview
HTML
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive data-index="{{$index}}" loading="mc.loading" offset="mc.offset"></loading-directive>
<button ng-click="mc.showMe($index)">show</button>
<button ng-click="mc.hideMe($index)">hide</button>
</div>
Angular
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
mc.offset =-1;
function showMe(offset){
mc.loading = true;
mc.offset = offset;
}
function hideMe(offset){
mc.loading = false;
mc.offset = offset;
console.log(offset);
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading",
offset:"=offset"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('[loading, offset]' , function (val) {
if(attr.index == scope.offset || scope.offset == -1){
if (val[0])
element.show();
else
element.hide();
}
});
}
};
}
SCRIPT:
function showMe(i){
mc.loading = true;
i=true;
}
IN HTML ADD THIS
<div ng-repeat="i in [1,2,3,4,5]">
<span><loading-directive loading="mc.loading" ng-show="i==true"></loading-directive>
<button ng-click="mc.showMe(i)">show</button></span>
<button ng-click="mc.hideMe()">hide</button>
</div>
Related
I am using Angular 1.3.I have a form in modal Pop-up.After submitting the form my modal Pop-up form is reset and if i click cancel button my form also reset
$scope.add_user = function(add_form)
{
if(add_form.$valid)
{
$http({
method:'POST',
url:file_path,
headers:{'Content_Type':'appliaction/json'},
data:$scope.text
}).success(function(data){
$scope.modalShown_add = ! $scope.modalShown_add;
$scope.modalShown_addsuccess = !$scope.modalShown_addsuccess;
$scope.getlist();
add_form.reset();
}).error(function(data){
add_form.reset();
})
}
}
but when i have any validation error if i click any place of my page my modal Pop-up gets closed after i open the modal Pop-up i am not able to reset my form.Suppose if i pass form name in add function to reset the form i am getting error
$scope.add =function()
{
$scope.modalShown_add = ! $scope.modalShown_add;
}
Each form directive creates an instance of FormController so you can access it by setting the name property like name="$ctrl.addForm".
To clear the form you need to clear the model and then use form controller to control the validation state of your form (see resetForm method):
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var ctrl = this;
ctrl.users = [];
ctrl.showPopup = false;
ctrl.openModal = openModal;
ctrl.saveUser = saveUser;
function openModal(user) {
ctrl.showPopup = true;
ctrl.user = angular.copy(user); // a copy of the user to disable 2-way binding with the list
resetForm(ctrl.addForm);
}
function resetForm(form){
form.$setPristine(); //set the form to its pristine state
form.$setUntouched(); //set the form to its untouched state
}
function saveUser(user){
//your saving logic here it is just a sample
if (!user.id){
user.id = ctrl.users.length;
ctrl.users.push(user);
} else {
ctrl.users[user.id] = user;
}
//hide pop up
ctrl.showPopup = false;
}
$scope.$ctrl = ctrl;
}])
.directive('modalDialog', function() {
return {
restrict: 'E',
scope: {
show: '='
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
if (attrs.overflow)
scope.dialogStyle.overflow = attrs.overflow;
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'><i class='fa fa-times-circle'></i></div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"// See below
};
});
.ng-invalid.ng-touched {
border: 1px solid red;
}
.ng-modal{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.ng-modal-overlay{
background-color: black;
opacity: 0.3;
z-index: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.ng-modal-dialog {
position: relative;
top: 50%;
z-index: 1;
background-color: white;
padding: 1em;
border: 1px solid gray;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.3.20/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<!-- send en empty object to create a new user -->
<button ng-click="$ctrl.openModal({})">Show</button>
<div>
{{u.name}}<span ng-repeat-end ng-if="!$last">, </span>
</div>
<modal-dialog show="$ctrl.showPopup">
<form name="$ctrl.addForm" ng-submit="$ctrl.saveUser($ctrl.user)">
<input name="user_name" ng-model="$ctrl.user.name" type="text" ng-required="true"/>
<div>
<button type="submit">Save</button>
<button type="button" ng-click="$ctrl.showPopup = false;">Cancel</button>
</div>
</form>
</modal-dialog>
</div>
</div>
Hope this helps you.
I have written a simple Angular JS code. I'm a beginner. However, one of my expressions is not getting evaluated. Need help. Please check the code below -
var myAppModule = angular.module('myAppModule', []);
myAppModule.controller('myController', function($scope){
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function () {
$scope.isHidden = !$scope.isHidden;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="myAppModule">
<head>
<title>Angular JS</title>
<script src="js/angular.min.js"></script>
<script src="js/myAppModule.js"></script>
<style>
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif;
}
div {
margin: 20px;
padding: 20px;
font-size: 16px;
color:#ffffff;
}
#red {
background-color: red;
}
#green {
background-color: green;
}
#blue {
background-color: blue;
}
#purple {
background-color: purple;
}
#gray {
background-color: gray;
}
#olive {
background-color: olive;
}
</style>
</head>
<body ng-controller="myController">
<h2>AngularJS Socks</h2>
<p>Keep warm this winter with our 100% wool, 100% cool, AngularJS socks!</p>
<button ng-click="showHideColors()" type="button">
{{isHidden ? 'Show Available Colors' : 'Hide Available Colors'}}
</button>
<div id="red" ng-hide="isHidden">Red</div>
<div id="green" ng-hide="isHidden">Green</div>
<div id="blue" ng-hide="isHidden">Blue</div>
<div id="purple" ng-hide="isHidden">Purple</div>
<div id="gray" ng-hide="isHidden">Dark Slate Gray</div>
<div id="olive" ng-hide="isHidden">Olive</div>
</body>
</html>
The expression - {{isHidden ? 'Show Available Colors' : 'Hide Available Colors'}} is not getting evaluated but displaying as is on the button. No clue as to what i missed. Thanks in advance.
The code is missing closing bracket. You can see the working demo here - http://jsfiddle.net/me8j3zyc/
var app = angular.module('myAppModule', []);
app.controller('myController', function($scope) {
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
} // <- This is missing.
});
This is because you havent closed your function
myAppModule.controller('myController', function($scope){
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
}})
Your expression is fine, but you have a typo error in your JS file:
var myAppModule = angular.module('myAppModule', []);
myAppModule.controller('myController', function($scope) {
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
} //MISSING
});
You can try this:
<button ng-if="isHidden" ng-click="showHideColors()" type="button">Show Available Colors</button>
<button ng-if="!isHidden" ng-click="showHideColors()" type="button">Hide Available Colors</button>
How to watch offset top position (of each element) and if the distance to top screen border lower than 100 px, i need to hide this div. (each div can move on page). Help please write Angular directive.
I can't watch each element top offset.
var app = angular.module('app', []);
app.app.directive('blah', function() {
return {
restrict: 'A',
link: function(scope, element) {
element.show();
scope.$watch(function() {
return element.offset().top > 120
}, function(outOfBorder) {
if (outOfBorder) {
element.hide()
console.log(outOfBorder)
} else {
element.show()
console.log(outOfBorder)
}
});
}
}
});
.all {
height: 100px;
width: 300px;
border:1px solid;
overflow: scroll;
}
.elements {
height: 30px;
width: 300px;
border:1px solid red;
float: left;
clear: both;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="all" ng-app="app">
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
<div class="elements" blah></div>
</div>
You simply add a watcher with a function (not tested + probably need some renaming):
angular.module('blah').directive('blah', function() {
return {
restrict: 'A',
link: function(scope, element) {
scope.$watch(function() {
return element.offset().top < 100
}, function(outOfBorder) {
if (outOfBorder) {
element.hide()
} else {
element.show()
}
});
}
}
});
The reason why your code is not working is that value you passed to a $watch is evaluated before $watch is executed. As a result, you watch top offset on the element at the time link is executed - this is a constant number.
EDIT:
If your elements are not absolutely positioned, calling hide will cause all the remaining elements to move up which eventually will make all elements hidden. In this case use element.addClass('invisible') and element.removeClass('invisible') instead of hiding and showing and define css class as:
.invisible { visibility: hidden; }
Changed visibility will not affect other element position.
Basically, i am using a service for to manage the popup-modal. some point i made the mistake or wrongly i have understand the way of using the service, i am not able to update the scope value here.
my service.js
"use strict";
angular.module("tcpApp").service("modalService", function () {
this.Name = "My Modal";
this.showModal = false; //default false.
});
controller.js:
$scope.modalService.Name = "Mo!" //changing the name works!
$scope.showModal = true; //changing the ng-show works!
here is my directive :
"use strict";
var modalPopup = function ( modalService ) {
return {
restrict : "E",
replace : true,
scope : {
showModal:"=" //getting value from controller.js
},
templateUrl : "views/tools/modalPopup.html",
link : function ( scope, element, attrs ) {
scope.Name = modalService.Name; //if i remove this Name not working!
scope.hideModal = function () {
alert("i am called");
scope.showModal = false; //it's not updating the value!
}
}
}
}
angular.module("tcpApp")
.directive("modalPopup", modalPopup);
here is my html in the index.html :
<modal-popup ng-show="showModal" showModal="showModal"></modal-popup>
here is my template in views/tools/modalPopup.html
<div class='ng-modal'>
<div class='ng-modal-overlay'></div>
<div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-close' ng-click='hideModal()'>X</div>
<div class='ng-modal-dialog-content'>Please test me {{Name}}</div>
</div>
</div>
I am clicking on the hideModal(), But the showModal is not became false and the poup-up modal not closing.
where is the mistake here? and how the way i wrongly understand the service here? or what is the correct way to do this?
Thanks in advance.
You don't need to pass around anything in your view because you have a service setup to do this for you:
(function() {
"use strict";
var app = angular.module("tcpApp", []);
app.controller('someController', function($scope, modalService) {
$scope.modal = modalService;
$scope.modal.Name = "Mo!"
});
app.service("modalService", function() {
this.Name = "My Modal";
this.isOpen = false;
this.hide = function() {
this.isOpen = false;
};
this.show = function() {
this.isOpen = true;
};
});
})();
(function() {
"use strict";
angular.module("tcpApp").directive("modalPopup", function(modalService) {
return {
restrict: "E",
replace: true,
scope: {},
templateUrl: "modalPopup.html",
link: function(scope, element, attrs) {
scope.modal = modalService;
}
}
});
})();
.ng-modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ng-modal:after {
content: "";
display: block;
position: absolute;
height: 100%;
width: 100%;
z-index: 10;
background-color: rgba(0, 0, 0, 0.2);
}
.ng-modal-dialog {
width: 300px;
height: 150px;
position: absolute;
left: 50%;
top: 15px;
margin-left: -150px;
z-index: 100;
text-align: center;
background-color: white;
}
.ng-modal-close {
width: 32px;
height: 32px;
line-height: 32px;
border-radius: 50%;
background-color: red;
margin: 5px auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='tcpApp'>
<div ng-controller='someController'>
<input type="text" ng-model="modal.Name" />
<button ng-click='modal.show()'>show modal</button>
<modal-popup></modal-popup>
</div>
<script type="text/ng-template" id="modalPopup.html">
<div class='ng-modal' ng-if="modal.isOpen">
<div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-close' ng-click='modal.hide()'>X</div>
<div class='ng-modal-dialog-content'>Please test me {{modal.Name}}</div>
<input type=text" ng-model="modal.Name"/>
</div>
</div>
</script>
</div>
Are you getting alert 'i am called'? If yes, try this
alert("i am called");
scope.showModal = false; //it's not updating the value!
scope.$apply(); // add this line it will update scope object value
try this
scope.$apply(function() {
scope.showModal = false;
});
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style>
.rating {
color: #a9a9a9;
margin: 0;
padding: 0;
}
ul.rating {
display: inline-block;
}
.rating li {
list-style-type: none;
display: inline-block;
padding: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.rating .filled {
color: red;
}
</style>
</head>
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" on-rating-selected="getSelectedRating(rating)"></div>
</span>
</div>
<script>
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.rating = 0;
$scope.ratings = [{
current: 5,
max: 10
}];
$scope.getSelectedRating = function (rating) {
console.log(rating);
}
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star" ng-click="toggle($index)">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '=',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function () {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
};
scope.toggle = function (index) {
scope.ratingValue = index + 1;
scope.onRatingSelected({
rating: index + 1
});
};
scope.$watch('ratingValue', function (oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
}
});
</script>
</body>
</html>
I want to use this star rating directive but I am clueless about how to use get and post using ng-resource so that i can fetch and push the selected values in my json file.
Also I want to ask that what's the benefit of using this instead of scope?
Thanks in advance.
Your issue was that your callback function on your controller wasn't triggered.
You can modify this as shown below:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style>
.rating {
color: #a9a9a9;
margin: 0;
padding: 0;
}
ul.rating {
display: inline-block;
}
.rating li {
list-style-type: none;
display: inline-block;
padding: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.rating .filled {
color: red;
}
</style>
</head>
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" on-rating-selected="getSelectedRating"></div>
</span>
</div>
<script>
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.rating = 0;
$scope.ratings = [{
current: 5,
max: 10
}];
$scope.getSelectedRating = function (rating) {
console.log(rating);
}
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star" ng-click="toggle($index)">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '=',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function () {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
};
scope.toggle = function (index) {
scope.ratingValue = index + 1;
//console.log(scope.onRatingSelected);
scope.onRatingSelected()(index + 1);
};
scope.$watch('ratingValue', function (oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
}
});
</script>
</body>
</html>
Honestly I don't know why it works... :/
There is a plunk which shows another way to do this: http://plnkr.co/edit/2mrW4SfRrI0qng7ORF0i