How to get component's controller's scope from animation? - angularjs

I've got a component with an animation defined as such:
angular
.module('slider')
.component('slider', {
templateUrl: '/mytemplate.html',
controller: function SliderController() {
var self = this;
self.direction = 'left';
}
})
.animation('.slide-animation', function() {
return {
beforeAddClass: function (element, className, done) {
var scope = element.scope();
// The line below logs undefined???
console.log(scope.direction);
}
};
});
I'm trying to access a value from the controller's scope but it's being returned as undefined which leads me to believe I've got the wrong scope. Is that correct? How can I get the controller's scope from within the animation?

I was able to get the scope I was looking for with help from Genti's comment.
angular
.module('slider')
.component('slider', {
templateUrl: '/mytemplate.html',
controller: function SliderController() {
var self = this;
self.direction = 'left';
}
})
.animation('.slide-animation', function() {
return {
beforeAddClass: function (element, className, done) {
var scope = element.scope().$ctrl;
console.log(scope.direction);
}
};
});

Related

How can I put an $ionicModal to use it from a .factory?

I know that from a .factory I can not use the $scope. How can I do to open the popup from a controller?
In my controller I need:
testigosElectoralesApp.controller('almacenarFotoController', function($scope,popup) {
poupup.openModalAlmacenamientoErroneo ()
})
This is my code
factory("poupup", function($ionicModal,$rootScope)
{
var oPopup = {}
$ionicModal.fromTemplateUrl('templates/modals/almacenamientoErroneo.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modalAlmacenamientoErroneo = modal;
});
$scope.openModalAlmacenamientoErroneo = function() {
$scope.modalAlmacenamientoErroneo.show();
};
$scope.closeModalAlmacenamientoErroneo = function() {
$scope.modalAlmacenamientoErroneo.hide();
};
return oPopup;
})
I do not know how to fix this code to make it work for me.
You probably would not want to call the scope inside a service/factory. But if you really want to, you can add $rootScope to service/factory and the controller. then use $rootScope.$emit or $rootScope.$broadcast in the service/factory.
In the controller where the $scope exists, you can add:
$rootScope.$on('scopeFunction', function () {
$scope.something();
});
In the service/factory, you can then call:
$rootScope.$emit('scopeFunction');
or
$rootScope.$broadcast('scopeFunction');
OR you can store the item in the $rootScope. Though I would recommend not to overuse $rootScope.
*******************EDIT*****************
To be able to use this:
testigosElectoralesApp.controller('almacenarFotoController', function($scope,popup) {
poupup.openModalAlmacenamientoErroneo();
})
You need this:
factory("poupup", function($ionicModal,$rootScope) {
var oPopup = {
openModalAlmacenamientoErroneo: function(){
//Add function here
}
};
return oPopup;
})

Angular not updating view in $http.get().then()

This is my template:
<p>{{$ctrl.status}}</p>
This is the component:
var phoneListModule = angular.module("phoneListModule");
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
this.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
self.status = "again";
});
}
});
And this is the module:
var phoneListModule = angular.module("phoneListModule", []);
The problem is that, the view is compiled properly with the "testing" text, but it's never updated to "again" when the GET request completes.
A console.log confirms that it completed fine
console.log(self.status);
Inside of the then() method.
Why is the view not updating if the dataset has changed?
self is not defined inside of the then() function, you need to define it at the start of the controller.
var phoneListModule = angular.module("phoneListModule");
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
var self = this; <<<< THIS IS IMPORTANT
this.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
self.status = "again";
});
}
});
i think you should avoid using this or self because depending where you are ( like inside function or loop ) it means something different. Try with a variable pointing on this in the beggining of controller
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
var vm = this ;
vm.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
vm.status = "again";
});
}
});

AngularJS Two way data binding

