How do I modify a dependency of an injected dependency in angular? - javascript

I'm looking at the source code for the angular plugin of JQuery File upload, and I see the following code:
angular.module('blueimp.fileupload', [])
// The fileUpload service provides configuration options
// for the fileUpload directive and default handlers for
// File Upload events:
.provider('fileUpload', function () { ... })
.controller('FileUploadController', [
'$scope', '$element', '$attrs', '$window', 'fileUpload',
function ($scope, $element, $attrs, $window, fileUpload) {
...
// Observe option changes:
$scope.$watch(
$attrs.fileUpload,
function (newOptions) {
if (newOptions) {
$element.fileupload('option', newOptions);
}
}
);
so it seems obvious to me that the module was written to allow for me to update the options on the fileupload widget (which is what $element.fileupload('option', ...) does); however, I'm not sure how to get to the $attrs.fileUpload.
I need to update the options to fileupload after an async call in my controller:
var accountEditor = angular.module('accountEditor', [ 'ngResource', 'blueimp.fileupload' ]);
accountEditor.controller('accountEditorCtrl', [
'$scope',
'$resource',
'fileUpload',
function($scope, $resource, fileUpload) {
doAsyncThing(function() {
// update options...what goes here?
});
My current solution is a hack, and it is to mess with the options on a event callback (as described in How to change dynamically the upload (jquery-file-upload) url using $scope?). I consider this a hack because it requires user interaction for the options to get set, and also leads to a race condition where user interaction might occur before the async call is complete.

Do you have the view code?
I would imagine you would need to update the options attribute on the controller.
e.g.
View:
<div data-ng-controller="accountEditorCtrl">
<form data-ng-controller="FileUploadController" data-file-upload="options">
</options>
</div>
Controller:
var accountEditor = angular.module('accountEditor', [ 'ngResource', 'blueimp.fileupload' ]);
accountEditor.controller('accountEditorCtrl', [
'$scope',
'$resource',
'fileUpload',
function($scope, $resource, fileUpload) {
$scope.options = {}; // Set options here
doAsyncThing(function() {
$scope.options = {}; // Update options
// Note if this async method is not handled by angular you may need
// to call $scope.$apply(); to notify angular of the changes to scope
});

Related

AngularJS - state parameters don't persist to controller using ui-route

What seems to be an issue for me is how to use $state.go(state, params) and have params persist through to the state's controller, regardless of a component. The plunkr I've setup that demonstrates this is linked here: https://plnkr.co/edit/u5VaMZIBtVoBGbAZaQe0?p=preview
Basically what I'm doing is setting up 2 states, one links to the other. I'm using $state.go to go one of those states with passed parameters. It doesn't appear those parameters are passed though and I'm wondering why this is.
I'm using AngularJS version 1.6.6 and ui.router version 0.4.2 if that makes a difference or not.
var app = angular.module("yay", ['ui.router']);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
var states = [
{
name: 'paramsDontWork',
url: '/doesntwork',
template: '<div>hi{{variable}}</div><a style="color:blue;" ng-click="goto()">goto</a>',
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
$scope.variable = 'hi';
$scope.goto = function() {
$state.go('newplace', { should: "should"});
};
}],
},
{
name: 'newplace',
url: '/newplace',
template: 'new place {{should}} have params<br><a ui-sref="paramsDontWork">hi</a>',
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
console.log($stateParams);
$scope.should = $stateParams.should;
}]
}
];
states.forEach(function(state) {
$stateProvider.state(state);
});
$urlRouterProvider.when('', '/doesntwork');
}]);
Routes must explicitly specify any parameters that can be overwritten (e.g. transition from another route) as part of their state configuration.
Relevant guidance from the UI-Router documentation:
Any parameters that are not specified will be inherited from currently defined parameters. Only parameters specified in the state definition can be overridden, new parameters will be ignored.
(Emphasis mine.)
In your example, /newplace doesn't specify a params property to provide the additional non-URL parameter.
All you need to do to ensure value of should from /doesntwork gets passed is to explicitly declare it as part of /newplace as a parameter with some default value:
{
name: 'newplace',
...
params: { should: null },
}
Plunker fork to demonstrate the above.

How to limit the number of function calls in a controller angular Js?

I have a partial in which data is coming from multiple controllers, not the situation is those functions which are called in the controller,they are hitting the server for more than fifty times, and they keep hitting as long as they dont get the response from server. I dont know how to tackle this situation please guide me.
mainControllers.controller('AddProductController', ['$scope', '$http', '$routeParams', '$cookies', '$rootScope', 'Upload', '$timeout', '$uibModal', '$log', '$document', '$window', 'variantsService', 'toaster', '$route', '$rootScope', 'Lightbox', function ($scope, $http, $routeParams, $cookies, $rootScope, Upload, $timeout, $uibModal, $log, $document, $window, variantsService, toaster, $route, $rootScope, Lightbox) {
/*Currency dynamic*/
$scope.currency = function () {
$http.get('currencies',
{headers:
{'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': $rootScope.keyword_auth_token, 'Accept-Language': $cookies.get('type')}
})
.success(function (data) {
$scope.user_curr = data[0].code;
})
.error(function (data) {
console.log(data);
});
};
/*Currency dynamic ends here*/
$scope.currency();
}]);
Is there any way, any way, so that I can limit this thing?
It definitely is a bad idea to have multiple controllers for a single partial. You should consider using angular factories for maintaining data in such cases. But to provide you a short solution, you should remove the line $scope.currency(); from your controller (because it would make an api call as soon as your controller is initialized) and consider using ng-init built-in directive. So, basically in your partial where you are using ng-controller="AddProductController", you can add ng-init="currency()" (If you want to make an api call).
I always put the calls in a service, and then you can take full control. Something like this:
app.service("currencyService", function($q, $http) {
var _currencyPromise = null,
_currencies = null;
this.getCurrencies = function() {
var deferred = $q.defer();
// Check if the currencies are resolved before
// If so, immediately return these
if (_currencies) {
deferred.resolve(_currencies);
}
// Else check if the promise is already running
// If so, use that promise
else if (_currencyPromise) {
_currencyPromise.then(function(response) {
deferred.resolve(response.data);
});
}
// Else make the http call and assign to the promise
// So that the promise can be used instead of a new http call
else {
_currencyPromise = $http.get("..");
_currencyPromise.then(function(response) {
// Assign data to the currencies, so that it can be used
// by next calls immediately
_currencies = response.data;
deferred.resolve(_currencies);
}, function(error) {
// something went wrong
_currencyPromise = null;
deferred.reject();
});
}
return deferred.promise;
}
});
Then in your controllers you can always use this service, while the http call will only be made once:
app.controller("myCtrl", ["$scope", "currencyService", function($scope, currencyService) {
currencyService.getCurrencies().then(function(currencies) {
$scope.user_curr = currencies[0].code;
});
}]);
See this jsfiddle for reference. In the console you can see that the API is only called once.
I came up with a quite simple solution. For example I have a view like this
<div ng-controller="HomeController">
<div class="active tab-pane" ng-controller="AddProductController" ng-init="subcategories_id();currency();">
<p>{{user_curr}}</p>
</div><!--ends here->
<p>first controller {{abc}}</p>
</div>
I am using the nginitwhich works fine.

"is not a function" type error inside $scope.$on

I ran into a specific problem working with Angular. I have a parent controller named uploadController that has a validateFiles function to validate the uploaded files.
On a button click from UI , a modal popup is opening there I can input some values. On submit of that form I want to invoke that validateFiles method which is on other controller(the name of modal controller is UserUploadDataCtrl with the input data from the UI modal form).
I thought $rootScope.$broadcast in UserUploadDataCtrl and $scope.$on in uploadController would be great.
What I did is ...
if ($scope.groupMembershipUserInputForm.$valid) {
$scope.groupmembershipUploadParams = {
"chapterCode": $scope.groupmembership.chapterCode,
"groupCode": $scope.groupmembership.groupCode,
"createdDate": $filter('date')(new Date($scope.groupmembership.createdDate), 'MM/dd/yyyy'),
"endDate": $filter('date')(new Date($scope.groupmembership.endDate), 'MM/dd/yyyy')
}
$rootScope.$broadcast('validateUploadedFilesEvent', $scope.groupmembershipUploadParams);
and then in uploadController
// NOW VALIDATE THE UPLOADED THE FILES.
$scope.$on('validateUploadedFilesEvent', function (event, arg) {
UploadDataServices.setUploadParams(arg);
$scope.validateFiles();
});
It says UploadDataServices.setUploadParams(arg) is not a function.
But I have used it many times the same way .
UploadModule.factory('UploadDataServices', ['$http', '$rootScope', '$filter', function ($http, $rootScope, $filter) {
var uploadParams = null;
return {
setUploadValidationResult: function (result) {
uploadValidationResultData = result;
},
getUploadValidationResult: function () {
return uploadValidationResultData;
},
setUploadParams: function (params) {
uploadParams = params;
},
getUploadParams: function () {
return uploadParams;
}
....
Where am I doing wrong?
Here is the error
Here is the uploadController definition
UploadModule.controller('GroupMembershipUploadController', ['$scope', '$location', '$log',
'$window', '$localStorage', 'mainService', '$state', '$rootScope', '$http', '$uibModal', '$uibModalStack', 'uiGridConstants', 'UploadDataServices', 'UploadServices',
function ($scope, $location, $log, $window, $localStorage, mainService, $state, $rootScope, $http, $uibModal, $uibModalStack, uiGridConstants, UploadDataServices, UploadServices) {
I don't have an answer to your problem but I would suggest structuring your code differently. I've added it as an answer as this is too much for a comment.
In my opinion, the modal controller should only manage the lifecycle of the modal itself (close, cancel) passing a result back to the instance if needed.
The modal html should include an upload component. The upload component controller can have the upload data service injected and use it directly.
Apart from the separation of concerns, another benefit is that you can now do uploads independently of the modal (should you wish to).
You may be aware that services are singletons. Spreading your use case across multiple calls to the service may be considered a code smell and might encounter problems if it were servicing more than one controller.
For example,
Modal controller
For example, let the modal controller handle the form being cancelled. Using this pattern you could handle other actions from the form such as close.
angular.module('myapp').controller('ModalCtrl', function ($scope, $uibModalInstance) {
'use strict';
// handle a cancel on the form
$scope.cancel = function () {
$uibModalInstance.dismiss();
};
});
Modal html
<div class="deposit-funds">
<upload cancel="cancel()"></upload>
</div>
Upload directive
angular.module('myapp').directive('upload', function() {
'use strict';
return {
...
'scope': {
cancel: '&?' // let the modal controller close the modal on cancel
},
'controller': 'UploadCtrl',
...
};
});
Upload controller
angular.module('myapp').controller('UploadCtrl', function (uploadService) {
var that = this;
that.submit = function () {
uploadService.doStuff();
}
}

I can not get MEAN JS to create new customer

I am new to Node JS and MAEN stack , i am following a tutorial to MEANJS to learn it and in this screencast http://www.youtube.com/watch?v=HNpMCFB8TFI&list=PL6rhBJX0L3TWYrwrQIi1_MzQDvVhkUVPI&index=26 we get to create new customer . However, i am not able create a new customer as i am getting an error message says "object is not a function" which refers to this line "var customer = new Customers" as the console in google chrome suggests . here is the code
customersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
// Create new Customer
this.create = function() {
// Create new Customer object
var customer = new Customers ({
firstName: this.firstName,
surname: this.surname,
suburb: this.suburb,
country: this.country,
industry: this.industry,
email: this.email,
referred: this.referred,
phone: this.phone,
channel: this.channel
});
// Redirect after save
customer.$save(function(response) {
// Clear form fields
$scope.firstName = '';
$scope.surname = '';
$scope.suburb = '';
$scope.country = '';
$scope.industry = '';
$scope.email = '';
$scope.referred = '';
$scope.phone = '';
$scope.channel = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}
]);
kindly , note that the update function in the update controller works fine , here is the code .
customersApp.controller('CustomersUpdateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
// Update existing Customer
this.update = function(updatedCustomer) {
var customer = updatedCustomer;
customer.$update(function() {
//wont do anything as the modal will be closed
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}
]);
i really would appreciate your help , thanks in advance
In your dependencies list, you are passing in $stateParams, $location, and Authentication, which may not be needed.
CustomersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
Regardless, the order you specify in the dependencies array is the order that they will be passed into your controller. So, in your controller, "$scope" refers to $scope, but "Customers" refers to $stateParams.
You can probably change it to look like this:
CustomersApp.controller('CustomersCreateController', ['$scope', 'Customers',
function($scope, Customers ) {
Or, if you need those services:
CustomersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, $stateParams, $location, Authentication, Customers ) {
When you declare a controller in angular you pass in 2 parameters: the name of the controller and an array that contains all of the names of other variables and modules that the controller requires and finally the constructor method of the controller at the end of the array.
Now the method signature of the constructor method needs to line up with the array of parameters passed in through the array.
var myApp = angular.module('myApp');
myApp.controllers('myController', ['$scope', 'foo', 'bar', // note the position for these
function($scope, foo, bar) { // match the position of these
foo.doStuff(bar);
}
]);
So in your example you can see that you're mapping the parameters incorrectly
customersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) { // note the arrangement of these parameters
If you map the arrangement of the parameters in the function to what's declare in the array you'll see that the Customers object is actually mapped to the $stateParams object and you're calling that as a function.
If you arrange your required parameters to match the method signature of the constructor then your example should start to work (assuming that the other code is wired up correctly)
customersApp.controller('CustomersCreateController', ['$scope', 'Customers', '$stateParams', '$location', 'Authentication', // note that we moved 'Customers' to be next to '$scope'
function($scope, Customers ) {
The reason your other example works is that you're not actually using the Customers module anywhere in the code.
It also doesn't seem like you're using any of those other required variables and modules in these controller and if that is the case then you should remove those as well.
Now as to why angular does this? That's because when you put a JavaScript file through a minimizer tool it's very common for these tools to rename the passed in parameters of a function to reduce the over all size of a file.
E.g.
function(foo, bar) { /* code */ }
will become
function(a, b) { /* code */ }
Thus, this provides a way for angular to maintain a way to declare dependencies through modules and not have them removed via a minimizer.

Angular Modular File Structure

This post was based on this.
My intention is to separate components on a file basis. For example, I want a specific controller to have it's own file (Same goes with services, filters and directives). Of course, files will be group together based on the module they will fall into. Here's an overview of what I currently have:
Directory
User/
User/UserModule.js
User/UserDirective.js
User/UserService.js
User/UserFilter.js
User/UserController.js
UserModules.js
UserModule = angular.module('UserModule', []);
UserModule.controller('userCtrl', ['$scope', 'UserService', UserCtrl])
.factory('userService', function() {
return new UserService();
})
.filter('userFilter', UserFilter)
.directive('userDirective', UserDirective);
UserController.js
UserCtrl = function($scope, UserService) {
// ...
};
UserDirective.js
UserDirective = function() {
return {
// ...
}
};
UserService.js
UserService = function() {
// ...
};
UserFilter.js
UserFilter = function() {
return function() {
// ...
}
};
Then I'll just push the user module to the app module.
app.requires.push('UserModule');
My concern lies on the registration of the concepts (Such as controllers, services...) to the module. I was wondering if this is the best way to go and if it's correct. Also possible issues on the parameters and the external js file.
Consider this part:
.controller('userCtrl', ['$scope', 'UserService', UserCtrl])
The UserCtrl above refers to a function defined in a separate file. Will I be able to pass the $scope and UserService dependency as parameters to the UserCtrl?
UserCtrl = function($scope, UserService) { // Pass parameters (UserController.js)
What's the correct way of doing this in terms of Services, Filters and Directives?
Finally, how can I improve the code?
I'm also using Meteor btw.
You do not need to declare global variables UserModule, UserDirective, UserService. You just need to declare/register them as below. Angular will take care of injecting dependencies.
UserModule.js
angular.module('UserModule', []);
UserDirective.js
angular.module('UserModule').directive('userDirective', function() {
return {
// ...
}
});
UserService.js
angular.module('UserModule').service('UserService', function() {
});
UserController.js
angular.module('UserModule').controller('UserController', ['$scope', 'UserService', function($scope, UserService){
}])

Categories