AngularJS service TypeError - javascript

I want to capture the url of my query in an AngularJS service as this
var mortgageloanService = angular.module('loanstreetIpadAppApp', []);
mortgageloanService.factory('updateTable', function($http) {
return {
getParams: function() {
var url = 'https://api.mongolab.com/api/1/databases/angularjs-intro/collections/users?apiKey=terrPcifZzn01_ImGsFOIZ96SwvSXgN9';
console.log('in service mode');
console.log(url);
return $http.get(url);
}
};
});
This is my controller.js code
angular.module('loanstreetIpadAppApp')
.controller('Mortgage_LoanCtrl', function ($location, $scope) {
$scope.update_result = function(updateTable) {
updateTable.getParams().success(function(loan){$scope.loan = loan});
console.log($scope.resulttable);
};
});
On my view page, i have a button which onclick shud call the update_result function. But whenever i click on the button i get the following error
TypeError: Cannot read property 'getParams' of undefined
at Scope.$scope.update_result (http://localhost:9003/scripts/controllers/mortgage_loan.js:22:16)
at http://localhost:9003/bower_components/angular/angular.js:10567:21
at http://localhost:9003/bower_components/angular/angular.js:18627:17
at Scope.$eval (http://localhost:9003/bower_components/angular/angular.js:12412:28)
at Scope.$apply (http://localhost:9003/bower_components/angular/angular.js:12510:23)
at HTMLButtonElement.<anonymous> (http://localhost:9003/bower_components/angular/angular.js:18626:21)
at HTMLButtonElement.jQuery.event.dispatch (http://localhost:9003/bower_components/jquery/dist/jquery.js:4430:9)
at HTMLButtonElement.elemData.handle (http://localhost:9003/bower_components/jquery/dist/jquery.js:4116:28)
Anyone knows how to solve this issue?

In order to use your updateTable's factory inside your controller, you need to inject it. So, your controller should look like this.
angular.module('loanstreetIpadAppApp')
.controller('Mortgage_LoanCtrl', function ($location, $scope, updateTable) {
$scope.update_result = function() {
updateTable.getParams().success(function(loan){$scope.loan = loan});
console.log($scope.resulttable);
};
});
Notice that I've removed "updateTable" as "$scope.update_result"'s parameter since it would overwrite your updateTable object inside that closure.

Related

how do I fix exception on calling length on an empty array

I have this angular service which on startup contains an empty array. In the getter, I log what it is returning. I would like avoid having to write logic around a logging statement, and I would like the logging statement to stay, for now.
here's the provider:
appInstance.service('ActivityNavigationDataService',
['$log', '$rootScope', 'UIManager',
function($log, $rootScope, UIManager) {
$log.debug('ActivityNavigationDataService constructor');
$rootScope.activities = [];
UIManager.registerActivityListener($rootScope, activitiesUpdated);
function activitiesUpdated(event, activities) {
$rootScope.activities = activities;
}
this.getActivities = function() {
$log.debug('returning ' + $rootScope.activities.length + ' activities');
return $rootScope.activities;
};
}]);
here's the error:
TypeError: Cannot read property 'length' of undefined
at Object.getActivities (ActivityNavigationDataService.js:28)
at new ActivityNavigationServiceController (ActivityNavigationServiceController.js:31)
at Object.instantiate (angular.js:5055)
at $controller (angular.js:11015)
at UIManager.js:40
at processQueue (angular.js:17051)
at angular.js:17095
at Scope.$digest (angular.js:18233)
at angular.js:18462
at completeOutstandingRequest (angular.js:6362) "Possibly unhandled rejection: {}"
thoughts?
Thnx, Matt
edit:
I think the problem is the ActivityNavigationDataService is not finished loaded and constructed by the time activitiesUpdated is called.
I am loading the service via define[] like this:
define(['services/ActivityNavigationDataService',
// theres others here
'controllers/UIManager'],
function() {
'use strict';
var appInstance = angular.module('obdPortletApp');
appInstance.controller('obdMasterController', ['$rootScope','$scope', '$log', 'UIManager',
function($rootScope, $scope, $log, UIManager) {
UIManager.initialize();
UIManager.buildUI();
}]);
}
);
The UIManager.buildUI() method uses ocLazyLoad to load additional resources, which in turn push activities to the ActivityNavigationService through a broadcasted message, not direct reference to the service.
So I think something is off on timing.
Your activitiesUpdated() handler is getting undefined for activities. If you are the one returning the activities make sure you return an empty array. However, if you don't, you could do something like this:
function activitiesUpdated(event, activities) {
$rootScope.activities = activities || [];
}

JS $scope.something equals to a function

I can't understand what $scope.search is doing there, like, what it is doing when it set equals to the function? And, if i want to do in other way, how should i do?
(i am using angular js, 1.6 version)
$scope.search = function(){
query.get($scope.username , {
success: function(gameScore) {
console.log(gameScore);
return gameScore;
},
error: function(object, error) {
console.log("Sorry, this user does not exist yet");
}
});
};
You can call $scope.search() from anywhere in your controller. You can also do something like ng-click=search() on any element in a template that is using that controller.
Calling $scope.search() will either return gameScore or print an error message to the console.
Not sure if this function is written in a general controller or a component controller
Since you are using 1.6
Incase of it being a component controller you could attach this function to component controller and use it in your template or component
angular.module('app',[])
.component('testComponent', {
bindings: {}.
tempalate:'<div>$ctrl.getValue()</div>',
controller: function(){
var ctrl = this;
ctrl.getValue = function() {
console.log('log your value');
}
}
});

Update $scope inside .then?

I have a function inside a controller and I'm confused as to how to update a $scope variable from inside a .then function.
Heres my function:
searchSpotify(query, $scope) {
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
$scope.result = data;
});
}
In the console log I receive this error:
TypeError: Cannot set property 'result' of undefined
Do I use $scope.$apply somehow?
EDIT:
Here's the entire controller for context
(function() {
class SelectionController {
constructor(User, groupService, selectionService, songService, $scope, $routeParams, Spotify) {
this.groupId = $routeParams.groupId;
this.selectionId = $routeParams.selectionId;
this.groupService = groupService;
this.selectionService = selectionService
//this.selections = this.selectionService.getSelections();
this.songService = songService;
this.searchResults;
this.$scope = $scope;
this.Spotify = Spotify
}
searchSpotify(query) {
this.Spotify.search(query, 'track').then(function(data) {
console.log(data.tracks.items);
$scope.result = data;
});
}
addSong(name, artist, album) {
alert('called from selection controller');
this.songService.addSong(this.selectionId, name, artist, album);
}
createSelection(name, description) {
this.selectionService.createSelection(this.groupId, name, description);
}
deleteSelection(selection) {
this.selectionService.deleteSelection(selection);
}
}
angular.module('songSelectionApp')
.controller('SelectionController', SelectionController);
})();
Save reference to $scope:
searchSpotify(query) {
var $scope = this.$scope;
this.Spotify.search(query, 'track').then(function(data) {
console.log(data.tracks.items);
$scope.result = data;
});
}
Or use arrow functions:
searchSpotify(query) {
this.Spotify.search(query, 'track').then((data) => {
console.log(data.tracks.items);
this.$scope.result = data;
});
}
.bind(this) should also work, as another answers suggest.
Regarding $scope.$apply: you need it only when you want to change a $scope outside of a $digest, for example when you use external (non-angular) libraries/functions, like WebSocket, or jquery event listeners. Untill Spotify is an angular service/factory - you don't need $scope.$apply
From docs:
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
this.result = data;
}.bind($scope));
Should be all you need really. You are basically telling the internal scope what this should really be
You should use
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
this.$scope.result = data;
}.bind(this));

