Javascript call function in Angular - javascript

I had a strange problem in my Angular app so I asked some simple Javascript questions to make myself clear about the fundamentals (please see Javascript Callbacks). The solution worked fine for pure Javascript but when applied to my Angular app the problem still occurs.
I have defined a service which creates new instance of an object.
appModule.factory('myService', ["$rootScope", "$http", "DataCall", "$routeParams", "$log", function($rootScope, $http, DataCall, $routeParams, $log ) {
/////////////////////////// My- Object /////////////////////////////////////
MyObject= function(a, b, callback) {
var thisTemp = this;
this.accounts = [];
thisTemp.accounts[0] = 1;
thisTemp.accounts[1] = 2;
DataCall.get('xxx',function(data, status){
callback.call(this);
});
}
});
In my controller I create the new object and assign it to a scope variable
within controller....
$scope.pageInit = function () {
$scope.currentObject = {};
$scope.currentObject = new myService.MyObject('a', 'b', function(){
alert(this.account[0]);
});
This still doesn't work. Within the call back function 'this' refers to the 'window' rather than to currentObject.
Any help would be highly appreciated.

MyObject= function(a, b, callback) {
var thisTemp = this;
this.accounts = [];
tmpThis.accounts[0] = 1;
tmpThis.accounts[1] = 2;
DataCall.get('xxx',function(data, status){
// it is here 'this' become 'window'
callback.call(this);
});
}
});
since you willing to read how javascript callback, I leave you to figure it out.

There's no need to explicitly instantiate your service in the controller. Angular will already do this for you when it's needed.
So you could re-write your service to something like this.
appModule.factory('myService', ["$rootScope", "$http", "DataCall", "$routeParams", "$log", function($rootScope, $http, DataCall, $routeParams, $log ) {
/////////////////////////// My- Object /////////////////////////////////////
myService = {
accounts : [],
accounts[0] : 1,
accounts[1] : 2,
dataCall : function(a, b, callback) {
callback.call(this);
}
}
return myService;
});
within controller....
$scope.pageInit = function () {
$scope.myService = myService;
$scope.myService.currentObject = myService.dataCall('a', 'b', function(){
alert($scope.myService.accounts[0]);
});

In your MyObject you get hold of a reference to 'this' (which refers to the function MyObject) and you called it thisTemp. Since you got hold of that reference in a variable, you should use that variable instead of 'this' unless you want to reference another context.
appModule.factory('myService', ["$rootScope", "$http", "DataCall", "$routeParams", "$log", function($rootScope, $http, DataCall, $routeParams, $log ) {
/////////////////////////// My- Object /////////////////////////////////////
function MyObject(a, b, callback) {
var thisTemp = this;
this.accounts = [];
thisTemp.accounts[0] = 1;
thisTemp.accounts[1] = 2;
DataCall.get('xxx',function(data, status){
callback.call(thisTemp);
});
}
return {
MyObject: MyObject
}
}]);
within controller....
$scope.pageInit = function () {
$scope.currentObject = {};
$scope.currentObject = new myService.MyObject('a', 'b', function(data){
alert(data.account[0]);
});

Related

Binding with service variable using ControllerAs syntax without $scope?

I'm familiarizing myself with controllerAs syntax in AngularJS, and I've come to a problem when I need to do a simple binding to a service variable. Typically a $scope.$watch or $scope.$on would do, but that would involve injecting $scope, which seems to defeat the purpose of controllerAs.
Currently what I have is that after clicking one of the buttons and calling config.setAttribute(attr), the controller calls the service's setAttribute function, but not getAttribute, so config.attribute never changes.
Is there something I'm overlooking in how I'm approaching this? Would I need to inject $scope or change the controller syntax to use $scope instead?
View:
<div data-ng-controller="ConfigCtrl as config">
<h3>Customize</h3>
<pre>Current attribute: {{config.attribute}}</pre>
<label>Attributes</label>
<div data-ng-repeat="attr in config.attributes">
<button ng-click="config.setAttribute(attr)">{{attr.name}}</button>
</div>
</div>
Service:
(function() {
'use strict';
angular.module('app')
.factory('Customization', Customization);
function Customization() {
var service = {
attribute: null,
getAttributes: getAttributes,
setAttribute: setAttribute,
getAttribute: getAttribute
}
return service;
/////
function getAttributes() {
return [
{name: 'Attr1', value: '1'},
{name: 'Attr2', value: '2'} // etc.
];
}
function setAttribute(attr) {
service.attribute = attr;
}
function getAttribute() {
return service.attribute;
}
}})();
Controller:
(function(){
'use strict';
angular.module('app')
.controller('ConfigCtrl', ConfigCtrl);
function ConfigCtrl(Customization){
var vm = this;
vm.attribute = Customization.getAttribute(); // bind
vm.attributes = [];
// Functions
vm.setAttribute = Customization.setAttribute;
init();
/////
function init(){
// Get attributes array
vm.attributes = Customization.getAttributes();
}
}})();
Here is what my controller looks like after injecting $scope and adding the watch for attribute:
(function(){
'use strict';
angular.module('app')
.controller('ConfigCtrl', ConfigCtrl);
function ConfigCtrl($scope, Customization){
var vm = this;
vm.attribute;
vm.attributes = [];
// Functions
vm.setAttribute = Customization.setAttribute;
init();
/////
function init(){
// Get attributes array
vm.attributes = Customization.getAttributes();
}
$scope.$watch(function() {
return Customization.getAttribute()
}, function() {
vm.attribute = Customization.getAttribute();
});
}})();
I also have the Karma test in case anyone is interested:
(function() {
'use strict';
describe('ConfigCtrl', function () {
var ConfigCtrl, scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ConfigCtrl = $controller('ConfigCtrl',
{$scope: scope}
);
}));
describe('#setAttribute', function(){
it('sets the current attribute', function(){
var selected = {
name:'Attr1',
value:'1'
};
ConfigCtrl.setAttribute(selected);
scope.$apply();
expect(ConfigCtrl.attribute).to.eql(selected);
});
});
});
})();
Thanks for the help. I'm welcome to any better answers anyone else might have.

