AngularJS inheritance - overwrite $scope.method() - javascript

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();
}
}​

Related

How to pass data from directive to parent scope in my case?

I have a question regarding the directive and controller.
I want to pass the data from directive to controller in my case.
template html
<img ng-src=“{{url}}” image-detect />
<div>width: {{width of image}}</div> // how do I show the value here
<div>height: {{height of image}}</div>
directive
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
$scope.imageWidth = $(this).width();
$scope.imageHeight = $(this).height();
//not sure what to do to pass the width and height I calculate in directive to the parent
});
}
})();
How do I pass imageWidth and imageHeight to the parent scope and show it in the template? Thanks a lot!
Two methods come to my mind:
You can have an object something like imageDimention declared on base/parent/whatever scope and then have it shared with directive through scope isolation and then from directive's controller's scope you can access that imageDimention object and set its imageWidth and imageHeight properties. Setting these properties in directive will take effect in base/parent/whatever scope as well:
Example of scope isolation, copied from angular documentation
angular
.module('yourapp')
.directive('myImage', function() {
return {
restrict: 'E',
scope: {
imageDimention: '=imageDimention'
},
controller: 'ImageController'
};
});
Then in ImageController's scope you can access same imageDimention object
Create a ContextService this can be used for sharing data between different angular components not just these image properties. Its good to have a ContextService somewhat generic so that it can be reused/general purpose.
ContextService can be something like:
angular.module('yourapp')
.factory('ContextService', ContextService);
function ContextService() {
var service = {};
var data = {};
service.set = set;
service.get = get;
function set(key, value) {
if(key !== null && key !== undefined){
data[key] = value;
}
}
function get(key) {
return data[key];
}
return service;
}
And then you can inject this service into your angular components(controllers/directives/other services) and access it as some kind of global objects because services are singleton, this will serve you as data sharing module.
In your case, you probably have a controller that is attached to view, so assuming you have that controller, should declare an object say image on this controllers scope:
$scope.image = {
url: 'imageUrl'
width: '0px',
height: '0px',
}
Then your html template should probably be something like this:
<img ng-src="{{image.url}}" image-detect />
<div>width: {{image.width}}</div>
<div>height: {{image.height}}</div>
And your directive should look like this:
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'scope': {
'image': '=image'
},
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
//here you can access image object from scope which is same as controller that is attached to the view
$scope.image.width = $(this).width();
$scope.image.height = $(this).height();
});
}
})();
I hope this might help...

How to inject variable into controller

I am trying to get started with angular.js but I can't figure out how to inject a simple variable into my controller before testing.
This is my controller:
angular.module("app").controller("SimpleController", SimpleController);
function SimpleController() {
var vm = this;
vm.myVar = 1;
vm.getMyVar = function() {
return vm.myVar;
};
}
My test looks like the following:
describe("SimpleController", function() {
var vm;
beforeEach(module('app'));
beforeEach(inject(function($controller, $rootScope) {
vm = $controller('SimpleController', {
$scope: $rootScope.$new(),
myVar: 2
});
}));
it('myVar should be 2 not 1', function() {
expect(vm.getMyVar()).toEqual(2);
});
});
I did a lot of google searching but this is really confusing because many people do use $scope in controller and not thisas I do. But I guess the injection of variables should work with this too?
You may want to try writing your controller this way. This will make $scope accessible.
(function () {
angular.module("app").controller("SimpleController", SimpleController);
SimpleController.$inject = ['$scope'];
function SimpleController($scope) {
$scope.somevar = "something";
}
})();
I'm sure you've probably came across the documentation, but here is a link to the docs which contains the basics and should at least get you going in the right direction.
Another alternative would be something like this:
app.controller('SimpleController', ['$scope', function($scope) {
$scope.somevar = "something";
....
}]);
When you use $scope, you're making that property publicly available in the view.

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.

http.jsonp and callbacks in javascript

I am working on a calculator that will consider AWS instance costs. I am pulling the data from a .js file on amazon and I would like to read it into an object but i keep getting an error "Uncaught ReferenceError: callback is not defined" .. here is my .js file.
(function() {
var app = angular.module('formExample', []);
var ExampleController = function($scope, $http) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
$scope.GetAws();
};
$scope.reset = function() {
$scope.user = "";
};
function callback(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};
$scope.reset();
};
app.controller('ExampleController', ['$scope', '$http', ExampleController]);
}());
It is weird that the aws link you are using supports jsonp but it does not take custom callback function name. (Atleast you can look up to find out if the query string they are looking for is callback or not). angular handles it when we provide callback=JSON_CALLBACK it gets translated to angular.callbacks_x which are exposed globally temporarily by angular to handle the request and resolve the promise accordingly. But for this the endpoint must take the callback argument and wrap the response in the same string and function invocation. However this endpoint does not seem to consider it and even without any callback it automatically wraps into default callback function invocation. So you would need to inject $window (Correct DI way) object and set callback function to it and ?callback=callback is irrelevant.
var ExampleController = function($scope, $http, $window) {
$scope.master = {};
//....
$window.callback = function(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};
$scope.reset();
};
app.controller('ExampleController', ['$scope', '$http', '$window', ExampleController]);
Plnkr
It is because the AWS script is looking to call a function called "callback" on the global scope (outside of Angular). Since your function is within the scope of another (IIFE) function, it cannot be accessed.
What I've done in a case like this is simply put the function in the global scope.
In cases where an application requires some API to have loaded before Angular can do it's magic and has a callback similar to your situation, I have done the following, manually bootstrapping Angular:
index.html
<script src="http://www.example.com/api?callback=myCallbackFunction"></script>
app.js
// callback function, in global (window) scope
function myCallbackFunction() {
// manually bootstrap Angular
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
}
// your IIFE
(function() {
})();
Notice callback should be set in window scope.
So,one solution is like:
$scope.reset = function() {
$scope.user = "";
};
window.callback = function(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};

Javascript call function in Angular

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]);
});

Categories

Resources