AngularJS Error: d[h].apply is not a function - angularjs

When I navagate away from a page in my angular site the error console fills up (occurs 108 times) with the error:
Error: d[h].apply is not a function
jf/this.$get</n.prototype.$broadcast#http://localhost:9885/Scripts/angular.min.js:137:355
jf/this.$get</n.prototype.$destroy#http://localhost:9885/Scripts/angular.min.js:133:254
ye</<.link/<#http://localhost:9885/Scripts/angular.min.js:252:477
jf/this.$get</n.prototype.$digest#http://localhost:9885/Scripts/angular.min.js:132:257
jf/this.$get</n.prototype.$apply#http://localhost:9885/Scripts/angular.min.js:135:267
Kc[c]</<.compile/</<#http://localhost:9885/Scripts/angular.min.js:252:124
n.event.dispatch#http://localhost:9885/Scripts/jquery-bundle.js:3:6414
n.event.add/r.handle#http://localhost:9885/Scripts/jquery-bundle.js:3:3224
It only occurs on this one page but I have no $watches or $broadcast events on it. Could someone help me by suggesting where I should look to find the trigger for this error?
I appreciate not having the code makes this difficult but I am keen to have some suggestions on what things cause errors like this and/or the best way to debug it.
UPDATE
app.controller('ticketController', ['$scope', '$state', 'Page', 'globals', 'localStorageService', 'Ticket', 'User', 'ticketData', 'ELearning', 'dialogs', 'Notification', 'Payment','Note', 'DTOptionsBuilder', 'DTColumnDefBuilder', 'History', 'Correspondance',function ($scope, $state, Page, globals, localStorageService, Ticket, User, ticketData, ELearning, dialogs, Notification, Payment,Note, DTOptionsBuilder, DTColumnDefBuilder, History, Correspondance) {
if (globals.debug) { console.log('Ticket controller loaded'); }
$scope.globals = globals;
Page.setTitle(ticketData.forename + ' ' + ticketData.surname);
$scope.authData = localStorageService.get('authorizationData');
$scope.ticket = ticketData;
$scope.user = User.admindata({ id: ticketData.ticketGUID });
$scope.person = {};
$scope.reOpenTicket = {
isOpen: false,
previousChanges: [],
newCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').format('DD MMM YYYY'),
minCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(-1, 'd').format('DD MMM YYYY'),
maxCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').add(1, 'd').format('DD MMM YYYY'),
minErrorDate: moment(new Date($scope.ticket.criticalDate)).format('DD MMM YYYY'),
maxErrorDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').format('DD MMM YYYY'),
reason: '',
form: {},
saving: false,
saveError: ''
};
$scope.notes = {
data: [],
newNote: '',
loading: false,
loadError: ''
};
$scope.payments = {
data: [],
loading: true,
dtOptions: {},
dtColumnDefs: {},
ticketGUID: ''
};
$scope.learning = {
data: [],
loading: true,
ticketGUID: '',
dtOptions: {},
dtColumnDefs: {}
};
$scope.history = {
data: [],
loading: true,
loadError: ''
};
$scope.letters = {
data: [],
loading: true,
laddaResendLetter: false,
laddaCancelLetter: false
};
$scope.dob = {
minDate: moment(new Date()).add(-90, 'y').format(),
maxDate: moment(new Date()).add(-10, 'y').format()
};
$scope.titles = ['Mr', 'Miss', 'Mrs', 'Ms', 'Dr', 'Rev'];
$scope.savePersonTab = function (validity) {
if (validity) {
$scope.ticket.dateOfBirth = $scope.dob.chosenDate;
Ticket.personTabSave({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
Ticket.getAdmin({ id: success.ticketGUID });
$scope.person.form.$setPristine();
$scope.getHistory();
}, function (error) {
});
} else {
console.log('skip Save');
}
};
//#region Tickets
$scope.reopenTicket = function () {
$scope.reOpenTicket.isOpen = true;
$scope.reOpenTicket.previousChanges = Ticket.getCriticalDateChanges({ id: $scope.ticket.ticketGUID });
// Reset
$scope.reOpenTicket.saveError = '';
$scope.reOpenTicket.reason = '';
};
$scope.saveReopen = function (validity) {
if (validity) {
$scope.reOpenTicket.saving = true;
var data = {
ChangeTo: $scope.reOpenTicket.newCriticalDate,
ChangeReason: $scope.reOpenTicket.reason
};
Ticket.reOpenTicket({ id: $scope.ticket.ticketGUID }, data, function (response) {
$scope.reOpenTicket.saving = false;
if (response.isSuccessful) {
$scope.getNotes();
$scope.getHistory();
$scope.ticket = Ticket.getAdmin({ id: $scope.ticket.ticketGUID });
$scope.reOpenTicket.isOpen = false;
} else {
$scope.reOpenTicket.saveError = response.errorMessage;
}
});
}
};
$scope.closeNewCriticalDate = function () {
$scope.reOpenTicket.isOpen = false;
};
$scope.confirmTCs = function () {
var opts = {
'keyboard': true,
'size': 'lg' //small or large modal size
};
// Checks
if ($scope.person.form.$dirty) {
dialogs.notify('Cannot Confirm!', 'Unsaved changes to personal details detected.', opts);
return;
}
// email address is complete
if (!$scope.ticket.eMailAddress) {
dialogs.notify('Cannot confirm!', 'An Email address must be entered and saved before confirming Terms and Conditions.', opts);
return;
} else {
if ($scope.ticket.status != 'AwaitingPayment' && $scope.ticket.status != 'Referred') {
dialogs.notify('Cannot confirm!', 'Ticket status must be Awaiting Payment or Referred before confirming Terms and Conditions. The current ticket status is ' + $scope.ticket.status, opts);
return;
}
}
var dlg = dialogs.confirm('Confirm terms and conditions', 'Please confirm that this delegate has read and agreed to the Terms and Conditions and also the details and offence relate to them.', opts);
dlg.result.then(function (btn) {
Ticket.confirmation({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
$scope.ticket = success;
$scope.getHistory();
}, function (error) {
});
});
};
$scope.lockTicket = function () {
Ticket.lock({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been locked');
}, function (error) {
console.log(error);
});
};
$scope.unlockTicket = function () {
Ticket.unlock({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been unlocked');
}, function (error) {
console.log(error);
});
};
$scope.cancelTicket = function () {
Ticket.cancelTicket({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been cancelled');
}, function (error) {
console.log(error);
});
};
$scope.restoreTicket = function () {
Ticket.restoreTicket({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been restored');
}, function (error) {
console.log(error);
});
};
//#endregion
//#region Payments
$scope.markAsPaid = function () {
var opts = {
'keyboard': true,
'size': 'lg' //small or large modal size
};
var dlg = dialogs.confirm('Mark as paid', 'Please confirm that you would like to manually mark this delegate as having paid.', opts);
dlg.result.then(function (btn) {
Payment.markAsPaid({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
$scope.ticket = Ticket.getAdmin({ id: success.ticketGUID });
}, function (error) {
console.info(error);
});
});
};
$scope.payments.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withDOM('tr');
$scope.payments.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0),
DTColumnDefBuilder.newColumnDef(1).withOption('width', '180'),
DTColumnDefBuilder.newColumnDef(2),
DTColumnDefBuilder.newColumnDef(3),
DTColumnDefBuilder.newColumnDef(4)
];
$scope.getPaymentData = function () {
$scope.payments.loading = true;
Payment.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.payments.loading = false;
$scope.payments.data = result;
});
};
//#endregion
//#region Notes
$scope.addNote = function () {
Note.add({ id: $scope.ticket.ticketGUID }, '"' + $scope.notes.newNote + '"', function (successResponse) {
$scope.notes.data.push(successResponse);
$scope.notes.newNote = '';
Notification.success('Note added');
}, function (err) {
console.log(err);
});
};
$scope.getNotes = function () {
$scope.notes.loading = true;
$scope.notes.data = Note.query({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.notes.loading = false;
$scope.notes.loadError = '';
}, function (error) {
$scope.notes.loading = false;
$scope.notes.loadError = error.data;
});
};
//#endregion
//#region ELearning
$scope.learning.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withDOM('tr');
$scope.learning.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0),
DTColumnDefBuilder.newColumnDef(1),
DTColumnDefBuilder.newColumnDef(2),
DTColumnDefBuilder.newColumnDef(3).notSortable()
];
$scope.getLearningData = function () {
$scope.learning.loading = true;
ELearning.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.learning.loading = false;
$scope.learning.data = result;
});
};
$scope.markAsCompleted = function () {
ELearning.MarkAsCompleted({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.ticket = successResponse;
$scope.getHistory();
$scope.getLearningData();
Notification.success('Ticket has been marked as completed');
});
};
$scope.getLearningHistory = function (learningData) {
var dlg = dialogs.create('app/elearning/ResultDialog.html', 'learningDialogController', { data: learningData.onlineLearningResultId }, 'lg');
};
//#endregion
//#region History
$scope.getHistory = function () {
$scope.history.loading = true;
History.query({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.history.data = successResponse;
$scope.history.loading = false;
$scope.history.loadError = '';
}, function (error) {
$scope.history.loading = false;
$scope.history.loadError = error.data;
});
};
$scope.animateElementIn = function ($el) {
$el.removeClass('hidden');
$el.addClass('bounce-in');
};
// optional: not mandatory (uses angular-scroll-animate)
$scope.animateElementOut = function ($el) {
$el.addClass('hidden');
$el.removeClass('bounce-in');
};
//#endregion
//#region Correspondance
$scope.getCorrespondanceData = function () {
$scope.letters.loading = true;
Correspondance.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.letters.loading = false;
$scope.letters.data = result;
});
};
$scope.resendLetter = function (letter) {
$scope.letters.laddaResendLetter = true;
Correspondance.resend({ id: letter.correspondanceID }, function (result) {
Notification.success('New ' + result.correspondanceType.toLowerCase() + ' ' + result.deliveryMethod.toLowerCase() + ' has been requested');
$scope.getCorrespondanceData();
$scope.letters.laddaResendLetter = false;
}, function (error) {
$scope.letters.laddaResendLetter = false;
});
};
$scope.cancelLetter = function (letter) {
$scope.letters.laddaCancelLetter = true;
Correspondance.cancelLetterRequest({ id: letter.correspondanceID }, function (result) {
Notification.success(result.correspondanceType.toLowerCase() + ' ' + result.deliveryMethod.toLowerCase() + ' has been cancelled');
$scope.getCorrespondanceData();
$scope.letters.laddaCancelLetter = false;
}, function (error) {
$scope.letters.laddaCancelLetter = false;
});
};
//#endregion
$scope.getPaymentData();
$scope.getNotes();
$scope.getLearningData();
$scope.getHistory();
$scope.getCorrespondanceData();}]);

