How to get rid of code duplication in Angular controllers? - javascript

I have two controllers which perform very similar tasks.
What's the appropriate way to remove code duplication?
Initially I tried to move create a template method as an angular service, but it looks like I cannot inject $scope to services.
.controller("TspController", function($scope, $rootScope, $routeParams, $location, ProblemsLoader) {
$scope.problemType = "tsp";
var problemId = $routeParams.problemId;
ProblemsLoader.loadIndex("tsp", function(index) {
if (problemId) {
$scope.problems = index;
ProblemsLoader.loadProblem("tsp", problemId, function(problem) {
$scope.problem = problem
});
} else {
var firstProblemId = index[0].fileName;
$location.path("/tsp/" + firstProblemId)
}
});
})
.controller("VrpController", function($scope, $rootScope, $http, $routeParams, ProblemsLoader) {
$scope.problemType = "vrp";
var problemId = $routeParams.problemId;
ProblemsLoader.loadIndex("vrp", function(index) {
if (problemId) {
$scope.problems = index;
ProblemsLoader.loadProblem("vrp", problemId, function(problem) {
$scope.problem = problem
});
} else {
var firstProblemId = index[0].fileName;
$location.path("/vrp/" + firstProblemId)
}
});
});

Actually, services are a good solution for this use case, and injecting $scope isn't necessary.
When a service exposes an object property, implicit watches are set up on them, so state changes get propogated transparently.
Service:
var problemSvc = function($http) {
var problemData = {
problemId: 1,
problemField: '',
otherProblemField: ''
};
return {
problem: problemData, // exposing the data as object
loadProblem: function(problemId) {
// load logic here
problemData.problemField = 'load data here';
problemdata.otherProblemField = 'more data from server or whatever';
}
}
}
angular.service('problemSvc', ['$http', problemSvc]);
1-N consuming controllers:
angular.controller('ctrl', ['$scope', 'problemSvc', function($scope, problemSvc) {
$scope.problem = problemSvc.problem;
}]);
As fields change in your problem service, any controller where the service is injected will be updated automatically
<div>{{problem.problemField}}</div>

Related

Changing the date format if not correct in Javascript

I have created a mobile app that scans a QR-CODE that has various information embedded within it. One of the bits of information is a date.
There has been changes with in the QR-CODE from the previous versions with the format of the date. The previous date format was mm/dd and the new version is yy-MM-dd.
I am able to get it to scan the new version of the labels or the old ones, but not both. I need to get it to recognize bot types. I am not sure if there is a way to use an if statement to recognize the old format and convert it to the new format.
Here is an example of the working code using the new labels.
(function(){
'use strict';
var app = angular.module('Controllers', []);
var baseUrl = 'https://apps.laticrete.com/LSCWebApiService/lscapi/';
app.controller('BarcodeCtrl', ['$scope', '$state', '$stateParams', '$http', 'alertsManager', '$timeout', 'localStorageService', '$cordovaBarcodeScanner', '$cordovaGeolocation', '$filter',
function($scope, $state, $stateParams, $http, alertsManager, $timeout, localStorageService, $cordovaBarcodeScanner, $cordovaGeolocation, $filter) {
var SessionId = localStorageService.get('SessionId');
// Get GeoLocation
$cordovaGeolocation
.getCurrentPosition()
.then(function(position) {
$scope.lat = position.coords.latitude;
$scope.long = position.coords.longitude;
});
document.addEventListener("deviceready", function() {
$scope.scanMaterial = function() {
$cordovaBarcodeScanner
.scan()
.then(function(result) {
var codeArray = result.text.split(',');
$scope.SKU = codeArray[1].replace("I:", "").trim();
$scope.ControlNumber = codeArray[0].replace("W:", "").trim();
//$scope.ManufactureDate = codeArray[2].replace("MFG:", "").trim();
$scope.ManufactureDate = codeArray[2].replace("MFG:", "20").replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3").trim();
$scope.BatchCode = codeArray[3].replace("B:", "").trim();
var dataObj = {
SessionId: SessionId,
JobId: $stateParams.JobId,
ManufactureDate: $scope.ManufactureDate,
BatchCode: $scope.BatchCode,
SKU: $scope.SKU,
ControlNumber: $scope.ControlNumber,
CreatedClient: new Date(),
Latitude: $scope.lat,
Longitude: $scope.long,
Product: { Id : 1}
};
$http.post(baseUrl + 'Material/PostNewMaterial', dataObj)
.success(function() {
alertsManager.addAlert('Success: You have successfully added material.', 'alert-success');
$state.go('^');
}).error(function(dataObj) {
alertsManager.addAlert('Failure Meesage: ' + JSON.stringify({dataObj:dataObj}), 'alert-danger');
});
$timeout(function(){
alertsManager.clearAlerts();
}, 5000);
}, function(error) {
console.log("An error has happened " + error);
});
};
}, false);
}]);
})();
The part of the code I am asking for help on is $scope.ManufactureDate
Thank You in advance.
Check if the code matches one of the formats and if it does, parse it accordingly.
if (codeArray[2].match(/MFG:\d+-\d+-\d+/) !== null) {
/* The format is of 'MFG:000-000-000', \d+ matches 1..n numbers */
...
}
Or you could combine these to a one regex:
let matches = codeArray[2].match(/MFG:(20(\d{2})-\d{2}-\d{2})|(\d{2}/\d{2}))/);
See String.prototype.match