In my controller I have the following data:
(function() {
'use strict';
angular
.module('myappl.mymodule')
.controller('MyController', MyController);
MyController.$inject = ['$scope', 'myService'];
function MyController($scope, 'myService') {
$scope.vm = this;
var vm = this;
vm.myService = myService;
vm.userManagement = userManagement.data;
vm.userManagementSomeDataObjects = vm.userManagement.someDataObjects;
Somewhere in this controller I have a function which first gets data from backend and than invoke showModal:
function modalForUserInteraction() {
vm.myService.getData(parameters).success(function(data) {
vm.modalService.showModal(data, vm.userManagement, vm.userManagementSomeDataObjects);
}).error(function(data) {
console.log('error');
});
}
The modal- controller looks like this:
...
function showModalService($modal, $stateParams, otherService) {
var service = {
showModal: showModal
};
return service;
////////////
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
userManagement: function() {
return userManagement;
},
userManagementSomeDataObjects: function() {
return userManagementSomeDataObjects;
}
},
templateUrl: 'url/to.html'
});
return myModal;
and in the modal controller there is a method like this one:
function ModalController(userManagement, userManagementSomeDataObjects) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
userManagement = data;
userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}
If I do it like this:
userManagement = data; and userManagementSomeDataObjects = data.someDataObjects; than the new data is not set.
If I set each property separately of the objects than it works more often than not but somethimes it does not.
My question now would be what I can do in order to get it work.
Currently I do not have a $scope- variable in my modal and actually I don't know
if $scopeOfModal.$apply() would help and I also don't know how to get access from modal to MyController - $scope.
I would be glad for any hint in this direction.
Thanks a lot!
[EDIT]
Here is an image of my currently viewed (right) an on the left side the object, which should be shown after setting in modal- function.
[EDIT]
is there any posibility to pass parameters to this function in the modal controller:
this.previewArchivedSchedule = function(hereINeedParamerts) {
alert('archivedScheduleIntervalContainerId: ' + hereINeedParamerts);
};
This looks to me just like it may have nothing to do with angular, just some confusion with javascript variable references.
First you pass userManagement from showModalService.showModal to ModalController via resolve.
So now ModalController has a reference to the same object as showModalService.
However, in ModalController, you reassign the userManagement variable to point to data instead. So now the userManagement variable inside ModalController isn't pointing at the injected object anymore, because you've reassigned it. This has nothing to do with angular two-way data binding, it's just javascript variable assignment. You've lost your reference to the original object.
showModalService still has a reference to the instance that it sent in via resolve, it has no idea that you swapped the reference out in the ModalController.
I'd try sending over an object encapsulating the data you want to share to fix this problem.
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
sharedData: function() {
return {
userManagement: userManagement,
userManagementSomeDataObjects: userManagementSomeDataObjects
}
},
templateUrl: 'url/to.html'
});
return myModal;
Then manipulate the properties on the shared object instead of overwriting references.
function ModalController(sharedData) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
sharedData.userManagement = data;
sharedData.userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}

Isolating The controller in Angularjs custom directive