Angular 1.3 breaking changes - scope set but reset before $apply

AngularJs 1.3.x, simple controller works but as soon as I re-write it using Typescript and Injection, it fails. If I reference 1.2.x it starts working again.
//This works in 1.3.x
scopeApp.controller('MyController', ['$scope', function ($scope) {
$scope.username = 'World';
$scope.sayHello = function () {
$scope.greeting = 'Hello ' + $scope.username + '!';
};
}]);
http://plnkr.co/edit/ssSuuZuGlrypemx3BU5r?p=preview
//This DOES NOT works in 1.3.x but does in 1.2.x
//The following code is produced via Typescript:
var MainFeature;
(function (MainFeature) {
var MainCtrl = (function () {
function MainCtrl($scope) {
this.scope = $scope;
this.name = "Sirar";
this.message = '';
}
MainCtrl.prototype.SetMessage = function () {
this.message = 'Hello' + this.name;
};
return MainCtrl;
})();
MainFeature.MainCtrl = MainCtrl;
})(MainFeature || (MainFeature = {}));
scopeApp.controller("MainCtrl", ["$scope", function ($scope) {
return new MainFeature.MainCtrl($scope);
}]);
Breaking changes docs that have valuable information but didn't help:
https://docs.angularjs.org/guide/migration
http://ng-learn.org/2014/06/Migration_Guide_from_1-2_to1-3/
http://wildermuth.com/2014/11/11/Angular_1_3_and_Breaking_Change_for_Controllers
You need to pass the constructor function, not some other function like you did. As I explained in another answer the controller is not created by calling new. It's created as follows:
instance = Object.create(controllerPrototype);
...
return fn.apply(self, args);
The catch is that the return value is not used, but the instance. In your case this would mean:
instance = Object.create({}); // should be MainCtrl.prototype
...
return fn.apply(self, args);
So "MainCtrl" ends up as empty object. You have to do what you should have done in the first place, pass the constructor:
scopeApp.controller("MainCtrl", ["$scope", MainFeature.MainCtrl]);

AngularJS inheritance - overwrite $scope.method()