Please suggest how to write a Jasmine Test case for below angularjs code

In below example I want to mock dataService.The problem is I need to test the functionality after the data is returned under function(data).
As the logic is dependent on the data returned.I think mocking/Spying the dataservice might not help in this regard.
Kindly provide the probable solution.
var app = angular.module('app', []);
app.controller('Controller', ['$scope', '$location', 'dataService', '$window', 'OtherService', 'Config', function ($scope, $location, dataService, $window, SomeService, Config) {
$scope.btnClick = function () {
var SomeNumber = $scope.enteredNumber;
if (SomeNumber == "xyz") {
$scope.errorType = 'errorRequired';
}
else {
dataService.validate_enteredNumber(SomeNumber, function (data) {
if (data != null) {
if (data.responsetype == 1) {
$scope.errortype = data.responsemessage;
}
else {
dataservice.getdetailsforenterednumber(data.responsemessage, function (getdata) {
if (getdata.responsetype == 1) {
$scope.errortype = getdata.responsemessage;
}
else if (getdata.responsetype == 2) {
if (getdata.responsemessage.touppercase() == "consumer")
$window.location.href = "url";
}
else
$location.path('/Somelist').search({ SomeNumber: getdata.responsemessage });
});
}
}
});
}
}
if ($location.search().SomeNumber != null) {
$scope.enteredNumber = $location.search().SomeNumber;
$scope.btnClick();
}
} ]);
I'm not entirely sure what you are after. Assuming you just want to test the functionality after the validate_enteredNumber function is called, you can still spy on the function and provide a mock result.
spyOn(data_service, 'validate_enteredNumber').andReturn(mockObjectYouCreate);

How to use JavaScript setTimeout() in this scenario

In my Angular app, the app.js code retrieves a sessionID and a userID from the server side.
At the same time, my navigation controller is setting up a menu which also comes from the server side.
The problem is that my navigation controller is executing BEFORE the app.js routine. And the result is that my sessionID and userID variables are assigned yet.
Ideally, I would like to know how to get my app.js code to fire before anything.
However, in the meantime, I would like to use the JavaScript setTimeout() function to simulate a deferred call. Meaning, I only want to continue when my sessionID and userID vars are defined.
My app.js code (where the session and user vars are initialized):
(function () {
'use strict';
angular.module('rage', [
'ui.router',
'ui.bootstrap',
'ui.dashboard',
'kendo.directives'
]).run(['$rootScope', '$state', 'userService', init]);
function init($rootScope, $state, userService) {
$rootScope.rageSessionVars = {};
$rootScope.$state = $state;
userService.getInitParams().then(function (razorEnvJson) {
$rootScope.rageSessionVars = razorEnvJson;
userService.init(rzEnvJson).then(function (data) {
var response = data.status;
if (response.match(/SUCCESS/g)) {
userService.openUserSession(razorEnvJson).then(function (data) {
var sessionID = data.data[0];
$rootScope.rageSessionVars.sessionID = sessionID;
$rootScope.rageSessionVars.userID = "bobmazzo1234";
console.log("sessionID = " + sessionID);
$rootScope.rageSessionVars.currDashboardName = "Default";
});
}
});
});
}
})();
The navigation-controller.js code (where the user and session vars are not defined yet):
function activate() {
$timeout(getDashboards, 1000); // give time for app.js to init vars
}
function getDashboards() {
// GET ALL DASHBOARDS VIA API.
var timeout;
if ($rootScope.rageSessionVars.sessionID == undefined) {
?? SHOULD I DO SOMETHING HERE IN CASE ???
}
var sid = $rootScope.rageSessionVars.sessionID;
var userId = $rootScope.rageSessionVars.userID;
dashboardcontext.getDashboards(sid, userId);
}
You could use $interval in your controller to check every x number of milliseconds if the userID and sessionID have returned, like this:
var stop = $interval(function() {
if ($rootScope.rageSessionVars.sessionID != undefined) {
//SUCCESS CODE HERE - DON'T FORGET TO STOP THE INTERVAL!
$interval.cancel(stop);
}
}, 100);
I've used 100 milliseconds, change to suit your needs. $interval API
You can use watcher:
$rootScope.$watch(function () {
return $rootScope.rageSessionVars.sessionID;
}, function (newValue, oldValue) {
if (newValue !== void 0) {
foo();
}
});
But i think you need use promises.

