I am getting an unknown provider error when attempting to launch an angular bootstrap modal window from my app by clicking on an image. I launch the same type of modal successfully elsewhere in the app.
Here is a screenshot of the error in the debugger
Is there something wrong with my controller code below? I looked at several other unknown provider error posts on stack, and to my knowledge I'm doing things properly.
app.controller('ModalInstanceCtrl', function($scope, $modalInstance, items,
removeVideoFromCart, removeLiteratureFromCart, productHasItems, cartItemHasVideos,
cartItemHasLiterature, getCartMailToBody, cartMailtoLink, logSentItems) {
$scope.items = items;
$scope.ok = function() {
$modalInstance.close($scope.test);
};
$scope.removeVideoFromCart = function (video, familyIndex, index) {
removeVideoFromCart(video, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove video links
}
$scope.removeLiteratureFromCart = function (literature, familyIndex, index) {
removeLiteratureFromCart(literature, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove lit links
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.productHasItems = function(index) {
return productHasItems(index);
}
$scope.cartItemHasVideos = function(index) {
return cartItemHasVideos(index);
}
$scope.cartItemHasLiterature = function (index) {
return cartItemHasLiterature(index);
}
$scope.getCartMailToBody = function () {
getCartMailToBody();
}
$scope.cartMailtoLink = getCartMailToBody();
$scope.logSentItems = function () {
logSentItems();
}
});
Thank you very much for your time. Let me know if you need more information or if I am being unclear.
I'm going to assume that app points to a declaration of that module that is defined at the root of your app e.g. in app.js:
app = angular.module('app', []);
And that you're including each dependency within your index.html e.g. after any angular scripts and app.js
<script src="yourDependency.js"></script>
In terms of the controller code itself, you don't need to assign to $scope a property that contains a function that calls the removeVideoFromCart service within your 'ModalInstanceCtrl' controller, because then you will still need to call that wrapper function again (which it currently looks like you are not doing).
You can just call the method within the controller rather than wrap it in a function e.g.
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
or just call the service e.g. if you don't need to bind the data to the UI like sending form data that on success just redirects elsewhere (although in your case it looks like you do want to bind the data to the UI):
removeVideoFromCart(video, familyIndex, index);
It's not clear from your code where the parameters for each service originate from. Are they within the items object? e.g.
var video, familyIndex, index
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
In terms of style, I prefer not assigning the module instances to a variable and instead use the setter syntax (following [John Papa's] (https://github.com/johnpapa/angular-styleguide#modules) styleguide, but also included in Todd Motto's), like so:
angular
.module('app')
.controller('ModalInstanceCtrl', ModalInstanceCtrl);
ModalInstanceCtrl.$inject['your', 'dependencies', 'go', 'here']
function ModalInstanceCtrl(/*dependencies here as parameters e.g.*/, removeVideoFromCart) {
var vm = this; // use in place of $scope and clarifies the context of the this keyword
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
$scope.removeLiteratureFromCart = removeLiteratureFromCart(literature, familyIndex, index);
//etc
});
NB: I would prefer a facade into all of those methods e.g. clearCartAndCloseModal('other', 'services') to hide all of the implementation details from the controller. This also makes it easier to create one controller per view that is in turn easier to test beacuse you have pushed all logic into the services. But I'm not clear from your code whether there is any relationship between each of the services.
#Claies #ritesh I was typing a long edit with responses to the questions when I happened upon my solution. I had multiple functions that opened modal windows using ModalInstanceController. For example, here are two:
$scope.open = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.selectedVideo3;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.openCart = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myAttachmentModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return "";
},
removeVideoFromCart: function() {
return $scope.removeVideoFromCart;
},
removeLiteratureFromCart: function() {
return $scope.removeLiteratureFromCart;
},
productHasItems: function() {
return $scope.productHasItems;
},
cartItemHasVideos: function() {
return $scope.cartItemHasVideos;
},
cartItemHasLiterature: function() {
return $scope.cartItemHasLiterature;
},
getCartMailToBody: function() {
return $scope.getCartMailToBody
},
cartMailtoLink: function() {
return $scope.cartMailtoLink
},
logSentItems: function () {
return $scope.logSentItems;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
I only use most of the dependencies for ModalInstanceController in the openCart function, so I didn't add all of the dependency function declarations to my other open methods (You can see in the resolve for the $scope.open method above I only declare items and no functions).
I needed to declare all of these functions like I did in $scope.openCart and it fixed my problem.
Thank you for reaching out.
My case was UpperCase LowerCase problem in the injected service name.
Related
I have simple controller with one method:
app.controller('MyApp', function($scope) {
$scope.myMethod() {...}
}
I have also many components for input fields (for example for text input, number input, checkbox, radio etc.):
app.component('TextInput', {
template: "<input type='text' ng-change='$ctrl.myMethodInComponent()' ng-model='inp' />",
bindings: {
myMethod: '&',
},
controller: function() {
var ctrl = this;
ctrl.myMethodInComponent = function() {
/* some customizations */
ctrl.myMethod();
}
}
});
I create this input in following way:
<text-input myMethod="myMethod()"></text-input>
Everything works as expected, but the problem is that I have many components which want to use method 'myMethod' from main controller and I don't want to transfer it to each component using bindings ('&').
Rather I want to have this method in something like mainScope. I know that Angular provides rootScope but I don't have idea how to use it in my case. Is there possibility to attach 'myMethod' to some main (root) scope which will be shared between all my components?
What you want to do can be achieved by using services and factories. Take alook into it, and if you need help or a template, just ask me.
EDIT template
app.factory('myFactory', function($scope)
var ret = {
myMethod: myMethod,
myMethodWithParams: myMethodWithParams
}
return ret;
function myMethod() {...}
function myMethodWithParams(param1, param2) {...}
}
And now, in your controllers, you can use it as a dependency
app.controller('myController', function(myFactory) {
var x = myFactory.myMethod();
var y = myFactory.myMethodWithParams('hello', 'world');
});
Not sure this is the sort of use case you are looking for using $rootScope, but here's a solution along those lines:
angular.module('myApp', [])
.controller('MyController', function ($scope, $rootScope) {
$scope.message = 'Hello from Controller 1';
$scope.$watch('message', function () {
$rootScope.$emit('controller1_scope_change', {
message: $scope.message
});
});
}).controller('MyController2', function ($scope, $rootScope) {
$scope.message = 'Hello from Controller 2, here is the output from Controller 1:';
$rootScope.$on('controller1_scope_change', function (event, args) {
console.log('arguments received by the handler for the event: ', args);
$scope.message2 = args.message;
});
// really hacky way of doing it
/*var controller1_scope = angular.element(document.querySelector('#controller1')).scope();
controller1_scope.$watch(function () {
$scope.message2 = controller1_scope.message
});*/
});
View example here on codepen
I need to pass data from modal to another page. Could you please help me to accomplish this task?
$scope.productdetails = function (size,selectedproduct)
{
var modalInstance = $uibModal.open({
templateUrl: 'ProductDetails.html',
controller: function ($scope, $uibModalInstance, product) {
$scope.product = product;
$scope.buynow = function (path) {
$uibModalInstance.close($scope.product);
$location.path(path); // Need to pass $scope.product to the new page
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
},
size: size,
resolve: {
product: function () {
return selectedproduct;
}
}
});
Be sure to read the docs on $uibModal: https://github.com/angular-ui/bootstrap/tree/master/src/modal/docs
the open() method will return an object with some useful stuff in it. Currently you're not doing anything with that object, but that's where the magic is.
To pass data to the controller that opened the modal, use the modalInstance.result promise like this:
modalInstance.result.then(function(data) { /*... do something with the data*/ });
Once that is set up, you can use the $close() function that this modal library places on the scope to resolve the result promise.
var data = {info: 'information to be returned to the parent controller'};
$scope.$close(data);
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');
});
}
Scenario:
I'd like to use this service to share some properties inside my webApp.
This service should have some object inside and the controllers should be able to get and set those objects.
JS:
var oneApp = angular.module('myApp', ['ui.bootstrap', 'ngTouch', 'ngAnimate'])
.service('sharedProps', function() {
var anObjNameDest = '';
var anObjPropName = '';
this.progressBarDynTyped00 = {
dynamic: 0,
dynMultiplier: 10,
max: 100
};
var progressBarDynTyped00 = {
dynamic: 1000,
dynMultiplier: 10010,
max: 100100
};
var test = this.progressBarDynTyped00;
var test2 = progressBarDynTyped00;
return {
getDynObject: function(anObjNameDest) {
return this[anObjNameDest];
},
getDynObjectVal: function(anObjNameDest, anObjPropName) {
return this[anObjNameDest][anObjPropName];
},
setDynObjectVal: function(anObjNameDest, anObjPropName, value) {
this[anObjNameDest][anObjPropName] = value;
},
setDynObject: function(anObjNameDest, ObjSrc) {
this[anObjNameDest] = ObjSrc;
}
}
});
oneApp.directive("progressBarDynTyped", function() {
return {
restrict: "E",
templateUrl: "aTemplateUrl.html",
controller: function(sharedProps) {
this.test = sharedProps.getDynObject('progressBarDynTyped00');
this.dynMultiplier
= sharedProps.getDynObjectVal('progressBarDynTyped00', 'dynMultiplier');
},
controllerAs: "progressBarDynTyped00"
};
});
Problem:
in the code above I have a service where there are two test Objects (previously there were only one of those, the second one was added for test purpose), those test objects should be returned from this service to some contrellers functions, like in the "progressBarDynTyped00" controller, because I need to get and set some values inside those objects.
Calling all of the retuned functions from the service gave me always an Undefined object or a "Cannot read property 'dynMultiplier' of undefined".
Question:
Is there a way to return a dynamic object passing the name of the object to the service function?
Thanks to all.
I think you are confused between two ways of creating service in Angular: using service keyword and factory keyword
For service keyword, the public functions are supposed to be made available by assigning it to this:
angular.module('myApp', ['ui.bootstrap', 'ngTouch', 'ngAnimate']).service('sharedProps', function () {
this.getDynObject = function (anObjNameDest) {
//...
};
}]);
And it doesn't expect to have return value. So with the code above, there's no public functions to be called, and that explains the error
An angular service is a simplified version of an angular factory. So you should use a factory instead of a service
myApp.service('myservice', function() {
this.method = function() {
return "result";
};
});
myApp.factory('myfactory', function() {
return {
method : function() {
return "result";
};
}
});
I'm relatively new to Angularjs, Till now I have resolved my problems with angularjs by searching a lot on internet but I can't resolve this. Hope anyone can help me with ideas and better knowledge of angularjs.
I'm trying to make my first custom directive and I need communicate two directives in the same module through parent controller. When I try to use the require attribute I always have an error: Error: $compile:ctreq
Missing Required Controller.
Am I making something wrong?
(function(){
'use strict';
var INTERVAL_DELAY = 100;
var SCROLL_DELTA = 50;
angular.module('vm.hidemenu',[])
.controller('vmHideMenuTopCtrl',function(){
return{
sayHello : function(){
console.log('Hello man!');
}
}
})
.directive('vmHideMenuTop',[function(){
return {
restrict: "A",
replace: true,
template: "<h1>Hello World</h1>",
link: function(scope, element, attrs){
},
controller: 'vmHideMenuTopCtrl'
}
}])
.directive('vmScrollableArea',['$window',function($window){
return {
restrict : "A",
require : "^vmHideMenuTop",
link : function(scope,element,attrs,menuCtrl){
var e = angular.element(element[0]);
var isScrolling = false;
var lastScrollPos = 0;
var scrolling; // timeout
e.bind('scroll',function(event){
var obj = event.target;
var scrollTop = obj.scrollTop;
isScrolling = true;
$window.clearTimeout(scrolling);
if(scrollTop > lastScrollPos ){
console.log('scroll Down');
}else{
console.log('scroll UP');
}
scrolling = $window.setTimeout(function(){
isScrolling = false;
lastScrollPos = obj.scrollTop;
menuCtrl.sayHello();
},INTERVAL_DELAY);
})
}
}
}]);
}());
I've tried other ways to achieve this, like firing events and try to hear those events. Thats only works if I fire the events through $rootScope. I read that this is a bad practice so i don't know how to achieve this.
The idea behind the code is to have an scrollable area that can communicate with the top navbar to hiding or showing it, just like mobile apps do.
Sorry if I can't express myself in the best way, I'm not english native.
Thanks for your consideration and help!
My advice would be to use a separate controller for each directive and a service to share the data. You could use an observer pattern like so:
app.service('myService', function () {
this.data = [];
var observers = [];
var self = this;
this.addObserver = function (callback) {
observers.push(callback);
};
var notifyObservers = function () {
angular.forEach(observers, function (callback) {
callback(self.data);
});
};
this.addData = function (data) {
this.data.push(data);
notifyObservers();
}
});
and inside the controllers:
myService.registerObserverCallback(function (data) {
$scope.myData = data;
console.log(data);
});
Or if you just wanted to use events with the $broadcast inside the service when data is updated and inside a controller use $on to request the updated data.