Call a method only if my $scope variable is false - angularjs

I've a div on click of which I'm calling a method.
Now, there's a 'Cancel' button, on click of which I'm setting a $scope.variable to true.
Next, I need to execute my function on click of the 'div', only if $scope.variable is set to false.
But it is now working! Could you help me fix this?
Here's my code:
angular.module('app', ['ngSanitize']).controller('Ctrl', function($scope) {
$scope.stopFunc = function() {
$scope.stopFuncExec = true;
}
$scope.stopFuncExec == false;
$scope.myFunc1 = function() {
console.log("Inside " + $scope.stopFuncExec);
var whoAreYou = "Coder";
if (whoAreYou == "Coder" && $scope.stopFuncExec == false) {
console.log("Hello, stop me if you can!");
}
}
});
.parent {
height: 100px;
width: 200px;
background: skyblue;
border: 1px solid lightgrey;
border-radius: 5px;
padding: 20px;
margin: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="app" ng-controller="Ctrl">
<div class="parent">
<div ng-click="myFunc1()">Click Me!</div>
</div>
<button ng-click="stopFunc()">Cancel</button>
</div>

You have typo: $scope.stopFuncExec == false;
Did you mean: $scope.stopFuncExec = false;?
After $scope.stopFuncExec == false; the $scope.stopFuncExec will be undefined
Fixed Demo

As you said, the function should get called only when the variable is false. I believe you can follow below work around for this. The function will get called on the click of the Div, but there you can check the condition as below.
If the variable is false, then only it will execute the block. I hope this will solve your problem.
$scope.myFunc1 = function()
{
if(!$scope.stopFuncExec)
{
console.log("Inside " + $scope.stopFuncExec);
var whoAreYou = "Coder";
if (whoAreYou == "Coder" && $scope.stopFuncExec == false)
{
console.log("Hello, stop me if you can!");
}
}
}

Related

Sharing a variable between Controller and Factory

I have a variable $scope.name defined in the controller which I am passing to the factory MyService as
$scope.name = MyService.name;
Inside the factory there is
var myserivce = {};
myservice.open = function() {
myservice.name = 'abc';
};
return myservice;
I want to return myservice.name so that abc is stored in $scope.name
How can I do so? Without using rootScope
In JavaScript, variables are shared by assigning them to properties of an object and passing a reference to that object.
app.factory("share", function() {
var objectRef = {};
return {
set: set,
get: get,
getRef: getRef,
};
function set(prop, val) {
objectRef[prop] = val;
}
function get(prop) {
return objectRef[prop];
}
function getRef() {
return objectRef;
}
});
The share.get(prop) method returns the value of the property at the time that the function is invoked. Any changes will not update values previously returned.
The share.getRef() method returns a reference to the object. When the contents of the object changes, every entity that has a reference to that object will share those changes.
Holding the shared data in a factory or service:
app.factory('Holder', function() {
return {
name : 'abc'
};
});
app.controller('YourCtrl', function($scope, Holder) {
$scope.Holder = Holder;
var name = $scope.Holder.name;
});
It's may help you.
If I understand your problem correctly then what you want is the Controller know that name variable is changed in Service and the view should be updated based on that.
This can be done in one of 3 ways:
Using rootScope - Bad way
Using watchers - Ok way
Using pub/sub models
angular
.module("myApp", [])
.controller("CtrlList", CtrlList)
.service("MyService", MyService);
CtrlList.$inject = ["$scope", "MyService"];
MyService.$inject = ["$q", "$timeout"];
function CtrlList($scope, MyService) {
MyService.name.subscribe(function(name) {
$scope.name = name;
});
$scope.$watch(
function() {
return MyService.age;
},
function(newValue, oldValue) {
$scope.age = MyService.age;
console.log(newValue + " " + oldValue);
console.log(MyService.age);
}
);
MyService.open();
}
function MyService($q, $timeout) {
var myservice = { name: new rxjs.Subject(), age: 1 };
myservice.open = function() {
$timeout(function() {
myservice.name.next("Value Changed.");
myservice.age = 2;
}, 10000);
myservice.name.next("Initial Value.");
};
return myservice;
}
* {
font-family: "Work Sans";
padding: 0;
margin: 0;
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #F6F7EB;
}
.wrapper {
width: 500px;
border: 1px dotted gray;
padding: 10px;
}
.wrapper .heading {
font-size: 30px;
margin-bottom: 12px;
}
.wrapper p {
margin-bottom: 6px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.3.3/rxjs.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Work+Sans:400' rel='stylesheet' type='text/css'>
<section class="wrapper">
<p class="heading">AngularJS v1.7.5 - Wait for 10 seconds to see changes</p>
<div ng-app="myApp">
<div class="container" ng-controller="CtrlList">
<p>This value is changed through rxjs : {{ name }}</p>
<p>This value is changed through watcher: {{ age }}</p>
</div>
</div>
</section>

ng-click not working when element compiled in controller

How do I use $compile to get ng-click working on a block of code? My code currently displays a suggestion box when the parameters for a particular event are met. However, I want to let the user hide the suggestion box by clicking on the close button.
View
<textarea class="form-control suggest"
ng-keyup="vm.suggestActivate($event.keyCode)"
ng-mouseenter="vm.suggestActivate(32)"
rows="3"
ng-model="vm.summaryData"></textarea>
Controller
var vm = this;
var suggestionElement = document.createElement('div');
vm.suggestActivate = function(keyCode) {
if(keyCode === 32) {
if(vm.summaryData) {
var words = vm.words;
var suggestions = null;
suggestions = '<div style="padding-bottom: 20px"><strong>Suggested next word:</strong></div>'
for(var i = 0; i < 5; i++) {
suggestions += '<div style="padding-bottom: 20px">' + words[Math.floor(Math.random() * words.length)] + '</div>';
}
suggestions += '<div class="btn btn-default" ng-click="vm.suggestDeactivate()">Close</div>'
suggestionElement.innerHTML = suggestions;
suggestionElement.setAttribute('style', 'background: #B0B0B0; padding: 20px; position: relative; top: -3.9em; width: 25%');
suggestionElement.style.display = 'block';
var targetElement = event.srcElement;
targetElement.parentNode.appendChild(suggestionElement);
}
}
else {
suggestionElement.style.display = 'none';
}
};
try with $compile
targetElement.parentNode.appendChild($compile(suggestionElement)($scope));
mention that you have to inject $compile first.
ADD:
use angular.element to add new elements to DOM.
refer below demo:
angular.module("app", [])
.controller("myCtrl", function($scope, $compile) {
$scope.add = function() {
var strNewElement = document.createElement('div');
strNewElement.innerHTML = '<button ng-click="test()">Test</button>';
angular.element(event.srcElement.parentNode).append($compile(strNewElement)($scope));
};
$scope.test = function() {
alert('I am new element.');
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<textarea ng-keypress="add()" rows="3"></textarea>
</div>

trying to get image in gallery of images to expand on click

Trying to get selected clicked image to expand in a gallery of images. I have the expanding working but it only works on the first image in the sets. If I click on another image in the set the first one is the one that gets expanded
<div ng-repeat="album in albumData|filter:q" id="thumbWrapper">
<h1>{{album.id}}</h1>
<h2 ng-click="showme = !showme">{{album.title}}</h2>
<div id="thumbList"ng-show="showme"class="albumContent">
<ul ng-controller="PhotoCtrl" id="thumbList">
<li ng-repeat="photo in photoData" ng-if="album.userId == photo.albumId">
<img id="view" ng-click="zoom()" ng-src={{photo.url}}>
</li>
</ul>
</div>
</div>
</div>
here's my angular js code
var app = angular.module('myApp', []);
app.controller('AlbumCtrl', function ($scope, $http) {
$http.get("http://jsonplaceholder.typicode.com/albums").then(function(response) {
$scope.albumData = response.data;
console.log($scope.albumData);
});
});
app.controller('PhotoCtrl', function($scope, $http) {
$http.get("http://jsonplaceholder.typicode.com/photos").then(function(response) {
$scope.photoData = response.data;
$scope.zoom = function() {
var imageId = document.getElementById('view');
if(imageId.style.width == "1000px"){
imageId.style.width = "600px";
imageId.style.height = "600px";
}else{
imageId.style.width = "1000px";
imageId.style.height = "1000px";
}
};
// console.log($scope.photoData);
});
});
any help would be awesome!
make the image id unique for each img tag
<img id="view{{$index}}" ng-click="zoom($index)" ng-src={{photo.url}}>
pass the index as parameter to the function
$scope.zoom = function(index) {
var elem = "view" + index;
var imageId = document.getElementById(elem);
if (imageId.style.width == "1000px") {
imageId.style.width = "600px";
imageId.style.height = "600px";
} else {
imageId.style.width = "1000px";
}
}
and put the zoom function out side of the http request
You can try this directory. Just copy and paste this code in the app.js and css code in style.css. But be careful, this applies to all images in your website
.directive('img', function ($window) {
'use strict';
function getElementOffset (element) {
var de = document.documentElement;
var box = element.getBoundingClientRect();
var top = box.top + window.pageYOffset - de.clientTop;
var left = box.left + window.pageXOffset - de.clientLeft;
return { top: top, left: left };
}
return {
restrict: 'E',
link: function (scope, element, attr) {
var expanded = false,
cloned = element.clone(true),
offset = getElementOffset(element[0]);
cloned.addClass('large');
cloned.attr('src', attr.src);
cloned.css('top', offset.top + 'px');
cloned.css('left', offset.left + 'px');
cloned.bind('click', function () {
if (expanded) {
cloned.removeClass('expanded');
expanded = false;
} else {
cloned.addClass('expanded');
expanded = true;
}
});
element.parent().append(cloned);
angular.element($window).bind('scroll', function () {
if (expanded) {
cloned.removeClass('expanded');
expanded = false;
}
});
}
};
});
CSS:
.app img {
display: block;
float: right;
width: 200px;
}
.app img.large {
cursor: -moz-zoom-in;
cursor: -webkit-zoom-in;
cursor: zoom-in;
position: absolute;
-webkit-transition: all 0.25s ease-out;
transition: all 0.25s ease-out;
}
.app img.expanded {
cursor: -moz-zoom-out;
cursor: -webkit-zoom-out;
cursor: zoom-out;
left: 0 !important;
top: 0 !important;
width: 100%;
}
source: https://jsfiddle.net/kmturley/jwtj57kt/

Not able to access $scope property outside AngularJS controller in animation function

angular.module("modalApp", ['ngAnimate', "ngMaterial", "ngMessages"])
.controller('modalCtrl', function ($scope) {
$scope.direction = 'left';
$scope.currentIndex = 0;
$scope.init = true;
$scope.initWizard = function() {
if($scope.init) {
$scope.setCurrentIndex(0);
}
$scope.init = false;
}
$scope.setCurrentIndex = function (index) {
$scope.currentIndex = index;
}
$scope.isCurrentIndex = function (index) {
return $scope.currentIndex === index;
}
$scope.nextModalStep = function () {
$scope.direction = 'left';
if($scope.currentIndex < $scope.modalSteps.length - 1) {
++$scope.currentIndex;
}
}
$scope.prevModalStep = function () {
$scope.direction = 'right';
if($scope.currentIndex > 0) {
--$scope.currentIndex;
}
}
})
.animation('.modalViewAnimation', function () {
return {
beforeAddClass: function(element, className, done) {
var scope = element.scope();
if (className == 'ng-hide') {
var elementWidth = element.parent().width();
startPoint = 0;
if(scope.direction !== "right") {
finishPoint = elementWidth;
} else {
finishPoint = -elementWidth;
}
TweenMax.fromTo(element, 0.5, { left: startPoint}, {x: finishPoint, onComplete: done });
} else {
done();
}
},
removeClass: function(element, className, done) {
var scope = element.scope();
if (className == 'ng-hide') {
var elementWidth = element.parent().width();
finishPoint = 0;
if(scope.direction !== "right") {
startPoint = elementWidth;
} else {
startPoint = -elementWidth;
}
TweenMax.to("section", 0.5, { height: element.outerHeight()});
TweenMax.fromTo(element, 0.5, { x: startPoint}, {x: finishPoint, onComplete: done, delay:0.25});
} else {
done();
}
}
}
});
I have a wizard slider that is almost working. My problem is accessing the direction property in my animation function after it has been set in the controller. The scope object has value inside the animation but the dot notation of retrieving the direction property with "scope.direction" returns undefined. Why? Any help greatly appreciated. Worth mentioning, I modified the animation function ever so slightly from this https://github.com/simpulton/angular-photo-slider to achieve what I want. I can't see why my scope.direction returns undefined?
Not sure the reason, but I had to get it from childHead.
The key for me was injecting rootScope.
Working example: JSFiddle
(edited to 'tidy' my code ;-) )
var myApp = angular.module('myApp', ['ngAnimate']);
myApp.controller('myCtrl', function($scope) {
$scope.onOff = false;
});
myApp.animation('.fold-animation-expand', ['$animateCss', '$rootScope',
function($animateCss, $rootScope) {
return {
enter: function(element, doneFn) {
console.log("onOff:" + $rootScope.$$childHead.onOff);
return $animateCss(element, {
from: {
"font-size": '0px'
},
to: {
"font-size": '20px'
},
duration: 2
});
}
}
}
]);
.box {
height: 100px;
width: 100px;
background-color: blue;
color: white;
float: left;
}
.box.ng-enter {
transition: 2s linear all;
opacity: 0;
}
.box.ng-enter.ng-enter-active {
opacity: 1;
}
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-2.2.1.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://code.angularjs.org/1.5.0/angular-animate.js"></script>
</head>
<body>
<div ng-controller='myCtrl'>
onOff state:{{onOff}}
<br>
<br>
<button ng-click="onOff?onOff=false:onOff=true">Run animation</button>
<hr>
<div ng-if="onOff == true" class="fold-animation-expand" style="font-size:0px">
Expanding element
</div>
<hr>Example of CSS-based transition
<br>
<div ng-if="onOff" class="box">Animated Box</div>
</div>
</body>
</html>

AngularJS animate image on src change

I have an AnuglarJS app, where I load/change some images from a webservice...
Controller
.controller('PlayerCtrl', function($scope, programService) {
....
programService.refresh(function(data) {
$scope.program = data;
});
....
Template
<img src="{{program.image}}" />
When my app updates from the webservice the images changes as expected, I just want to make an fadeout / fadein when this happens, how can that be done?
Is it possible to always make a fadeout/in when a image src changes?
Thanks for the responses -
I ended up doing this, and it works ;)
--- Directive ---
.directive('fadeIn', function($timeout){
return {
restrict: 'A',
link: function($scope, $element, attrs){
$element.addClass("ng-hide-remove");
$element.on('load', function() {
$element.addClass("ng-hide-add");
});
}
};
})
--- Template ---
<img ng-src="{{program.image}}" class="animate-show" fade-in />
--- CSS ---
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
transition: all linear 0.5s;
display: block !important;
}
.animate-show.ng-hide-add.ng-hide-add-active, .animate-show.ng-hide-remove {
opacity: 0;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
Update 1.5.x - with Angular 1.5.x you can use ngAnimateSwap to achieve this effect.
Based on pkdkk's answer and the Angular.js 1.3.6 sources, my solution is as such (the CSS animation part is as used for standard ngShow):
// Copied from the Angular's sources.
var NG_HIDE_CLASS = 'ng-hide';
var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
app.directive('myFadeIn', function($animate, $timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs){
element.addClass("ng-hide");
element.on('load', function() {
$timeout(function () {
$animate.removeClass(element, NG_HIDE_CLASS, {
tempClasses: NG_HIDE_IN_PROGRESS_CLASS
});
});
});
}
}
});
As christoph has mentioned, you should watch using $watch on the image source change.
But first make sure you use the ng-src rather than the src for the image tag.
<image id="new" ng-src="program.image" />
$scope.$watch('program.image', function(newValue, oldValue) {
if(newValue===oldValue) return;
$('img#new').hide();
$('img#new').fadeIn("slow", function() {});
})
In case others end up here wanting to perform animations on change of a background image, I'll post what I ended up using.
This directive assumes it's attached to a template like this:
<!-- Full screen background image and scarecrow for onload event-->
<div class="full-screen-image" data-background-image="{{backgroundImageUrl}}"></div>
<img class="hidden-full-screen-image hidden" data-ng-src="{{backgroundImageUrl}}"></div>
We want to set the background image source for the <div>, but attach an onload event so we know when the new image has arrived. To do that, we use an <img> with a .hidden class that has .hidden {display: none;}. Then we use the following directive to dynamically set the div's background image source and perform a fade to white then back from white on image change:
/***
*
* Directive to dynamically set background images when
* controllers update their backgroundImageUrl scope
* variables
*
* Template: <div data-background-image="{{backgroundImageUrl}}" />
* AND <img data-background-image="{{backgroundImageUrl}}" class="image-onload-target hidden" />
*
***/
var angular = require('angular');
angular.module('BackgroundImage', [])
.directive('backgroundImage', [
"$timeout",
function ($timeout) {
return function(scope, element, attrs){
attrs.$observe('backgroundImage', function(value) {
/***
*
* Define a callback to trigger once the image loads.
* The value provided to this callback = the value
* passed to attrs.$observe() above
*
***/
var imageLoadedCallback = function(value) {
// once the image load event triggers, remove the event
// listener to ensure the event is called only once
fadeOut();
target.removeEventListener('load', imageLoadedCallback);
$timeout(function() {
fadeIn(value);
}, 700);
}
/***
*
* Define fade in / out events to be called once a new image
* is passed to the attrs.backgroundImage in the directive
*
***/
var fadeOut = function() {
element.css({'opacity': '0'})
};
var fadeIn = function(value) {
element.css({
'background': 'url(' + value +') no-repeat center center fixed',
'background-size' : 'cover',
'opacity': '1'
});
};
// add an onload event to the hidden-full-screen-image
var target = document.querySelector('.image-onload-target');
target.addEventListener('load', imageLoadedCallback(value));
});
};
}]);
Working with Angular makes me love React all the more...
I know its late but according to #Aides answer i am posting here an working example that how can you achieve animation with change in ng-src using ngAnimateSwap (with Angular 1.5.x). I hope this helps someone in future:
HTML Markup:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-ngAnimateSwap-directive-production</title>
<link href="animations.css" rel="stylesheet" type="text/css">
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<script src="//code.angularjs.org/snapshot/angular-animate.js"></script>
<script src="script.js"></script>
<script type="text/javascript">
angular.element(document.getElementsByTagName('head')).append(angular.element('<base href="' + window.location.pathname + '" />'));
</script>
</head>
<body ng-app="ngAnimateSwapExample" ng-controller="AppCtrl">
<div class="container">
<img ng-animate-swap="activeImage" class="cell swap-animation" ng-src="{{activeImage}}" alt="My Active Image" />
</div>
<div>
Current Image: {{activeImage}}
<br />
<button ng-click="previous()">Previous</button>
<button ng-click="next()">Next</button>
</div>
</body>
</html>
JS (script.js):
(function(angular) {
'use strict';
angular.module('ngAnimateSwapExample', ['ngAnimate'])
.controller('AppCtrl', ['$scope', '$timeout', function($scope, $timeout) {
var baseUrl = "http://lorempixel.com/400/200/sports";
$scope.images = [];
$scope.startIndex = 0;
for (var i = 0; i < 5; i++) {
$scope.images.push(baseUrl + "/" + i);
}
$scope.activeImage = $scope.images[$scope.startIndex];
/*
$interval(function() {
$scope.startIndex++;
if($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] != undefined){
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 2000);
*/
$scope.previous = function() {
$scope.startIndex--;
$timeout(function() {
if ($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] !== undefined) {
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 500);
};
$scope.next = function() {
$scope.startIndex++;
$timeout(function() {
if ($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] !== undefined) {
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 500);
};
}]);
})(window.angular);
Working plunker here.
My solution to this problem is to watch for changes on ng-src and using a timeout function to add a class which does the fadeIn effect.
HTML
<img ng-src="your-logic-will-go-here" class="animate-show ng-hide-add" fade-in>
Angular Code
.directive('fadeIn', function($timeout){
return {
restrict: 'A',
link: function($scope, $element, attrs){
$scope.$watch('selectedFormat.name', function(newValue, oldValue) {
if(newValue!=oldValue) {
$element.removeClass("ng-hide-add");
$element.addClass("ng-hide-remove");
$timeout(function () {
$element.addClass("ng-hide-add");
}, 100);
}
})
}
};
})
CSS
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
display: inline-block !important;
}
.animate-show.ng-hide-add.ng-hide-add-active, .animate-show.ng-hide-remove {
opacity: 0;
}
.animate-show.ng-hide-add{
transition: all linear 0.7s;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
You can't animate an img src change. You can, however, have multiple images and animate their opacity.
HTML/angular template
<div class="image-container">
<img src="image-one.jpg" ng-show="showImageOne">
<img src="image-two.jpg" ng-show="showImageTwo">
</div>
CSS
.image-container {
position: relative;
}
.image-container img {
position: absolute;
transition: 1s opacity linear;
}
.image-container img.ng-hide {
display: block!important;
opacity: 0;
}

Resources