Found the problem to be a third party module (angular-timeline) for animating the history

Related

WebRTC remote video stream not working despite having src

I'm trying to make a simple AngularJS WebRTC video chat app based on this tutorial.
I'm able to connect to connect clients, add streams and play my own stream, but somehow I can't play the remote stream.
When I check the elements I can see that the videoplayer does in fact have a blob source, but it won't play it.
Can anyone tell me why it won't show?
HTML element:
Room controller:
angular.module('publicApp').controller('RoomCtrl', function ($sce, VideoStream, $location, $routeParams, $scope, Room)
{
if (!window.RTCPeerConnection || !navigator.getUserMedia) {
$scope.error = 'WebRTC is not supported by your browser. You can try the app with Chrome and Firefox.';
return;
}
var stream;
VideoStream.get()
.then(function (s) {
stream = s;
Room.init(stream);
stream = URL.createObjectURL(stream);
if (!$routeParams.roomId) {
Room.createRoom()
.then(function (roomId) {
$location.path('/room/' + roomId);
});
} else {
Room.joinRoom($routeParams.roomId);
}
}, function () {
$scope.error = 'No audio/video permissions. Please refresh your browser and allow the audio/video capturing.';
});
$scope.peers = [];
Room.on('peer.stream', function (peer) {
console.log('Client connected, adding new stream');
$scope.peers.push({
id: peer.id,
stream: URL.createObjectURL(peer.stream)
});
console.log($scope.peers);
});
Room.on('peer.disconnected', function (peer) {
console.log('Client disconnected, removing stream');
$scope.peers = $scope.peers.filter(function (p) {
return p.id !== peer.id;
});
});
$scope.getLocalVideo = function () {
return $sce.trustAsResourceUrl(stream);
};
});
Room factory:
angular.module('publicApp').factory('Room', function ($rootScope, $q, Io, config)
{
var iceConfig = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]},
peerConnections = {},
currentId, roomId,
stream;
function getPeerConnection(id)
{
if (peerConnections[id]) {
return peerConnections[id];
}
var pc = new RTCPeerConnection(iceConfig);
peerConnections[id] = pc;
pc.addStream(stream);
pc.onicecandidate = function (evnt) {
socket.emit('msg', { by: currentId, to: id, ice: evnt.candidate, type: 'ice' });
};
pc.onaddstream = function (evnt) {
console.log('Received new stream');
api.trigger('peer.stream', [{
id: id,
stream: evnt.stream
}]);
if (!$rootScope.$$digest) {
$rootScope.$apply();
}
};
return pc;
}
function makeOffer(id)
{
var pc = getPeerConnection(id);
pc.createOffer(function (sdp) {
pc.setLocalDescription(sdp);
console.log('Creating an offer for', id);
socket.emit('msg', { by: currentId, to: id, sdp: sdp, type: 'sdp-offer' });
}, function (e) {
console.log(e);
},
{ mandatory: { OfferToReceiveVideo: true, OfferToReceiveAudio: true }});
}
function handleMessage(data)
{
var pc = getPeerConnection(data.by);
switch (data.type) {
case 'sdp-offer':
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
console.log('Setting remote description by offer');
pc.createAnswer(function (sdp) {
pc.setLocalDescription(sdp);
socket.emit('msg', { by: currentId, to: data.by, sdp: sdp, type: 'sdp-answer' });
});
});
break;
case 'sdp-answer':
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
console.log('Setting remote description by answer');
}, function (e) {
console.error(e);
});
break;
case 'ice':
if (data.ice) {
console.log('Adding ice candidates');
pc.addIceCandidate(new RTCIceCandidate(data.ice));
}
break;
}
}
var socket = Io.connect(config.SIGNALIG_SERVER_URL),
connected = false;
function addHandlers(socket)
{
socket.on('peer.connected', function (params) {
makeOffer(params.id);
});
socket.on('peer.disconnected', function (data) {
api.trigger('peer.disconnected', [data]);
if (!$rootScope.$$digest) {
$rootScope.$apply();
}
});
socket.on('msg', function (data) {
handleMessage(data);
});
}
var api = {
joinRoom: function (r) {
if (!connected) {
socket.emit('init', { room: r }, function (roomid, id) {
currentId = id;
roomId = roomid;
});
connected = true;
}
},
createRoom: function () {
var d = $q.defer();
socket.emit('init', null, function (roomid, id) {
d.resolve(roomid);
roomId = roomid;
currentId = id;
connected = true;
});
return d.promise;
},
init: function (s) {
stream = s;
}
};
EventEmitter.call(api);
Object.setPrototypeOf(api, EventEmitter.prototype);
addHandlers(socket);
return api;
});
Directive:
angular.module('publicApp').directive('videoPlayer', function ($sce) {
return {
template: '<div><video ng-src="{{trustSrc()}}" autoplay></video></div>',
restrict: 'E',
replace: true,
scope: {
vidSrc: '#'
},
link: function (scope) {
console.log('Initializing video-player');
scope.trustSrc = function () {
if (!scope.vidSrc) {
console.log('No vidSrc found');
return undefined;
}
return $sce.trustAsResourceUrl(scope.vidSrc);
};
}
};
});
Room.html:
<div class="video-wrapper">
<video-player class="col-md-4" ng-repeat="peer in peers" vid-src="{{peer.stream}}"></video-player>
</div>
<div class="video-wrapper">
<div class="col-md-2">
<video ng-src="{{getLocalVideo()}}" autoplay muted></video>
</div>
</div>