I have a directive which is working correctly when coded this way:
return {
restrict: 'E',
transclude: true, //transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside. So we can access the vm calling this.
scope: { // As the name suggests, the isolate scope of the directive isolates everything except models that you've explicitly added to the scope: {} hash object. This is helpful when building reusable components because it prevents a component from changing your model state except for the models that you explicitly pass in.
priceinformation: '=priceinformation'
},
controller: ['$scope','PriceCalculatorService', '$state', function ($scope,PriceCalculatorService, $state) {
var controller={
_$state:$state,
pricinginfo: [],
};
//console.log(PriceCalculatorService);
//debugger;
PriceCalculatorService.getPricingInfo()
.success(function (result) {
controller.pricinginfo = result;
})
.error(_handleError);
function _handleError(error) {
controller.error = error;
controller._$state.go('error-404');
}
$scope.controller = controller;
return controller;
}],
templateUrl: 'app/common/directives/views/price-calculator.html',
However, I want to isolate the controller, and do it this way instead:
(function (app) {
'use strict';
// #see https://docs.angularjs.org/guide/directive
// #important: Requires an object named pricinginformation is pushed into the directive.
var PriceCalculatorControl = (function () {
// scope is an isolated part of $scope limited this control only so we don't put a $
var PriceCalculatorControl = ['scope','PriceCalculatorService', function PriceCalculatorControl(scope,PriceCalculatorService) {
var control = {
total: 0,
subscriptionUnitprice: 100,
totalcal: function totalcal() {
if (scope.priceinformation.subscriptioninfo.type == 'Daily') {
control.subscriptionUnitprice = 100;
}
else if (scope.priceinformation.subscriptioninfo.type == 'Monthly') {
control.subscriptionUnitprice = 500;
}
else {
control.subscriptionUnitprice = 100;
}
control.total = control.subscriptionUnitprice * scope.priceinformation.subscriptioninfo.period * scope.priceinformation.subscriptioninfo.subjectsselected;
}
};
//kicking off the service
PriceCalculatorService.getPricingInfo()
.success(function (result) {
controller.pricinginfo = result;
})
.error(_handleError);
function _handleError(error) {
controller.error = error;
controller._$state.go('error-404');
}
// bootstrap
/* When injecting stuff, clean them up here. Like a timer that's running needs to be stopped.
element.on('$destroy', function() {});*/
//register on scope
scope.PriceCalculatorControl.PriceCalculatorControl;
}];
PriceCalculatorControl[2]();
return {
restrict: 'E',
transclude: true, //transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside. So we can access the vm calling this.
scope: { // As the name suggests, the isolate scope of the directive isolates everything except models that you've explicitly added to the scope: {} hash object. This is helpful when building reusable components because it prevents a component from changing your model state except for the models that you explicitly pass in.
priceinformation: '=priceinformation'
},
//controller: ['$scope','PriceCalculatorService', '$state', function ($scope,PriceCalculatorService, $state) {
// var controller={
// _$state:$state,
// pricinginfo: [],
// };
// //console.log(PriceCalculatorService);
// //debugger;
// PriceCalculatorService.getPricingInfo()
// .success(function (result) {
// controller.pricinginfo = result;
// })
// .error(_handleError);
// function _handleError(error) {
// controller.error = error;
// controller._$state.go('error-404');
// }
// $scope.controller = controller;
// return controller;
//}],
templateUrl: 'app/common/directives/views/price-calculator.html',
link: PriceCalculatorControl
};
});
// Register the directive
app.directive('priceCalculator', PriceCalculatorControl);
})(angular.module('app.common'));
This second method is not working. scope.PriceCalculatorControl.PriceCalculatorControl; says PriceCalculatorControl is not define.
PriceCalculatorService.getPricingInfo() says getPricingInfo is not defined.
Note: the service is working correctly. I guess the problem has to do with the dependency injection, but can't figure out what. Kindly enlighten me.
Thanks.
Try this:
(function (app) {
'use strict';
// #see https://docs.angularjs.org/guide/directive
// #important: Requires an object named pricinginformation is pushed into the directive.
var PriceCalculatorControl = (['PriceCalculatorService', function (PriceCalculatorService) {
// scope is an isolated part of $scope limited this control only so we don't put a $
var PriceCalculatorControl = function PriceCalculatorControl(scope) {
var control = {
total: 0,
subscriptionUnitprice: 100,
totalcal: function totalcal() {
if (scope.priceinformation.subscriptioninfo.type == 'Daily') {
control.subscriptionUnitprice = 100;
}
else if (scope.priceinformation.subscriptioninfo.type == 'Monthly') {
control.subscriptionUnitprice = 500;
}
else {
control.subscriptionUnitprice = 100;
}
control.total = control.subscriptionUnitprice * scope.priceinformation.subscriptioninfo.period * scope.priceinformation.subscriptioninfo.subjectsselected;
}
};
//kicking off the service
PriceCalculatorService.getPricingInfo()
.success(function (result) {
controller.pricinginfo = result;
})
.error(_handleError);
function _handleError(error) {
controller.error = error;
controller._$state.go('error-404');
}
// bootstrap
/* When injecting stuff, clean them up here. Like a timer that's running needs to be stopped.
element.on('$destroy', function() {});*/
//register on scope
scope.PriceCalculatorControl.PriceCalculatorControl;
};
//PriceCalculatorControl[2]();
return {
restrict: 'E',
transclude: true, //transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside. So we can access the vm calling this.
scope: { // As the name suggests, the isolate scope of the directive isolates everything except models that you've explicitly added to the scope: {} hash object. This is helpful when building reusable components because it prevents a component from changing your model state except for the models that you explicitly pass in.
priceinformation: '=priceinformation'
},
//controller: ['$scope','PriceCalculatorService', '$state', function ($scope,PriceCalculatorService, $state) {
// var controller={
// _$state:$state,
// pricinginfo: [],
// };
// //console.log(PriceCalculatorService);
// //debugger;
// PriceCalculatorService.getPricingInfo()
// .success(function (result) {
// controller.pricinginfo = result;
// })
// .error(_handleError);
// function _handleError(error) {
// controller.error = error;
// controller._$state.go('error-404');
// }
// $scope.controller = controller;
// return controller;
//}],
templateUrl: 'app/common/directives/views/price-calculator.html',
link: PriceCalculatorControl
};
}]);
// Register the directive
app.directive('priceCalculator', PriceCalculatorControl);
})(angular.module('app.common'));
Also I'm not sure what this is supposed to achieve:
PriceCalculatorControl[2]();

Testing element directive - can't access isolated scope methods during tests

I have the following directive.
directivesModule.directive('wikis', function() {
var urlRegex = new RegExp('^(https?)://.+$');
return {
restrict: 'E',
templateUrl: 'templates/wiki-list.html',
scope: {
wikis: '='
},
link: function(scope, element, attrs) {
scope.newWikiURL = '';
scope.$watch('wikis', function() {
if (scope.wikis == undefined || scope.wikis.length === 0) {
scope.class = 'hide';
} else {
scope.class = 'show';
}
}, false);
scope.addWiki = function() {
if (scope.wikis == undefined) {
scope.wikis = [];
}
var nw = scope.newWikiURL;
if (nw != undefined && nw.trim() != '' && urlRegex.exec(nw)) {
scope.wikis.push(nw);
scope.newWikiURL = '';
}
}
}
};
});
When I test it:
describe('Wikis Directive Test Suite', function() {
var scope, elem, directive, linkFn, html;
beforeEach(function() {
html = '<wikis wikis=''></wikis>';
inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.wikis = [];
elem = angular.element(html);
$compile(elem)(scope);
scope.$digest();
});
});
it('add Wiki should add a valid wiki URL to artist', function() {
var url = 'http://www.foo.com';
scope.newWikiURL = url;
scope.addWiki();
expect(scope.wikis.length).toBe(1);
expect(scope.wikis[0]).toBe(url);
expect(scope.newWikiURL).toBe('');
});
});
I get an error saying that Object doesn't have an addWiki method. I tried to debug it, and the link function is not called during the test. I suspect that's why the addWiki method is not part of the scope. Anybody knows why I'm getting this error?
By the way, Is it a normal practice to add some logic into the link function of a directive as it would be a Controller itself? Because looking at the code I know that it's why in reality I'm doing.
As per angular 1.2.0 docs, the way to get the isolate scope is through the method isolateScope
scope() - retrieves the scope of the current element or its parent.
isolateScope() - retrieves an isolate scope if one is attached directly to the current element. This getter should be used only on elements that contain a directive which starts a new isolate scope. Calling scope() on this element always returns the original non-isolate scope.
Angular doc - section jQuery/jqLite Extras
BREAKING CHANGE: jqLite#scope()
You need to load the module containing your directive, otherwise angular doesn't know what <wikis> is
Your directive creates an isolate scope, so once it has been compiled you need to get the new scope using elem.isolateScope()
So with those changes:
describe('Wikis Directive Test Suite', function() {
var $scope, scope, elem, directive, linkFn, html;
beforeEach(module('app'));
beforeEach(function() {
html = '<wikis></wikis>';
inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('templates/wiki-list.html', '<div>wiki template</div>');
$scope = $rootScope.$new();
$scope.wikis = [];
elem = angular.element(html);
$compile(elem)($scope);
scope = elem.isolateScope();
scope.$apply();
});
});
it('add Wiki should add a valid wiki URL to artist', function() {
var url = 'http://www.foo.com';
scope.newWikiURL = url;
scope.addWiki();
expect(scope.wikis.length).toBe(1);
expect(scope.wikis[0]).toBe(url);
expect(scope.newWikiURL).toBe('');
});
});
http://jsfiddle.net/QGmCF/1/

Resources