AngularJS : undefined is not a function from factory - javascript

I have multiple functions inside of my factory and I cannot call my saveCharacter function from a button click using ng-click. The getCharacters function works just fine. Sorry in advance for the repost, I have gone over many different examples and cannot solve my particular issue with those. I can see my functions when I log the xmlService, but i'm not sure why it won't call to it. I was trying to return a post to a PHP file in saveCharacter, but changed to a string return to try to test to see what my issue was.
Thanks again for any help.
(function(){
var app = angular.module('arena', []);
app.factory('xmlService', function($http){
var factory = {};
factory.getCharacter = function getCharacter(){
return $http.get('xml/characterTemplate.xml');
};
factory.saveCharacter = function saveCharacter(){
return "hello";
//return $http.post('php/crud.php');
};
return factory;
});
app.controller('FighterController', ['$scope','xmlService', function($scope, xmlService){
this.fighterList = fighterList;
$scope.saveFighter = function saveFighter(){
console.log(xmlService);
xmlService.saveCharacter.success(function(data){
console.log(data);
});
}
function loadFighters(){
xmlService.getCharacter().success(function(data){
var x2js = new X2JS();
var charactersList = x2js.xml_str2json(data);
for(var i = 0; i < charactersList.characters.character.length; i++)
{
var currentFighter = charactersList.characters.character[i];
fighterList.push(currentFighter);
}
$scope.FighterController = charactersList;
});
}
loadFighters();
}]);
var fighterList = [
];
})();
Other questions I had while writing my first Angular app, what is the point of the code:
$scope.FighterController = charactersList;
does that allow me to access the returned data on the view side? do I have to reset the scope in my saveFighter function to cause my button to work?
Am I setting the dependencies correctly for my app.controller, and is that dependency injection?
Thank you all, and any comments on how my code is setup are greatly appreciated!

You haven't really explained what you did to fix this issue, so I'll explain it.
Here, you are trying to call xmlService.saveCharacter.success():
xmlService.saveCharacter.success(function(data){
console.log(data);
});
But xmlService.saveCharacter is a function. It has no success property; success is undefined. So this gives the error you were seeing.
You need to call xmlService.saveCharacter():
xmlService.saveCharacter().success(function(data){
console.log(data);
});
But this is still a problem because the saveCharacter() function returns the string "hello". This string doesn't have a success property. Yet again success is undefined, so now that causes the same error.
To fix that error, you just need to remove the return "hello"; and uncomment the code you had commented out:
factory.saveCharacter = function saveCharacter(){
return $http.post('php/crud.php');
};
Fixing those two problems should remedy your issue.

You are missing invoking a function with () change code to:
$scope.saveFighter = function saveFighter(){
console.log(xmlService);
xmlService.saveCharacter().success(function(data){
// ----------------------^
console.log(data);
});
}
$scope.FighterController = charactersList;is assigning data of characterList to scope variable and scope variable are accessible in html scope is like a bridge between controller and views.
I recommend you to start reading angularjs

I adjusted my factory to this structure and now I can call my functions.
app.factory('xmlService', function($http){
var factory = {
getCharacter: function(){
return $http.get('xml/characterTemplate.xml');
},
saveCharacter:function(){
console.log('hello?');
return $http.post('php/crud.php');
}
};
return factory;
});
in my controller
$scope.saveFighter = function(){
console.log(xmlService);
xmlService.saveCharacter().success(function(data){
console.log(data);
});
}
function loadFighters(){
xmlService.getCharacter().success(function(data){
var x2js = new X2JS();
var charactersList = x2js.xml_str2json(data);
for(var i = 0; i < charactersList.characters.character.length; i++)
{
var currentFighter = charactersList.characters.character[i];
fighterList.push(currentFighter);
}
$scope.FighterController = charactersList;
});
}
loadFighters();

Related

Failing to override JS functions

I'm trying to override some functions from a lib called log4javascript.
I tried the following:
var _logFatal = log.fatal;
var _logError = log.error;
var _logWarn = log.warn;
var _logDebug = log.debug;
log.fatal = function(message){
return _logFatal(stackTrace(message));
};
log.error = function(message){
return _logError(stackTrace(message));
};
log.warn = function(message){
return _logWarn(stackTrace(message));
};
log.debug = function(message){
return _logDebug(stackTrace(message));
};
But it doesn't work, when I call log.warn('test') for instance, it fails with Uncaught TypeError: object is not a function. But it works fine if I remove that part of the code.
What did I do wrong?
What you are trying to do is what I've seen called "monkey-patching".
I believe the problem you are having is that you are not invoking the functions you are trying to extend with the correct scope.
Try this pattern:
var fnPreviousFatal = log.fatal;
log.fatal = function(message) {
fnPreviousFatal.apply(this, [message]);
}