Error: $controller:ctrlreg A controller with this name is not registered

I see this error when I press F12 in chrome, but strangely things still work. There seems to be no problems.
The controller with the name 'AccountUpdateViewModel' is not registered.
Here is my js file. Any idea why? The version of angualr I am using is 1.6.0 from nuget(I develop on Visual Studio)
var accountUpdateModule = angular.module('accountUpdate', ['common', 'ngRoute'])
.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
$routeProvider.when(Avts.rootPath + 'account/update/step1', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step1.html', controller: 'AccountUpdateStep1ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/step2', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step2.html', controller: 'AccountUpdateStep2ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/step3', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step3.html', controller: 'AccountUpdateStep3ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/confirm', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Confirm.html', controller: 'AccountUpdateConfirmViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/successfullyupdated', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Successfull.html', controller: 'AccountUpdateSuccessfullyUpdatedViewModel' });
// 5:40 Sec. If it does not find any of these steps in this little spa silo, then redirect to step1
$routeProvider.otherwise({ redirectTo: Avts.rootPath + 'account/update/step1' });
});
accountUpdateModule.controller("AccountUpdateViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
// Nested ViewModels or sub viewmodels. See Video 138 3:40 Sec
// Things that are set up here in this view model(AccountRegisterViewModel) is bound to the priticular view Register.cshtml.
// see 4:50 Sec
$scope.viewModelHelper = viewModelHelper;
$scope.accountModelStep1 = new Avts.AccountUpdateModelStep1();
$scope.accountModelStep2 = new Avts.AccountUpdateModelStep2();
$scope.accountModelStep3 = new Avts.AccountUpdateModelStep3();
$scope.fetchDataToUpdate = function () {
viewModelHelper.apiGet('api/account/update', null,
function (result) {
$scope.accountModelStep1.Email = result.data.Email;
$scope.accountModelStep1.UserName = result.data.UserName;
$scope.accountModelStep2.FirstName = result.data.FirstName;
$scope.accountModelStep2.LastName = result.data.LastName;
$scope.accountModelStep3.Address = result.data.Address;
$scope.accountModelStep3.City = result.data.City;
$scope.accountModelStep3.State = result.data.State;
$scope.accountModelStep3.PostalCode = result.data.PostalCode;
});
}
$scope.fetchDataToUpdate();
//$scope.previous = function () {
// // 6:00 Sec
// $window.history.back();
//}
});
accountUpdateModule.controller("AccountUpdateStep1ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
viewModelHelper.modelIsValid = true;
viewModelHelper.modelErrors = [];
// No setpup rules for step 1 edit. Just showing Email and userName
//var accountModelStep1Rules = [];
//var setupRules = function () {
// accountModelStep1Rules.push(new validator.PropertyRule("Email", {
// required: {
// message: "Email is required",
// email: { message: "Email is not an email." }
// }
// }));
// accountModelStep1Rules.push(new validator.PropertyRule("Password", {
// required: { message: "Password is required" },
// minLength: { message: "Password must be at least 6 characters", params: 6 }
// }));
//accountModelStep1Rules.push(new validator.PropertyRule("PasswordConfirm", {
// required: { message: "Password confirmation is required" },
// custom: {
// validator: Avts.mustEqual, // See 143, 2:30
// message: "Password do not match",
// params: function () { return $scope.accountModelStep1.Password; } // must be function so it can be obtained on-demand
// }
//}));
//}
//$scope.fetchData = function () {
// viewModelHelper.apiGet('api/account/edit', null,
// function (result) {
// $scope.accountModelStep1.Email = result.data.Email;
// $scope.accountModelStep1.UserName = result.data.UserName;
// });
//}
$scope.step2 = function () {
$location.path(Avts.rootPath + 'account/update/step2');
// Video 144
// Pressing should just do validation and proceed to step 2.
//validator.ValidateModel($scope.accountModelStep1, accountModelStep1Rules);
//viewModelHelper.modelIsValid = $scope.accountModelStep1.isValid;
//viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
//if (viewModelHelper.modelIsValid) {
// viewModelHelper.apiPost('api/account/register/validate1', $scope.accountModelStep1,
// function (result) {
// // See the video 144 3:35 Sec
// $scope.accountModelStep1.Initialized = true;
// $location.path(Avts.rootPath + 'account/edit/step2');
// },
// function (failureResult) {
// $scope.accountModelStep1.errors = failureResult.data;
// viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
// viewModelHelper.modelIsValid = false;
// }
// );
//}
//else
// viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
}
//setupRules();
//$scope.fetchData();
});
accountUpdateModule.controller("AccountUpdateStep2ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
viewModelHelper.modelIsValid = true;
viewModelHelper.modelErrors = [];
var accountModelStep2Rules = [];
var setupRules = function () {
accountModelStep2Rules.push(new validator.PropertyRule("FirstName", {
required: { message: "First name is required" }
}));
accountModelStep2Rules.push(new validator.PropertyRule("LastName", {
required: { message: "Last name is required" }
}));
}
// Video 146
$scope.step3 = function () {
validator.ValidateModel($scope.accountModelStep2, accountModelStep2Rules);
viewModelHelper.modelIsValid = $scope.accountModelStep2.isValid;
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
if (viewModelHelper.modelIsValid) {
viewModelHelper.apiPost('api/account/update/validate2', $scope.accountModelStep2,
function (result) {
$scope.accountModelStep2.Initialized = true;
$location.path(Avts.rootPath + 'account/update/step3');
},
function (failureResult) {
$scope.accountModelStep2.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
viewModelHelper.modelIsValid = false;
}
);
}
else
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
}
$scope.backToStep1 = function () {
$location.path(Avts.rootPath + 'account/update/step1');
}
setupRules();
});
accountUpdateModule.controller("AccountUpdateStep3ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
if (!$scope.accountModelStep2.Initialized) {
// got to this controller before going through step 2
$location.path(Avts.rootPath + 'account/update/step2');
}
var accountModelStep3Rules = [];
var setupRules = function () {
accountModelStep3Rules.push(new validator.PropertyRule("Address", {
required: { message: "Address is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("City", {
required: { message: "City is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("State", {
required: { message: "State is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("PostalCode", {
required: { message: "Postal code is required" },
pattern: { message: "Postal code is in invalid format", params: /^[1-9][0-9]{5}$/ }
}));
//accountModelStep3Rules.push(new validator.PropertyRule("CreditCard", {
// required: { message: "Credit Card # is required" },
// pattern: { message: "Credit card is in invalid format (16 digits)", params: /^\d{16}$/ }
//}));
//accountModelStep3Rules.push(new validator.PropertyRule("ExpiryDate", {
// required: { message: "Expiration Date is required" },
// pattern: { message: "Expiration Date is in invalid format (MM/YY)", params: /^(0[1-9]|1[0-2])\/[0-9]{2}$/ }
//}));
}
$scope.confirm = function () {
validator.ValidateModel($scope.accountModelStep3, accountModelStep3Rules);
viewModelHelper.modelIsValid = $scope.accountModelStep3.isValid;
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
if (viewModelHelper.modelIsValid) {
viewModelHelper.apiPost('api/account/update/validate3', $scope.accountModelStep3,
function (result) {
$scope.accountModelStep3.Initialized = true;
$location.path(Avts.rootPath + 'account/update/confirm');
},
function (failureResult) {
$scope.accountModelStep3.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
viewModelHelper.modelIsValid = false;
}
);
}
else
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
}
$scope.backToStep2 = function () {
$location.path(Avts.rootPath + 'account/update/step2');
}
setupRules();
});
accountUpdateModule.controller("AccountUpdateConfirmViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
if (!$scope.accountModelStep3.Initialized) {
// got to this controller before going through step 3
$location.path(Avts.rootPath + 'account/update/step3');
}
$scope.updateAccount = function () {
// Video 147 2:00 Sec
var accountModel;
accountModel = $.extend(accountModel, $scope.accountModelStep1);
accountModel = $.extend(accountModel, $scope.accountModelStep2);
accountModel = $.extend(accountModel, $scope.accountModelStep3);
viewModelHelper.apiPost('api/account/update', accountModel,
function (result) {
//$location.path(Avts.rootPath);
$location.path(Avts.rootPath + 'account/update/successfullyupdated');
//account/update/successfullyupdateded
//account/update/successfullyupdated
//$window.location.href = Avts.rootPath;
},
function (failureResult) {
$scope.accountModelStep1.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
viewModelHelper.modelIsValid = false;
}
);
}
$scope.backToStep3 = function () {
$location.path(Avts.rootPath + 'account/update/step3');
}
});
//AccountUpdateSuccessfullyUpdatedViewModel
accountUpdateModule.controller("AccountUpdateSuccessfullyUpdatedViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
});
You had 'AccountUpdateViewModel controller. but you didn't registered it in your route.config. if you didn't config means you never use it anywhere.
please remove unwanted model. or register it in the rout config section.
i import the file, in index.html and my problem solved,
<script src=".../AccountUpdateStep1ViewModel.js"></script>