Unit Testing a Controller with specific syntax

Whenever, I am testing a controller and have something like this in it.
$scope.isSomething = function (Item) {
return ItemCollection.someItem(Item.attachedItem);
};
giving error on karma console:
TypeError: undefined is not an object (evaluating 'Item.attachedItem')
I am simply calling the function from the test file like this:
scope.isSomething();
I need to mock the Item.attachedItem or I am missing something here.. Please Explain in details as this is happening in multiple files.. thanks in advance
Also, for this type of code
.controller('itemCtrl', function (itemCollection) {
var vm = this;
this.itemCollection= itemCollection;
itemCollection.someItem().then(function (Item) {
vm.pageUrl = Item.pageUrl;
vm.Item= Item.someItems;
});
});
Also, this is also part of the code for more broad view here it gives Item.pageUrl is not a object error
Refer angular unit testing docs
The ItemCollection being a service, you could inject a mock while initialising a controller using
var ItemCollection, ItemCrtl;
beforeEach(inject(function($controller, $rootScope) {
$scope = $rootScope.$new();
ItemCollection = jasmine.createSpyObj('ItemCollection', ['someItem']);
ItemCrtl = $controller('ItemCtrl', {
$scope: scope,
ItemCollection: ItemCollection
});
});
For Item, the method isSomething should take care of checking if Item is undefined before doing Item.attachedItem
Testing an aync block is tricky. someItem returns a promise. $q an angular service to which can be used create async functions while testing.
We need to resolve the deferred object to test the async task.
var ItemCollection, ItemCrtl, deferedObj;
beforeEach(inject(function($controller, $rootScope, $q) {
$scope = $rootScope.$new();
deferedObj = $q.defer();
ItemCollection = jasmine.createSpyObj('ItemCollection', ['someItem']);
ItemCollection.someItem.andReturn(deferedObj.promise);
ItemCtrl = $controller('ItemCtrl', {
$scope: scope,
ItemCollection: ItemCollection
});
});
it('sets page url', function() {
deferedObj.resolve({ pageUrl: 'http://url', someItems: [1,2,3] });
scope.$apply();
expect(ItemCtrl.pageUrl).toEqual('http://url');
});
you have to use mock Item data in test like this (assuming attachedItem value is boolean)
var item={attachedItem:true}
scope.isSomething(item)
$scope.isSomething = function (Item) {
if(!Item.attachedItem){
Item.attachedItem=YOUR_MOCK_VALUE;
}
return ItemCollection.someItem(Item.attachedItem);
};

scope.modal.show() is not a function?

I try to open a modal view. For that i use the following:
crop: function(options) {
options = this.initOptions(options);
var scope = $rootScope.$new(true);
ionic.extend(scope, options);
scope.modal = $ionicModal.fromTemplate(template, {
scope: scope
});
// Show modal and initialize cropper.
return scope.modal.show().then(function() {
return (new jrCropController(scope)).promise.promise;
});
},
This is working fine. But now i dont want to define my template inside the javascript. I want to define my template in an extra html file and so i did.
So i had to replace this:
$ionicModal.fromTemplate(template,
with this:
$ionicModal.fromTemplateUrl(url,
But now i get the following error:
ionic.bundle.js:20306 TypeError: scope.modal.show is not a function
at Object.crop (jr-crop.js:287)
at Scope.$scope.crop (polaroids_edit.js:51)
at $parseFunctionCall (ionic.bundle.js:21044)
at ionic.bundle.js:53458
at Scope.$eval (ionic.bundle.js:23100)
at Scope.$apply (ionic.bundle.js:23199)
at HTMLButtonElement.<anonymous> (ionic.bundle.js:53457)
at HTMLButtonElement.eventHandler (ionic.bundle.js:11713)
at triggerMouseEvent (ionic.bundle.js:2863)
at tapClick (ionic.bundle.js:2852)
Why do i get this error when i use a template url instead of a template? For me that makes no sense since it is the same method in both cases.
I think that you're missing the fact that the fromTemplate() returns the modal object while fromTemplateUrl() returns a promise, since you're loading the template asynchronously.
Thus, this works fine
scope.modal = $ionicModal.fromTemplate(template, {
scope: scope
});
while this will not work
scope.modal = $ionicModal.fromTemplateUrl(url, {
scope: scope
});
since scope.modal will be a promise object.
You will have to wait for the promise to resolve and then assign the modal, like
$ionicModal.fromTemplateUrl(url, {
scope: $scope,
}).then(function(modal) {
$scope.modal = modal;
});
For reference check out $ionicModal: fromTemplate vs. fromTemplateUrl functions
crop: function(options) {
options = this.initOptions(options);
var scope = $rootScope.$new(true);
ionic.extend(scope, options);
$ionicModal.fromTemplateUrl(template, {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
scope.modal = modal;
return scope.modal.show().then(function() {
return (new jrCropController(scope)).promise.promise;
});
});
}
Iconic modal

Categories

Resources