In my case, using AngularJS, i have a json with some configurations that can be used in controllers.
The problem happens when i minify the application and the controller variables name does not match with the json.
Is there a way to not minify a single variabe?
My code:
setting.json
{
"settings": {
"adIndex": 3
}
}
controller:
function settingsService($q, $http, $log) {
var settings = {
adIndex: 0
};
var service = {
init: init,
getAdIndex: getAdIndex
};
return service;
function init() {
var deferred = $q.defer();
$http.get('assets/settings/settings.json').then(function(response) {
var loaded = response.data.settings;
if (loaded) {
settings = angular.extend(settings, loaded);
}
deferred.resolve(settings);
}, function(error) {
$log.info('Error loading settings', error);
deferred.reject(error);
});
return deferred.promise;
}
function getAdIndex() {
return settings.adIndex;
}
}
UPDATE
After your suggestions i made some changes on code, but the problem not yet resolved.
The code in controller is now:
function settingsService($q, $http, $log) {
var settings = {
'adIndex': 0
};
var service = {
init: init,
getAdIndex: getAdIndex
};
return service;
function init() {
var deferred = $q.defer();
$http.get('assets/settings/settings.json').then(function(response) {
var loaded = response.data['settings'];
if (loaded) {
settings = angular.extend(settings, loaded);
}
deferred.resolve(settings);
}, function(error) {
$log.info('Error loading settings', error);
deferred.reject(error);
});
return deferred.promise;
}
function getAdIndex() {
$log.debug('Object: ', settings); // result: settings { adIndex: 3 }
$log.debug('Value: ', settings['adIndex']); // result: 0
return settings['adIndex'];
}
}
How can 'adIndex' have 2 different values in logs?
var settings = {
"adIndex": 0 // quotes around the key, JSON style
};
Not 100% sure how your minifier will handle this, but usually they can not modify string values. If the above doesn't work, you can do something slightly more verbose:
var settings = {};
settings['adIndex'] = 0;
In this case, the variable settings will be minified, but the string 'adIndex' will remain unchanged.
Related
I'm currently working on a project to help me better understand angularjs! I am currently stuck on how to pass a parameter from the controller to service.
In my program, I have created a function called "GetForecastByLocation" when a user types in an input clicks on a button. From there I want to take their input and then pass it to the http call in service.js.
Originally, $http.get was in a long giant string of the API url, but I googled around and it seems that I'm supposed to use parameters when trying to change a portion of the string. As of right now, I know parameter is hardcoded to a specific city, but I want to take new input and pass the value of vm.city to the $http.get call.
If any one can help I would greatly appreciate it. Thank you!
controller.js
var app = angular.module('weatherApp.controllers', [])
app.controller('weatherCtrl', ['$scope','Data',
function($scope, Data) {
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);},
Data.getApps(city)
.then(function(data)){
//doing a bunch of things like converting units, etc
},
function(res){
if(res.status === 500) {
// server error, alert user somehow
} else {
// probably deal with these errors differently
}
}); // end of function
}]) // end of controller
service.js
.factory('Data', function($http, $q) {
var data = [],
lastRequestFailed = true,
promise;
return {
getApps: function() {
if(!promise || lastRequestFailed) {
promise = $http.get('http://api.openweathermap.org/data/2.5/weather?',{
params: {
q: Tokyo,
}
})
.then(function(res) {
lastRequestFailed = false;
data = res.data;
return data;
}, function(res) {
return $q.reject(res);
});
}
return promise;
}
}
});
Passing arguments to a factory method is no different than passing arguments to a plain old function.
First, set up getApps to accept a parameter:
.factory('Data', function($http, $q){
// ...
return {
getApps: function(city){
promise = $http.get(URL, {
params: {q: city}
}).then( /* ... */ );
// ...
return promise;
}
};
});
Then pass it your argument:
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);
}
It's just like setting a value to a function's context variable.
Services.js
Simple example of a service.
.factory('RouteService', function() {
var route = {}; // $Object
var setRoute_ = function(obj)
{
return route = obj;
};
var getRoute_ = function()
{
if(typeof route == 'string')
{
return JSON.parse(route);
}
return null;
};
return {
setRoute: setRoute_,
getRoute: getRoute_
};
})
Controllers.js
Simple example of Service usage:
.controller('RoutesCtrl', function ($scope, RouteService) {
// This is only the set part.
var route = {
'some_key': 'some_value'
};
RouteService.setRoute(route);
})
I have two modules "core" and "ui".
The ui module depends on core. This is the code for my core.js :
var core = angular.module('core', [ 'ngRoute' ]);
//Services
core.service('httpInformationService', function() {
this.requestCount = 0;
this.responseCount = 0;
this.incrementRequest = function() {
this.requestCount++;
console.log('incrementRequest:' + this.requestCount);
};
this.incrementReponse = function() {
this.responseCount++;
}
this.decrementRequest = function() {
this.requestCount--;
console.log('decrementRequest:' + this.requestCount);
};
this.decrementResponse = function() {
responseCount--;
}
this.getRequestCount = function() {
return requestCount;
}
this.getResponseCount = function() {
return responseCount;
}
});
//Service provider
core.provider("httpServiceInformationProvider", function() {
var provider = {};
provider.$get = ['httpInformationService', function( service ) {
return service;
}];
return provider;
});
//HTTP Interceptor
core.factory('coreHttpInterceptor' ,function( httpInformationService ){
var coreHttpInterceptor = {
request: function(config) {
httpInformationService.incrementRequest();
return config;
},
response: function(response) {
httpInformationService.decrementRequest();
return response;
}
}
return coreHttpInterceptor;
});
var config = {
base_url: enviromnent_url,
}
core.value('config', config);
core.config(function( $interpolateProvider ) {
$interpolateProvider.startSymbol( "[[" ).endSymbol( "]]" );
});
core.config(function( $httpProvider ) {
$httpProvider.interceptors.push('coreHttpInterceptor');
});
This is my ui.js code:
var ui = angular.module('ui',[ 'core' , 'ui.bootstrap' ]);
ui.directive( "shLoadify" , function( httpServiceInformationProvider ){
return {
restrict: "AE",
link: function(scope, element, attrs) {
element.bind( "click", function() {
element.text("Loading...");
element.prop( "disabled", true );
});
},
controller: function($scope) {
$scope.$watch('httpServiceInformationProvider', function(oldValue, newValue){
console.log(oldValue + ' ' + newValue);
}, true);
}
}
});
As you can see i am trying to access requestCount property of httpInfomationService from within my controller using $scope.watch.
The problem is newValue and oldValue is always null. Why is that so?
Approach 1
If you want to perform some action whenever your requestCount variable gets changed which is part of service, you need to broadcast/emit which then you can listen through on. But in this case you need to pass the scope in your service which is not recommended.
var app = angular.module('app',['app1']);
app.service('myService',function($rootScope){
this.requestCount=1
this.incrementRequestCount=function(){
this.requestCount++
$rootScope.$broadcast('requestCountChanged', { message: this.requestCount });
}.bind(this)
})
app.controller('myController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
$scope.click= myService.incrementRequestCount;
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])
Approach 2
Without passing scope in the service
var app = angular.module('app',['app1']);
app.service('myService',function(){
this.requestCount=1
this.incrementRequestCount=function(){
debugger;
this.requestCount++
}.bind(this)
})
app.controller('myController',['$scope','myService','$rootScope',function($scope,myService,$rootScope){
$scope.click=function(){
myService.incrementRequestCount();
$rootScope.$broadcast('requestCountChanged', { message: myService.requestCount });
}
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])
Approach 3
You can only attach watch to those properties which are actually in the scope otherwise you cannot have watch for those properties. So just add requestCount on you scope than you can easily detect its changes using watch and then use broadcast/emit approach.
var app = angular.module('app',['app1']);
app.service('myService',function(){
this.requestCount=1
this.incrementRequestCount=function(){
debugger;
this.requestCount++
}.bind(this)
})
app.controller('myController',['$scope','myService','$rootScope',function($scope,myService,$rootScope){
$scope.requestCount=myService.requestCount
$scope.$watch('requestCount',function(n,o){
debugger;
if(n!=o)
{
$rootScope.$broadcast('requestCountChanged', { message: n });
}
})
$scope.click=function(){
myService.incrementRequestCount();
$scope.requestCount=myService.requestCount
}
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])
Here is my service
angular.module('starter.services').factory('PaypalService', function ($q, $ionicPlatform, shopSettings, $filter, $timeout) {
var init_defer;
var service = {
initPaymentUI: initPaymentUI,
createPayment: createPayment,
configuration: configuration,
onPayPalMobileInit: onPayPalMobileInit,
makePayment: makePayment
};
return service;
function initPaymentUI() {
init_defer = $q.defer();
$ionicPlatform.ready().then(function () {
var clientIDs = {
"PayPalEnvironmentProduction": shopSettings.payPalProductionId,
"PayPalEnvironmentSandbox": shopSettings.payPalSandboxId
};
PayPalMobile.init(clientIDs, onPayPalMobileInit);
});
return init_defer.promise;
}
function createPayment(total, name) {
// "Sale == > immediate payment
// "Auth" for payment authorization only, to be captured separately at a later time.
// "Order" for taking an order, with authorization and capture to be done separately at a later time.
var payment = new PayPalPayment("" + total, "USD", "" + name, "Sale");
return payment;
}
function configuration() {
// for more options see `paypal-mobile-js-helper.js`
var config = new PayPalConfiguration({merchantName: shopSettings.payPalShopName});
return config;
}
function onPayPalMobileInit() {
$ionicPlatform.ready().then(function () {
// must be called
// use PayPalEnvironmentNoNetwork mode to get look and feel of the flow
PayPalMobile.prepareToRender(shopSettings.payPalEnv, configuration(), function () {
$timeout(function () {
init_defer.resolve();
});
});
});
}
function makePayment(total, name) {
var defer = $q.defer();
total = $filter('number')(total, 2);
$ionicPlatform.ready().then(function () {
PayPalMobile.renderSinglePaymentUI(createPayment(total, name), function (result) {
$timeout(function () {
defer.resolve(result);
});
}, function (error) {
$timeout(function () {
defer.reject(error);
});
});
});
return defer.promise;
}
})
and my settings
.constant('shopSettings',{
payPalSandboxId : 'id',
payPalProductionId : 'id',
payPalEnv: 'PayPalEnvironmentSandbox',
payPalShopName : 'app_name'
});
All I need is to make standard payment for user, so some user can log in in personal account, pay for some stuff and money will appear in my account.By the way everything works fine if I log in with sandbox test accounts, but not with real one's.
I have a controller which has a function to get some alerts from an API and update a count on the front-end of my site which is bound to the alert.
Unfortunately the ng-bind attribute I'm using doesn't seem to be updating the count live, even though a simple console.log() is telling me that the actual alert count is being updated in the controller.
Front-end
<div class="modeSelector modeSelector_oneUp" data-ng-controller="MyLivestockController as vm">
<a class="modeSelector-mode" data-ui-sref="my-livestock">
<div class="modeSelector-type">Alerts</div>
<img class="modeSelector-icon" src="/inc/img/_icons/envelope-black.svg" onerror="this.src=envelope-black.png" />
<span data-ng-bind="vm.alertCount"></span>
</a>
</div>
Controller
(function() {
'use strict';
function MyLivestockController(userService) {
var vm = this;
vm.myLivestockNotification = {
isLoading: true,
hasError: false
};
vm.alertsNotification = {
isLoading: true,
hasError: false,
hasData: false
};
vm.deleteAlert = function(id) {
vm.currentAlert = void 0;
vm.alertsNotification.isLoading = true;
userService.deleteAlert(vm.user.id, id).then(function() {
// Remove the alert from our Array
vm.alerts = vm.alerts.filter(function(alert) {
return alert.id !== id;
});
// Refresh the alert count for the user
vm.getAlerts(vm.user.id);
vm.alertsNotification.isLoading = false;
vm.alertsNotification.hasError = false;
}, function() {
vm.alertsNotification.hasError = true;
});
};
vm.getAlerts = function(id) {
userService.getAlerts(id).then(function(alertData) {
vm.alertCount = alertData.length;
if (vm.alertCount > 0) {
vm.alertsNotification.hasData = true;
} else {
vm.alertsNotification.hasData = false;
}
vm.alerts = alertData;
vm.alertsNotification.isLoading = false;
vm.alertsNotification.hasError = false;
}, function() {
vm.alertsNotification.hasError = true;
});
};
// Init
(function() {
userService.getCurrentUser().then(function(data) {
vm.myLivestockNotification.hasError = false;
vm.myLivestockNotification.isLoading = false;
vm.user = data;
// Get alert count for the user
vm.getAlerts(vm.user.id);
}, function() {
vm.myLivestockNotification.hasError = true;
});
})();
}
angular
.module('abp')
.controller('MyLivestockController', MyLivestockController);
})();
Service
(function() {
'use strict';
function userService($q, $sessionStorage, $localStorage, $filter, user) {
var service = this;
service.getAlerts = function(id) {
var deferred = $q.defer();
user.alerts({ userID: id }, function(response) {
if (response.hasOwnProperty('data')) {
// Convert dates to valid Date
angular.forEach(response.data, function(alert) {
/* jshint camelcase: false */
if (alert.created_at) {
alert.created_at = $filter('abpDate')(alert.created_at);
/* jshint camelcase: true */
}
});
deferred.resolve(response.data);
}
else {
deferred.reject('DATA ERROR');
}
}, function(e) {
deferred.reject(e);
});
return deferred.promise;
};
angular
.module('abp')
.service('userService', userService);
})();
As you can see, I've got my getAlerts() function being called every time an alert is deleted, using the deleteAlert() function, but the <span data-ng-bind="vm.alertCount"></span> on the front-end only updates after refreshing the page, where I'd like it to update live.
Your bind is not updating because you change the value of alertCount outside of digest cycle of your angular app. When you refresh your app, the digest runs and thus your value gets updated. Wrap the update of the variable in $scope.apply() like so:
$scope.$apply(function(){
vm.alertCount = alertData.length;
});
This will force digest and update the value live.
If you have more values that are updated outside of digest (any callback, promise etc) you can force digest cycle by calling:
$scope.$apply();
Hope it helps.
EDIT -----
Given your update with full code, I see that you are not injecting scope anywhere in your controller, the controllers I write usually start like that:
(function () {
var app = angular.module('mainModule');
app.controller('myController', ['$scope', '$myService', function ($scope, $myService) {
//logic
}]);
}());
EDIT -----
Here is a quick go I had on your code:
(function() {
'use strict';
var app = angular.module('abp');
app.controller('MyLivestockController', ['$scope', 'userService', function($scope, userService) {
var vm = {};
$scope.vm = vm;
vm.myLivestockNotification = {
isLoading: true,
hasError: false
};
vm.alertsNotification = {
isLoading: true,
hasError: false,
hasData: false
};
vm.deleteAlert = function(id) {
vm.currentAlert = void 0;
vm.alertsNotification.isLoading = true;
userService.deleteAlert(vm.user.id, id).then(function() {
// Remove the alert from our Array
vm.alerts = vm.alerts.filter(function(alert) {
return alert.id !== id;
});
// Refresh the alert count for the user
vm.getAlerts(vm.user.id);
vm.alertsNotification.isLoading = false;
vm.alertsNotification.hasError = false;
}, function() {
vm.alertsNotification.hasError = true;
});
};
vm.getAlerts = function(id) {
userService.getAlerts(id).then(function(alertData) {
vm.alertCount = alertData.length;
if (vm.alertCount > 0) {
vm.alertsNotification.hasData = true;
} else {
vm.alertsNotification.hasData = false;
}
vm.alerts = alertData;
vm.alertsNotification.isLoading = false;
vm.alertsNotification.hasError = false;
//important, this is promise so we have to apply the scope to update view
$scope.$apply();
}, function() {
vm.alertsNotification.hasError = true;
});
};
// Init
(function() {
userService.getCurrentUser().then(function(data) {
vm.myLivestockNotification.hasError = false;
vm.myLivestockNotification.isLoading = false;
vm.user = data;
// Get alert count for the user
vm.getAlerts(vm.user.id);
}, function() {
vm.myLivestockNotification.hasError = true;
});
})();
}]);
})();
The general idea is:
you create an app (angular.module)
you create a controller in this app, with $scope injected
any values you want to be updated on your view, you add to $scope
if you have any $scope updates in a callback, event or promise, you wrap them in (or follow with) $scope.$apply call
I think this should work for you :)
I have attempted to reproduce your code below with a mock userService, and some slight modifications to the html view so we can more clearly see the alerts and delete them. I have not modified your Controller.
This appears to work, yes?
Which leads me to believe there may be some issue with the implementation of your userService. If you are able to post the relevant code, I can update this answer with a clarified solution.
UPDATE: As you've updated your question with the userService code, I've updated the below to more closely match. I still have a mock service standing in place of the user dependency of the userService. Additionally I made a couple of small edits to the Controller class so that while promises are still resolving we can see 'Updating...' in place of the alerts count.
This all still appears to work, unless I'm misunderstanding - will think on it more and update this 'answer' when I can think of where else to investigate for the source of the issue, see if we can at least reproduce it!
(function() {
'use strict';
function MyLivestockController(userService) {
var vm = this;
vm.myLivestockNotification = {
isLoading: true,
hasError: false
};
vm.alertsNotification = {
isLoading: true,
hasError: false,
hasData: false
};
vm.deleteAlert = function(id) {
vm.currentAlert = void 0;
vm.alertsNotification.isLoading = true;
return userService.deleteAlert(vm.user.id, id).then(function() {
// Remove the alert from our Array
vm.alerts = vm.alerts.filter(function(alert) {
return alert.id !== id;
});
// Refresh the alert count for the user
vm.getAlerts(vm.user.id).then(function() {
vm.alertsNotification.isLoading = false; //put here, loading isn't really finished until after .getAlerts() is done
vm.alertsNotification.hasError = false;
});
}, function() {
vm.alertsNotification.hasError = true;
});
};
vm.getAlerts = function(id) {
vm.alertsNotification.isLoading = true;
return userService.getAlerts(id).then(function(alertData) { //return the promise so we can chain .then in .deleteAlert()
vm.alertCount = alertData.length;
if (vm.alertCount > 0) {
vm.alertsNotification.hasData = true;
} else {
vm.alertsNotification.hasData = false;
}
vm.alerts = alertData;
vm.alertsNotification.isLoading = false;
vm.alertsNotification.hasError = false;
}, function() {
vm.alertsNotification.hasError = true;
});
};
// Init
(function() {
userService.getCurrentUser().then(function(data) {
vm.myLivestockNotification.hasError = false;
vm.myLivestockNotification.isLoading = false;
vm.user = data;
// Get alert count for the user
vm.getAlerts(vm.user.id);
}, function() {
vm.myLivestockNotification.hasError = true;
});
})();
}
function userMock($q, $timeout, $log) {
var _alerts = {
data: [{
id: 1,
message: "He doesn't sleep, he waits..."
}, {
id: 2,
message: "He doesn't mow his lawn, he stands outside and dares it to grow."
}, {
id: 3,
message: "Some magicians can walk on water. He can swim through land."
}]
},
_currentUser = {
id: 'Q2h1Y2sgTm9ycmlz'
};
return {
getCurrentUser: function getCurrentUser() {
$log.log("getCurrentUser");
//return $q.when(_currentUser);
return $timeout(function() { //use $timeout to simulate some REST API latency...
return _currentUser;
}, 500);
},
getAlerts: function getAlerts(id) {
$log.log("getAlerts: " + id); //not doing anything with the id in this mock...
$log.log(_alerts.data);
//return $q.when(_alerts);
return $timeout(function() {
return _alerts;
}, 500);
},
deleteAlert: function deleteAlert(userId, id) {
$log.log("deleteAlert: " + userId + " :: " + id);
//return $q.when(_alerts);
return $timeout(function() {
for (var i = 0; i < _alerts.data.length; i++) {
if (_alerts.data[i].id === id) {
_alerts.data.splice(i, 1);
$log.log("alert found and deleted");
break;
}
}
$log.log(_alerts.data);
return _alerts;
}, 500);
}
};
}
function userService($q, $timeout, $log, userMock) {
var service = this;
service.getCurrentUser = userMock.getCurrentUser;
service.getAlerts = function(id) {
var deferred = $q.defer();
userMock.getAlerts(id).then(function(response) {
if (response.hasOwnProperty('data')) {
// Convert 'he' to 'Chuck Norris'
angular.forEach(response.data, function(alert) {
if (alert.message) {
alert.message = alert.message.replace(/he/gi, "Chuck Norris");
}
});
deferred.resolve(response.data);
} else {
deferred.reject('DATA ERROR');
}
}, function(e) {
deferred.reject(e);
});
return deferred.promise;
};
service.deleteAlert = function(userId, id) {
var deferred = $q.defer();
userMock.deleteAlert(userId, id).then(function(response) {
deferred.resolve(response.data);
}, function(e) {
deferred.reject('DATA ERROR');
});
return deferred.promise;
};
return service;
};
angular
.module('abp', [])
.service('userMock', userMock)
.service('userService', userService)
.controller('MyLivestockController', MyLivestockController);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<div ng-app="abp">
<div data-ng-controller="MyLivestockController as vm">
<div>Alerts</div>
<span data-ng-bind="vm.alertsNotification.isLoading ? 'Updating...' : vm.alertCount"></span>
<div data-ng-repeat="alert in vm.alerts">
{{alert.id}}: {{alert.message}}
<button ng-click="vm.deleteAlert(alert.id)">Delete</button>
</div>
</div>
</div>
Im just starting on AngularJS. I'm not sure how to churn this out. I'm trying to include multiple functions within one service. (I hope this is not against bad practice.)
The following is my working code:
myDataService.async().then(function (d) {
$scope.dbCalls = d.d;
});
My Service:
app.factory('myDataService', function ($http) {
// How do you get this bottom line to work?
// this.getAllCalls = function () {
var myService = {
async: function () {
var promise = $http.post('AngularTest.aspx/FetchCalls', { data: {} }).then(function (response) {
console.log(response);
return response.data;
});
return promise;
}
};
return myService;
//}; <--Commented out for clarity
});
Thanks!
you just return an object with properties from the service, then you are able to call those properties as different service methods
like so:
.service('myService', function() {
return {
firstMethod: function() { ... },
secondMethod: function() { ... },
thirdMethod: function() { ... }
}
})
and in the controller/directive
.controller('myCtrl', function(myService) {
myService.firstMethod();
myService.secondMethod();
myService.thirdMethod();
})