Clearing a page and reloading a controller?

So I have a small angular app that takes in a search query, sends it to an elasticsearch node I've got set up, and then displays the result set on screen.
My problem is that when I make a new query, the results gets appended to the end of the current result set. What I would like it to do is to erase whatever is currently on the page, and reload it with only the new data, much like how searching for something on Google returns a completely new set of results.
Is there any way to do this? Code below for reference.
// this is the controller that displays the reuslts.
var displayController = function($scope, $rootScope, $window, notifyingService) {
var dataReady = function(event, data) {
$scope.resultSet = notifyingService.getData();
}
$rootScope.$on('data-ready', dataReady)
}
app.controller("displayController", ["$scope", "$rootScope", "$window", "notifyingService", displayController]);
// this is the service that's responsible for setting the data
var notifyingService = function($http, $rootScope) {
var svc = {
_data: [],
setData: setData,
getData: getData
};
function getData() {
return svc._data;
}
function setData(data) {
var base_obj = data.hits.hits
console.log("Setting data to passed in data.");
console.log('length of dataset: ' + base_obj.length);
for(var i = 0; i < base_obj.length; i++){
svc._data.push(base_obj[i]._source);
}
$rootScope.$broadcast('data-ready', svc._data);
}
return svc;
};
app.factory("notifyingService", ["$http", "$rootScope", notifyingService]);
In setData just before the loop re-initialize svc._data
svc._data = [];
Clear you svc._data before you start adding the new query.
function setData(data) {
var base_obj = data.hits.hits;
svc._data = [];//reset your array before you populate it again.
for(var i = 0; i < base_obj.length; i++){
svc._data.push(base_obj[i]._source);
}
$rootScope.$broadcast('data-ready', svc._data);
}

AngularJS SPA using API and Mongoose

I am creating a simple AngularJS SPA using an API to load data into Mongoose.
My app just adds, displays and edits a list of members. It works when I just store the members in an array in my factory service but now I want to change it to hook up to Mongoose via an API.
Factory
app.factory('SimpleFactory', ['$http', function($http){
var factory = {};
var members = $http.get('/api/members')
factory.getMembers = function ()
{
return members = $http.get('/api/members');
}
factory.getMember = function (index) {
if (index >=0 && index < members.length ) {
return members[index] = $http.get('/api/members/' + member_id )
}
return undefined
}
factory.addMember = function(member) {
return $http.post('/api/members',member)
}
factory.updateMember = function(index,member) {
$http.put('/api/members/' + member_id, member)
}
return factory;
}])
Controller
app.controller('MembersController', ['$scope','SimpleFactory',
function ($scope,SimpleFactory) {
SimpleFactory.getMembers()
.success(function(members) {
$scope.members = members;
});
$scope.addMember = function()
{
var member = {
name: $scope.newMember.name,
address: $scope.newMember.address,
age : $scope.newMember.age,
level : $scope.newMember.level,
swimmer : $scope.newMember.swimmer,
email : $scope.newMember.email,
regdate : $scope.newMember.regdate,
}
SimpleFactory.addMember(member)
.success(function(added_member)
{
$scope.members.push(added_member);
$scope.newMember = { }
} );
}
}])
But I am not sure how to change my controller for updating a member, it is coded as follows to pick up the members from an array in my factory setting, how do I code it to pick up members from Mongoose via API:
app.controller('MemberDetailController', ['$scope', '$location', '$routeParams', 'SimpleFactory',
function($scope, $location, $routeParams, SimpleFactory) {
$scope.member = {
index: $routeParams.member_index,
detail: SimpleFactory.getMember($routeParams.member_index)
}
$scope.updateMember = function() {
SimpleFactory.updateMember($scope.member.index,
$scope.member.detail)
$location.path('/members')
}
}
])
Can anyone help, its not a complicated app but I'm only learning and I am stuck here!
Thanks
You $scope.member object should set after getMember promise success.
Code
SimpleFactory.getMember($routeParams.member_index).then(function(data){
$scope.member = {
index : $routeParams.member_index,
detail : data.user
};
});
Apart from that you need to make sure getMember method should always return a promise while index is 0
factory.getMember = function (index) {
var deferred = $q.defer();
if (index >=0 && index < members.length ) {
return members[index] = $http.get('/api/members/' + member_id )
}
deferred.resolve;
}
Update
For calling update method you need to do change service first which would return a promise
factory.updateMember = function(index,member) {
return $http.put('/api/members/' + member_id, member)
}
Then call factory.updateMember resolve that promise and then do $location.path
$scope.updateMember = function() {
SimpleFactory.updateMember($scope.member.index, $scope.member.detail)
.then(function(data) {
$location.path('/members')
});
};

Categories