Updating angular.js service object without extend/copy possible?

I have 2 services and would like to update a variable in the 1st service from the 2nd service.
In a controller, I am setting a scope variable to the getter of the 1st service.
The problem is, the view attached to the controller doesn't update when the service variable changes UNLESS I use angular.extend/copy. It seems like I should just be able to set selectedBuilding below without having to use extend/copy. Am I doing something wrong, or is this how you have to do it?
controller
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
$scope.building = BuildingsService.getSelectedBuilding();
});
service 1
app.factory('BuildingsService', function() {
var buildingsList = [];
var selectedBuilding = {};
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
angular.extend(selectedBuilding, _.find(
buildingsList, {'building_id': buildingId})
);
};
var getSelectedBuilding = function() {
return selectedBuilding;
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
service 2
app.factory('AnotherService', function(BuildingsService) {
...
// something happens, gives me a building id
BuildingsService.setSelectedBuilding(building_id);
...
});
Thanks in advance!
When you execute this code:
$scope.building = BuildingsService.getSelectedBuilding();
$scope.building is copied a reference to the same object in memory as your service's selectedBuilding. When you assign another object to selectedBuilding, the $scope.building still references to the old object. That's why the view is not updated and you have to use angular.copy/extend.
You could try the following solution to avoid this problem if you need to assign new objects to your selectedBuilding:
app.factory('BuildingsService', function() {
var buildingsList = [];
var building = { //create another object to **hang** the reference
selectedBuilding : {}
}
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
//just assign a new object to building.selectedBuilding
};
var getSelectedBuilding = function() {
return building; //return the building instead of selectedBuilding
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
With this solution, you have to update your views to replace $scope.building bindings to $scope.building.selectedBuilding.
In my opinion, I will stick to angular.copy/extend to avoid this unnecessary complexity.
I dont believe you need an extend in your service. You should be able to watch the service directly and respond to the changes:
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
// first function is evaluated on every $digest cycle
$scope.$watch(function(scope){
return BuildingsService.getSelectedBuilding();
// second function is a callback that provides the changes
}, function(newVal, oldVal, scope) {
scope.building = newVal;
}
});
More on $watch: https://code.angularjs.org/1.2.16/docs/api/ng/type/$rootScope.Scope

AngularJS function and $scope

I have function defined in my controller like this:
var getTransactions = function(transactionType, partnerId) {
return loadTexts.getTransactionTexts(transactionType, partnerId).then(function (res) {
$scope.loadedTexts = res.data.Transactions;
});
};
Another $scope function is calling it. Now, when I put console.log for res.data.Transactions, everything is logged as it has to be, but when I want to assign that data to $scope variable, it simply doesn't work. (After load function, $scope.loadedTexts is empty however)
$scope.load = function() {
getTransactions($scope.TransactionTypeId, $scope.selectedPartner.Id);
};
Any help?
CODE:
app.controller("textsController", function ($scope, $http, autoComplete, loadTexts) {
$scope.selectedPartner = { "Name": "", "Id": null };
$scope.loadedTexts = [];
.. other functions are here ...
var getTransactions = function(transactionType, partnerId) {
return loadTexts.getTransactionTexts(transactionType, partnerId);
};
$scope.load = function () {
getTransactions($scope.transactionType, $scope.selectedPartner.Id).then(function(res) {
$scope.loadedTexts = res.data.Transactions;
});
};
});
In the code you are showing everything looks fine to me. Note however, that you are storing a reference to Transactions within loadedTexts. Maybe the "Transactions" object res.Data.Transactions is somehow cleared elsewhere.
You could try to copy the object instead of assigning a reference (use e.g. angular.copy) to test if this is the cause.

AngularJS: View is not updating after web worker returned

I'm using the library ParallelJS to do encryption/decryption in a web worker but when the promise is resolved it doesn't update the view accordly to the model's changes. Now, i know that i have to wrap the code that is called outside angularjs' scope in a $scope.$apply, but even doing this doesn't help.
I think that the reason is that I am resolving a deferred object inside a callback that is called outside angular's scope. It's a little hard to explain so let me show my code:
function _encrypt(options){
... //crypto-js code to do AES encryption
}
function _decrypt(options){
... //crypto-js code to do AES decryption
}
angular.module('CryptoService', []).factory('Cryptor', function($q, $rootScope){
function Cryptor(){};
Cryptor.prototype = {
encrypt: function(string, key) {
var deferred = $q.defer();
var ivS = generateIV();
var p = new Parallel({
iv: ivS,
text: string,
key: key
}, { evalPath: '/assets/js/eval.min.js' });
p.require('/assets/js/crypto.min.js');
p.spawn(_encrypt).then(function(result){
deferred.resolve(result);
});
return deferred.promise;
},
decrypt: function(string, key) {
var deferred = $q.defer();
var p = new Parallel({
text: string,
key: key
}, { evalPath: '/assets/js/eval.min.js' });
p.require('/assets/js/crypto.min.js');
p.spawn(_decrypt).then(function(result){
deferred.resolve(result);
});
return deferred.promise;
}
};
return new Cryptor();
});
angular.module('ContactService', ['CryptoService']).factory('Contact', function($q, $rootScope, Cryptor){
function Contact(){
//initialization
};
Contact.prototype = {
query: function(){
var deferred = $q.defer();
var options = {};
_oauth.secureGET(this._endpoint,options).done(function(result){
Cryptor.decrypt(result.cmc, key).then(function(string){
var data = JSON.parse(string);
var contacts = [];
for (var cidx in data){
var objContact = data[cidx];
var c = new Contact();
for(var pidx in this._properties){
var property = this._properties[pidx];
c[property] = objContact[property];
}
contacts.push(c);
}
//Since _oauth is using a jQuery method to execute the requests we are outside of angularjs' scope, so we need to wrap the promise resolution in
//the $apply method of the rootscope
$rootScope.$apply(function(){
deferred.resolve(contacts);
});
});
}.bind(this)).fail(function() {
$rootScope.$apply(function(){
deferred.resolve([]);
});
});
return deferred.promise;
},
};
return new Contact();
});
Now what's happen: If I leave the code the callback function of the query method is never called, since in the cryptor service the promise is called outside angular's scope. If I move the $rootScope.$apply wrapper to the Cryptor service then the callback inside the Contact service is called, the callback inside the controller is called BUT the view is not updated.
Any hint on how to resolve this?
Thanks you all
A.
Ok I feel so stupid... The problem wasn't the view not updating but the model empty. Since i was missing a bind(this) to the Cryptor promise's callback the models were empty and the view wasn't showing anything. Changing this
Cryptor.decrypt(result.cmc, key).then(function(string){
var data = JSON.parse(string);
var contacts = [];
for (var cidx in data){
var objContact = data[cidx];
var c = new Contact();
for(var pidx in this._properties){
var property = this._properties[pidx];
c[property] = objContact[property];
}
contacts.push(c);
}
//Since _oauth is using a jQuery method to execute the requests we are outside of angularjs' scope, so we need to wrap the promise resolution in
//the $apply method of the rootscope
$rootScope.$apply(function(){
deferred.resolve(contacts);
});
});
to this:
Cryptor.decrypt(result.cmc, key).then(function(string){
...
}.bind(this));
did the trick.
From your code:
p.spawn(_encrypt).then(function(result){
deferred.resolve(result);
});
Promise from Parallel.js is not same promise which in Angular.js. So you need to wrap
deferred.resolve(result);
into Angular.js $timeout:
$timeout(function(){
deferred.resolve(result);
}, 0)
in order to it notify Angular.js

Unset object property

I have a provider:
AdviceList.provider('$adviceList',function(){
this.$get = function ($rootScope,$document,$compile,$http,$purr){
function AdviceList(){
$http.post('../sys/core/fetchTreatments.php').success(function(data,status){
this.treatments = data;
console.log(this.treatments); // the correct object
});
this.adviceCategories = [
// available in the controller
];
}
return{
AdviceList: function(){
return new AdviceList();
}
}
}
});
Further, i have this controller:
AdviceList.controller('AdviceListCtrl',function($scope,$adviceList){
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; // undefined
});
Why is it, that the controller's $scope.treatments stays undefined, this.treatments inside the provider however, is filled correctly? Also, adviceCategories is available in my controller.
The call you get teatment is async in nature so the results may not have been populated when you try to assign them.
So here
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; //The treatments would only get filled after the server call is over.
You need to rewrite the code in a way that you assign it to your scope property on the success callback.
I will recommend you to simplify your code
1) Use simple factory method of angular instead of provider
2) return a promise to avoid using callbacks
AdviceList.service('adviceList', function ($http) {
return {
adviceList: function () {
return $http.post('../sys/core/fetchTreatments.php');
}
}
});
AdviceList.controller('AdviceListCtrl', function ($scope, $adviceList) {
adviceList.AdviceList().then(function (data) {
$scope.treatments = data //set value to data when data is recieved from server
});
});

Categories

Resources