How do I unit test further .then and .catch branching in an Angular controller?

(function() {
'use strict';
angular
.module('walletInformation')
.controller('WalletInformationController', WalletInformationController);
/* #ngInject */
function WalletInformationController(
$q,
$scope,
config,
logger,
session,
$interpolate,
flowstack,
profileService,
walletInformationFormlyService,
postMsg
) {
// jshint validthis: true
var vm = this;
var app = $scope.app;
var user = session.get('profile').profile || {};
vm.saveWalletInformation = saveWalletInformation;
vm.onClickCancel = onClickCancel;
vm.deleteWallet = deleteWallet;
vm.model = {
walletInformation : {
firstName: user.name.first,
lastName: user.name.last,
emailAddress: user.emailAddress,
phoneNumber: user.mobilePhone.phoneNumber,
keepMeUpdated: user.preferences.receiveEmailNotification
}
};
activate();
////////////////
/**
* #function activate
* #description init function for the controller
* Calls paymentCard to get the
* */
function activate() {
createFormFields();
logger.info('Activated the Wallet Information View.');
}
function createFormFields() {
vm.fields = walletInformationFormlyService.getFormlyFields($scope.app.text);
}
function saveWalletInformation() {
var updatedProfile = createLegacyProfileInformation();
profileService.updateProfileInformation(updatedProfile).then(onSuccess).catch(onError);
function onSuccess(response) {
session.set('profile', {
profile: response.profile
});
if (vm.model.walletInformation.currentPassword && vm.model.walletInformation.newPassword) {
changePasswordRequest();
} else {
flowstack.add('accountManagement');
flowstack.next();
}
}
function onError(error) {
logger.error('Verify save Wallet Information Error: ', error);
}
//changePassword
function changePasswordRequest() {
var updatedPassword = {
data: {
currentPassword: vm.model.walletInformation.currentPassword,
newPassword: vm.model.walletInformation.newPassword
}
};
profileService
.changePassword(updatedPassword)
.then(onChangePasswordSuccess, onChangePasswordError);
function onChangePasswordSuccess(response) {
flowstack.add('accountManagement');
flowstack.next();
logger.info('Change password request: ', response);
}
function onChangePasswordError(response) {
logger.error('Change Password Error: ', response);
}
}
}
function deleteWallet() {
var appText = app.text.walletInformation;
console.log(appText);
flowstack.add('confirmation');
flowstack.next({
data: {
title: appText.deleteWalletConfirmationTitle,
body: appText.deleteWalletConfirmationBody,
cancelButton: appText.deleteWalletConfirmationCancelButton,
confirmButton: appText.deleteWalletConfirmationConfirmButton,
onConfirm: function() {
profileService.deleteWallet().then(deleteProfileSuccess, deleteProfileFailure);
function deleteProfileSuccess(response) {
if (response.profile) {
logger.info('profile is successfully deleted.', response);
postMsg.send('closeSwitch');
}
}
function deleteProfileFailure(error) {
logger.error('something went wrong while deleting profile.', error);
}
},
onCancel: function() {
flowstack.add('walletInformation');
flowstack.next();
}
}
});
}
function createLegacyProfileInformation() {
var updatedProfile = user;
var formlyField = vm.model.walletInformation;
//Update the profile
updatedProfile.name = {
first: formlyField.firstName,
last: formlyField.lastName
};
updatedProfile.mobilePhone.phoneNumber = formlyField.phoneNumber;
updatedProfile.preferences.receiveEmailNotification = formlyField.keepMeUpdated;
return {
data: updatedProfile
};
}
function onClickCancel() {
flowstack.back();
}
}
})();
Coverage is saying that onSuccess of saveWalletInformation and changePasswordRequest isnt't covered but I'm not sure exactly how to test it, the only thing I've tested now is that the saveWalletInformation function is calling the profile service:
describe.only('WalletInformationController ---', function() {
var controller, scope;
var userProfile = {
profile: {
name: {
first: 'someonefirstname',
last: 'someoneslastname'
},
emailAddress: 'someone#something.com',
mobilePhone: {
countryCode: 'US+1',
phoneNumber: '123 212 2342'
},
preferences: {
receiveEmailNotification: true
}
}
};
beforeEach(function() {
bard.appModule('walletInformation');
bard.inject(
'$q',
'$controller',
'$rootScope',
'api',
'flowstack',
'logger',
'session',
'profileService'
);
session.set('profile', userProfile);
});
beforeEach(function() {
sandbox = sinon.sandbox.create();
scope = $rootScope.$new();
scope.app = {
text: {
global: {},
walletInformation: {
deleteWalletConfirmationTitle: 'Confirm Wallet Deletion?'
},
userInformation: {
emailValidation: 'Please enter valid email'
},
signin: {
rememberMeLabel: 'remember me'
}
}
};
controller = $controller('WalletInformationController', {
$scope: scope
});
loggerErrorStub = sandbox.stub(logger, 'error');
sandbox.stub(logger, 'info');
controller = $controller('WalletInformationController', {
$scope: scope
});
});
describe('saveWalletInformation method', function() {
beforeEach(function() {
// apiStub.restore();
sandbox.stub(profileService, 'updateProfileInformation', function() {
return $q.when(userProfile);
});
});
it('should saveWalletInformation successfully', function() {
controller.saveWalletInformation();
expect(profileService.updateProfileInformation).to.have.been.called;
});
it('should log error msg when saveWalletInformation call fails', function() {
profileService.updateProfileInformation.restore();
sandbox.stub(profileService, 'updateProfileInformation', function() {
return $q.reject();
});
controller.saveWalletInformation();
scope.$apply();
expect(loggerErrorStub).to.have.been.calledOnce;
});
it('should call changePasswordRequest when currentPassword and newPassword are set', function() {
sandbox.stub(profileService, 'changePassword', function() {
return $q.when({
success: true,
extensionPoint: null
});
});
sandbox.stub(flowstack, 'add');
sandbox.stub(flowstack, 'next');
controller.saveWalletInformation();
// scope.$apply();
// expect(flowstack.next).to.have.been.calledOnce;
// expect(flowstack.add).to.have.been.calledOnce;
expect(profileService.updateProfileInformation).to.have.been.called;
// expect(profileService.changePassword).to.have.been.called;
});
});
You can let the service call go a step further and instead of verifying that service function was called, as you do here:
expect(profileService.updateProfileInformation).to.have.been.called;
You can instead mock the response from the api call with $httpBackend:
httpBackend.expectGET('your/url/here').respond(200, {response object...});
or
httpBackend.expectGET('your/url/here').respond(500, {error object...});
Then you'll be hitting both the error and success functions when your tests run

Meanstack /Angular.js how to update a seperate model

I'm writing a management system for an ecommerce system. The app needs to create pricing rules on a per product/ per category basis.
SO ..... For a new price rule that has a CategoryID, I want to update all products with that CategoryId. How do I call all the Products from the pricing controller and then update them?
I want this function in the Pricing's controller to update Products with a CategoryId that was set in the form.
$scope.saveRule = function saveRule(row){
var CategoryId = row.CategoryId;
if(row.id =="newRecord"){
var roundup = $('#roundupnewRecord').val();
var percentage = $('#percentagenewRecord').val();
var pricing = new Pricing({
CategoryId: CategoryId,
roundup: roundup,
percentage: percentage
});
pricing.$save(function(response) {
$route.reload();
});
} else {
Pricing.get({
pricingId: row.id
}, function(pricing) {
pricing.roundup = $('#roundup'+row.id).val();
pricing.percentage = $('#percentage'+row.id).val();
pricing.$update(function() {
$route.reload();
});
});
}
}
Thanks in advance for any help.
Pricing Controller. - angular
'use strict';
angular.module('mean.pricing').controller('PricingController', [ '$route', '$http', '$scope', '$routeParams', '$location', 'Global', 'Pricing', function ($route, $http, $scope, $routeParams, $location, Global, Pricing) {
$scope.global = Global;
$scope.create = function() {
var pricing = new Pricing({
CategoryId: this.title,
content: this.content
});
pricing.$save(function(response) {
console.log(response);
$location.path('pricing/' + response.id);
});
this.title = '';
this.content = '';
};
function generateDefaultRule() {
return {
CategoryId: 0,
ProductId: '',
roundup: 2,
percentage: 1,
newRecord: 1,
id: 'newRecord'
}
}
$scope.addRule = function addRule(id) {
$scope.rowCollection.push(generateDefaultRule());
console.log();
};
$scope.saveRule = function saveRule(row){
var CategoryId = row.CategoryId;
if(row.id =="newRecord"){
var roundup = $('#roundupnewRecord').val();
var percentage = $('#percentagenewRecord').val();
var pricing = new Pricing({
CategoryId: CategoryId,
roundup: roundup,
percentage: percentage
});
pricing.$save(function(response) {
$route.reload();
});
} else {
Pricing.get({
pricingId: row.id
}, function(pricing) {
pricing.roundup = $('#roundup'+row.id).val();
pricing.percentage = $('#percentage'+row.id).val();
pricing.$update(function() {
$route.reload();
});
});
}
//Get Products with Relative CategoryId
}
$scope.update = function() {
var pricing = $scope.pricing;
if (!pricing.updated) {
pricing.updated = [];
}
pricing.updated.push(new Date().getTime());
pricing.$update(function() {
$location.path('pricing/' + pricing.id);
});
};
$scope.find = function() {
Pricing.query(function(pricing) {
$scope.pricing = pricing;
});
};
$scope.findOverall = function() {
$http.get('/Pricing/overall').then(function(pricing) {
$scope.overall = pricing;
});
};
$scope.findCategories = function() {
$http.get('/Pricing/categories').then(function(pricing) {
console.log(pricing);
$scope.categories = pricing.data;
});
};
$scope.findProducts = function() {
$http.get('/Pricing/products').then(function(pricing) {
$scope.products = pricing.data;
});
};
$scope.findOne = function() {
Pricing.get({
pricingId: $routeParams.pricingId
}, function(pricing) {
$scope.pricing = pricing;
});
};
$scope.remove = function(pricing) {
if (pricing) {
pricing.$remove();
for (var i in $scope.pricing) {
if ($scope.pricing[i] === pricing) {
$scope.pricing.splice(i, 1);
}
}
}
else {
$scope.pricing.$remove();
$location.path('pricing');
}
};
$scope.removeItem = function removeItem(row) {
Pricing.get({
pricingId: row.id
}, function(pricing) {
pricing.$remove(function() {
var index = $scope.rowCollection.indexOf(row);
if (index !== -1) {
$scope.rowCollection.splice(index, 1);
}
});
});
}
$scope.list = function(){
$('table').on('click', 'a' , function (event) {
var id = $(this).attr('id');
if($(this).hasClass('editButton')){
$('#percentage'+id).css('display','inline-block');
$('#roundup'+id).css('display','inline-block');
$('#percentageSpan'+id).css('display','none');
$('#roundupSpan'+id).css('display','none');
$('.actionButtonsDiv'+id).css('display','none');
$('#saveButtonDiv'+id).css('display','inline');
}
});
$http.get('/pricing').then(function(pricing) {
$scope.rowCollection = pricing.data;
});
$http.get('/category').then(function(categories) {
$scope.categories = categories.data;
});
}
}]);
Products Controller - angular
'use strict';
angular.module('mean.products').controller('ProductsController', ['$http', '$scope', '$routeParams', '$location', 'Global', 'Products', function ($http, $scope, $routeParams, $location, Global, Products) {
$scope.global = Global;
$scope.create = function() {
var product = new Products({
title: this.title,
content: this.content
});
product.$save(function(response) {
$location.path("products/" + response.id);
});
this.title = "";
this.content = "";
};
$scope.remove = function(product) {
if (product) {
product.$remove();
for (var i in $scope.products) {
if ($scope.products[i] === product) {
$scope.products.splice(i, 1);
}
}
}
else {
$scope.product.$remove();
$location.path('products');
}
};
$scope.update = function() {
var product = $scope.product;
if (!product.updated) {
product.updated = [];
}
product.updated.push(new Date().getTime());
product.$update(function() {
$location.path('products/' + product.id);
});
};
$scope.find = function() {
Products.query(function(products) {
// console.log(products);
$scope.products = products;
});
};
$scope.categories = function() {
var selected = {};
$('#multiple').on('click', function(){
$('.product-checkbox').each(function() {
if ($(this).is(":checked")) {
$(this).prop('checked', false);
}else{
$(this).prop('checked', true);
}
});
});
$.each( ['approveButton', 'rejectButton', 'multiButton'], function( index, value ){
$('.'+value).on('click', function(){
$('.product-checkbox').each(function() {
var productId = $(this).attr('id');
if ($(this).is(":checked")) {
if (value === 'rejectButton') {
var categoryId = 199;
}else{
var categoryId = $('#selectProduct'+$(this).attr('id')).val().replace('number:','');
}
Products.get({
productId: productId
}, function(product){
product.CategoryId = categoryId;
product.$update(function(result) {
});
});
}
//Approves checked and rejcts unchecked products
if (value == 'multiButton') {
if (!$(this).is(":checked")) {
Products.get({
productId: productId
}, function(product){
product.CategoryId = 199;
product.$update(function() {
});
});
}
}
});
$location.path('products/categories');
});
});
$http.get('/products/categories').then(function(products) {
$scope.products = products.data;
});
$http.get('/category').then(function(categories) {
$scope.categories = categories.data;
});
$http.get('/productCategoryMatchs').then(function(productCategoryMatchs) {
var pCMResponse = productCategoryMatchs.data;
var pcmArray = {};
for(var index in pCMResponse){
pcmArray[pCMResponse[index].ProductId] = pCMResponse[index].CategoryId;
}
$scope.pCMs = pcmArray;
});
};
$scope.findOne = function() {
Products.get({
productId: $routeParams.productId
}, function(product) {
$scope.product = product;
});
};
}]);
Products Controller -node
'use strict';
/**
* Module dependencies.
*/
var StandardError = require('standard-error');
var db = require('../../config/sequelize');
/**
* Find product by id
* Note: This is called every time that the parameter :productId is used in a URL.
* Its purpose is to preload the product on the req object then call the next function.
*/
exports.product = function(req, res, next, id) {
console.log('id => ' + id);
db.Product.find({ where: {id: id}}).then(function(product){
if(!product) {
return next(new Error('Failed to load product ' + id));
} else {
req.product = product;
return next();
}
}).catch(function(err){
return next(err);
});
};
/**
* Create a product
*/
exports.create = function(req, res) {
// augment the product by adding the UserId
req.body.UserId = req.user.id;
// save and return and instance of product on the res object.
db.Product.create(req.body).then(function(product){
if(!product){
return res.send('users/signup', {errors: new StandardError('Product could not be created')});
} else {
return res.jsonp(product);
}
}).catch(function(err){
return res.send('users/signup', {
errors: err,
status: 500
});
});
};
/**
* Update a product
*/
exports.update = function(req, res) {
// create a new variable to hold the product that was placed on the req object.
var product = req.product;
product.updateAttributes({
price: req.body.price,
CategoryId: req.body.CategoryId
}).then(function(a){
return res.jsonp(a);
}).catch(function(err){
return res.render('error', {
error: err,
status: 500
});
});
};
/**
* Delete an product
*/
exports.destroy = function(req, res) {
// create a new variable to hold the product that was placed on the req object.
var product = req.product;
product.destroy().then(function(){
return res.jsonp(product);
}).catch(function(err){
return res.render('error', {
error: err,
status: 500
});
});
};
/**
* Show an product
*/
exports.show = function(req, res) {
// Sending down the product that was just preloaded by the products.product function
// and saves product on the req object.
return res.jsonp(req.product);
};
/**
* List of Products
*/
exports.all = function(req, res) {
db.Product.findAll({}).then(function(products){
return res.jsonp(products);
}).catch(function(err){
return res.render('error', {
error: err,
status: 500
});
});
};
/**
* List of Products
*/
exports.list = function(req, res) {
db.Product.findAll({
limit : 20
}).then(function(products){
return res.jsonp(products);
}).catch(function(err){
return res.render('500', {
error: err,
status: 500
});
});
};
/**
* List of Products and there categories
*/
exports.categories = function(req, res) {
db.Product.findAll({
attributes : [
'name',
'id',
// 'ProductCategoryMatch.count'
],
where: {
CategoryId : null
},
// include : [
// { model: db.ProductCategoryMatch }
// ],
// order : [
// ]
limit: 20
}).then(function(products){
return res.jsonp(products);
}).catch(function(err){
return res.render(500, {
error: err,
status: 500
});
});
};
/**
* Article authorizations routing middleware
*/
exports.hasAuthorization = function(req, res, next) {
// if (req.product.User.id !== req.user.id) {
// return res.send(401, 'User is not authorized');
// }
next();
};

How to inject a controller in angularjs test?

I am new to angular and TDD in general and I have been trying to debug some one else's code; the code beolow is trying to inject inject a controller but it is failing, and I am confused on how to debug this, any tips on how to solve this would be appreciated
describe('WebApp: AccountController', function() {
beforeEach(function() {
module('webApp');
module('tpls');
return inject(function($injector) {
this.q = $injector.get('$q');
this.userMock = {
id: 1,
email: 'pitogsdfs#gmail.com',
name: 'Adones Pitogo'
};
this.modalMock = {
open: (function(_this) {
return function(tpl, ctrl) {
_this.def = _this.q.defer();
_this.def.resolve(true);
return {
result: _this.def.promise
};
};
})(this)
};
this.modalSpy = spyOn(this.modalMock, 'open').andCallThrough();
this.teamMock = {
id: 1
};
this.repoMock = {
id: 1
};
this.httpBackend = $injector.get('$httpBackend');
this.httpBackend.whenGET('/api/v1/session').respond({
status: 'ok',
user: this.userMock
});
this.httpBackend.whenGET("/api/v1/users/" + this.userMock.id + "/teams").respond({
status: 'ok',
teams: [this.teamMock]
});
this.httpBackend.whenGET("/api/v1/users/" + this.userMock.id + "/repositories/remote").respond({
status: 'ok',
repositories: []
});
this.httpBackend.whenGET("/api/v1/users/" + this.userMock.id + "/repositories/followed").respond({
status: 'ok',
repositories: []
});
this.InvoiceService = $injector.get('Common.InvoiceService');
this.TeamService = $injector.get('Common.TeamService');
this.RepositoryService = $injector.get('Common.RepositoryService');
this.UserService = $injector.get('Common.UserService');
this.rootScope = $injector.get('$rootScope');
this.scope = this.rootScope.$new();
this.$controller = $injector.get('$controller');
this.timeout = $injector.get('$timeout');
this.compile = $injector.get('$compile');
this.rootElement = $injector.get('$rootElement');
this.toasterMock = {
pop: function(type, title, message) {
return true;
}
};
this.modalMock = {
open: (function(_this) {
return function(opts) {
var def;
def = _this.q.defer();
def.resolve(true);
return {
result: def.promise
};
};
})(this)
};
this.modalSpy = spyOn(this.modalMock, 'open').andCallThrough();
this.toasterSpy = spyOn(this.toasterMock, 'pop').andCallThrough();
this.TeamServiceSpy = spyOn(this.TeamService, 'getUserTeams').andCallThrough();
$('body').append('<div id="profile-pic-input"></div>');
this.httpBackend.flush();
this.controller = this.$controller('AccountController', {
'$scope': this.scope,
'$rootScope': this.rootScope,
'Common.RepositoryService': this.RepositoryService,
'Common.UserService': this.UserService,
'Common.InvoiceService': this.InvoiceService,
'toaster': this.toasterMock,
'Common.TeamService': this.TeamService,
'$modal': this.modalMock
});
return this.scope.$digest();
});
});
return it('should have an Account Controller', function() {
expect(this.controller).not.toBe(null);
return expect(this.controller).not.toBe(void 0);
});
});

Resources