I have a ParentController and a ChildController, which look like this:
ParentController:
app.controller("ParentController", function($scope) {
// define preload method, expose to template
$scope.preload = function() {
console.log("parent");
};
// call preload function
$scope.preload();
});
ChildController:
app.controller("ChildController", function($scope) {
$controller("ParentController", {
$scope: $scope,
});
// override preload method here
$scope.preload = function() {
console.log("child")
};
});
As you can see, both Controllers define $scope.preload(), which is called upon instantiation in the ParentController. My goal is for ChildControllerto overwrite this method, so that when it is called, it logs a different string to the console.
Is this possible? If not, how else would I go about reusing all methods defined in ParentControllerin my ChildController?
To share the data among independent controllers, Services can be used. Create a service with the data model that needs to be shared. Inject the service in the respective controllers.
In the following example, Service is used to store the variable x. The independent controllers will check the value of X whenever needed.
angular.module('myApp', [])
.service('myService', function () {
var x=5 ;
return {
increase : function() {
x++;
},
getX : function() {
return x;
}
};
})
function ControllerA($scope, myService) {
$scope.x = 1;
$scope.incrementDataInService= function() {
myService.increase();
}
$scope.syncDataWithService= function() {
$scope.x = myService.getX();
}
}
function ControllerB($scope, myService) {
$scope.x = 1;
$scope.incrementDataInService= function() {
myService.increase();
}
$scope.syncDataWithService= function() {
$scope.x = myService.getX();
}
}​

AngularJS : How to create a two-way data binding between two isolated controllers and a shared service?

I am trying to create a two-way data binding between two isolated controllers and a shared service (which provides another isolated scope):
app.factory("sharedScope", function($rootScope) {
var scope = $rootScope.$new(true);
scope.data = "init text from factory";
return scope;
});
app.controller("first", function($scope, sharedScope) {
$scope.data1 = sharedScope.data;
});
app.controller("second", function($scope, sharedScope) {
$scope.data2 = sharedScope.data;
});
Fiddle: http://jsfiddle.net/akashivskyy/MLuJA/
When the application launches, data1 and data2 are correctly updated to the init text from factory, but later, if I change any of them, those changes are not reflected throughout those three scopes.
How can I bind them?
P.S. If there's a better way than returning a scope and still having access to event and observing functionalities (without basically re-writing them), let me know. :)
Fixed it. References will be lost if you are using primitives, as in your fiddle.
Check this:
Updated fiddle
app.factory("sharedScope", function($rootScope) {
var scope = $rootScope.$new(true);
scope.data = {text: "init text from factory"};
return scope;
});
app.controller("first", function($scope, sharedScope) {
$scope.data1 = sharedScope.data;
});
app.controller("second", function($scope, sharedScope) {
$scope.data2 = sharedScope.data;
});
Yet another fun bit: In this case, you don't need to inject $scope or $rootScope. The following code works if you utilize Controller As.
Check the Fiddle
var app = angular.module("app", []);
app.factory("sharedScope", function() {
var _this = this;
_this.data = {text: "init text from factory"};
return _this;
});
app.controller("first", function(sharedScope) {
var _this = this;
_this.data1 = sharedScope.data;
});
app.controller("second", function(sharedScope) {
var _this = this;
_this.data2 = sharedScope.data;
});
For even more fun, consider controllers, services, and factories as classes.
More Fiddles
var app = angular.module("app", []);
var SharedScope = function(){
var _this = this;
_this.data = {text: "init text from factory"};
return _this;
};
app.factory("sharedScope", SharedScope);
var First = function(sharedScope){
var _this = this;
_this.data1 = sharedScope.data;
};
var Second = function(sharedScope){
var _this = this;
_this.data2 = sharedScope.data;
};
First.$inject = ['sharedScope'];
Second.$inject = ['sharedScope'];
app.controller("first", First);
app.controller("second", Second);
I've been playing at implementing Josh Carroll's Guidelines to Avoid "Scope Soup"
JavaScript passes objects by reference, so all scopes will point to the same object. Why not just do this?
app.factory("sharedData", function() {
return {data: "init text from factory"};
});
app.controller("first", function($scope, sharedData) {
$scope.sharedData = sharedData;
});
app.controller("second", function($scope, sharedData) {
$scope.sharedData = sharedData;
});
and in your view:
<p>{{sharedData.data}}</p>

Without using the module revealing pattern is there a way I can access "this" inside an anonymous function?

If I have the following code. Is the only way that I can get access to the this inside of the function by using the self variable or is there another way that I can access the this inside the function ?
app.controller('ChildCtrl', function($scope, _o) {
var self=this;
this.option = {};
this.option.abc = 25;
$q.all([_o.getUserProfiles(),
_u.getConfigs()
])
.then(function (results) {
???.option.userProfiles = results[0].userProfiles;
});
You can use bind. May be this will be more comfortably for you.
app.controller('ChildCtrl', function($scope, _o) {
this.option = {};
this.option.abc = 25;
$q.all([_o.getUserProfiles(),
_u.getConfigs()
])
.then(function (results) {
this.option.userProfiles = results[0].userProfiles;
}.bind(this));
More about bind you can read here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Categories

Resources