Angular calling a parent method from an extended controller - javascript

I been spinning on this for days with no luck. I have BaseController with an init method among other things. I then wish to extend this controller and call the parents 'init' method from within the childs 'init' method.
The general answer for this is to call $scope.$parent.init($settings_object). However this is returning Error: $scope.$parent.init is not a function.
Generally the extended controller works fine being able to access the perents function and settings without issues. Just this example calling the same parent method from the child fails.
BaseController
(function( ){
var mainApp = angular.module("MyAppModule");
mainApp.controller('BaseController',function($scope, $rootScope,GLOBAL_CONFIG, ajaxRESTful,sharedValues, messageDisplay) { //base contorller
var bvm = this; //base vm
this.init = function($settings_object){
var keys = Object.keys($settings_object);
for(i=0;i<keys.length;i++){
bvm[keys[i]] = $settings_object[keys[i]];
}
}
//code remved to keep simple
});
})();
Extended controller
(function( ){
var app = angular.module('MyAppModule');
app.controller('RoleEdit', function($scope, $rootScope,$controller) {
angular.merge(this, $controller('BaseController', {$scope: $scope}));
var vm = this;
vm.newRoleFormData = [];
vm.role_id = null;
vm.mode = 'create';
vm.role = null;
vm.init = function ($object) {
console.log(vm);
console.log($scope.$parent);
$scope.$parent.init($object);
if(vm.role_id != null){
vm.loadInRole( );
}
};
}) //end contoller
})();//end of app
Why doesn't this work?
Is there a better way to do this?

The controller object (this) and the $scope object aren't directly related. There is no automatic wiring between them. $scope.$parent doesn't return a controller, it returns the parent scope. And since you registered your parent method in this.init instead of the usual $scope.init, you can't expect to find it using scopes.
You may circumvent this in a great number of ways, but as others have suggested, if you have functionality that is shared by many controllers, try to put it in a service instead. Maybe your BaseContoller itself should be a service.

You should do:
var parentInit = this.init.bind(this);
this.init = function ($object) {
parentInit($object);
if(vm.role_id != null){
vm.loadInRole( );
}
}

Related

Access controller property from another Class in AngularJS

today I discovered the following problem while using AngularJS.
My Code is as follows:
app.controller("SomeController", function(){
this.foo = true
this.changeFoo = function(bool){
this.foo = bool
}
api.check(function(response){
this.changeFoo(response.bar)
})
})
(by the way: response & response.bar are not undefined)
api is an instance of my class API, defined outside of my Angular-code.
The problem is, that I can not change this.foo inside my callback-function.
What can I do to access this.foo?
Edit:
I tried to pass this as an argument, the result is the same
api.check(this, function(scope, response){
scope.foo = response.bar
})
scope seems to be defined inside this function, but the changes doesn't effect anything
You need to assign this to a different variable in your angular code before your Api call. Then use that variable to access your this variable. Like this
app.controller("SomeController", ['$scope',function($scope){
this.foo = true;
this.changeFoo = function(bool){
this.foo = bool;
$scope.$apply();
};
Var controller=this;
api.check(function(response){
controller.changeFoo(response.bar);
});
}]);

Simple factory setter/getter in Angular?

I have this factory...
spa.factory("currentPageFactory", function() {
var pageDefinition = {};
pageDefinition.save = function(newPageDefinition) {
pageDefinition.value = newPageDefinition;
}
pageDefinition.read = function() {
return pageDefinition.value;
}
return pageDefinition;
});
...and this controller...
var pageDefinitionController = spa.controller("pageDefinitionController", ["currentPageFactory", function(currentPageFactory) {
currentPageFactory.save("foobar");
}]);
I have tested using the .read() function I created in this factory by including a definition in the factory of pageDefinition.value. I could read the variable using the getter just fine. The problem seems to lie in the setter.
I'm calling these functions like this...
/*Setter Call Example*/
currentPageFactory.save("blah");
/*Getter Call Example*/
this.foobar = currentPageFactory.read();
What am I doing wrong? Why is the setter not working?

How can i pass scope in template in angular js

I have one BaseController with common functions which my all other controllers inherit .
The controller is like this
function BaseController () {
this.defaultFilters = {};
this.doStuff = function ($scope) {
$scope.myobj.value = 1;
this.otherfunction();
};
I inherit that in my controller like this
BaseController.call($scope);
Now in my do stuff function i need to pass $scope because myobj is only visible there.
Now i want to know that how can i pass that in my template because i want to call that function when some click on some button
ng-click="doStuff(scope)"
Everything that you associate with your controller's scope, so you just associate your scope with some variable and i guess that will do the job.
Something like this :
app.controller(function($scope) {
$scope.scope = $scope;
});
But if you go by some standard approach, i suggest moving these common functions inside some service, injecting this service into each controller and using it in the views.
Something like this :
app.service("constantService", function() {
this.data = {}; // This will represent your common data.
this.commonFunction = function() {
};
});
app.controller(function() {
$scope.constantService = constantService;
// You can now use $scope.constantService.data as your reference for data, and then can copy it to some local $scope variable whenever required.
});
ng-click="constantService.commonFunction()"

knockout.js accessing container model property in a contained viewModel

I have nested view models like below. I am trying to access value in container view model from the contained view model (child). I got undefined error when the modelA.prop1 trying to get mainVM.prop1 value. Thanks for your help.
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA();
self.modelB = new modelB();
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(){
var self = this;
self.prop1 = ko.observable(mainVM.prop1); //I'd like to get value in containing view model above
}
function modelB(){....}
$(function () {
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
If you want to make sub-ViewModels dependent/aware of their parent you'll have to pass it to them. E.g.:
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA(self);
self.modelB = new modelB(self);
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(parent){
var self = this;
self.prop1 = ko.observable(parent.prop1); //I'd like to get value in containing view model above
}
function modelB(parent){....}
$(function () {
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
Think carefully though if this dependency is something you want in your design.
An alternative (though arguably worse from a design standpoint) solution would be to give them access through the scope, e.g.:
$(function () {
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA();
self.modelB = new modelB();
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(){
var self = this;
self.prop1 = ko.observable(viewModel.prop1); //I'd like to get value in containing view model above
}
function modelB(){....}
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
Some additional thoughts to #Jeroen answer
Having dependencies to parent from children is not only bad design it can create hard to find memory leaks
If you use the parent from a computed in the child KO will hook up a dependency, if you remove the child it's computed will still fire when the parent change state.
My general way of solving dependencies between models is to use a EventAggregator pattern, I have made one for this library
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy
Its a signalR library, if you do not need singalR you can extract the event aggregation part
Demo
http://jsfiddle.net/jh8JV/
ViewModel = function() {
this.events = ko.observableArray();
this.subModel = new SubViewModel();
signalR.eventAggregator.subscribe(Event, this.onEvent, this);
};
ViewModel.prototype = {
onEvent: function(e) {
this.events.push(e);
}
};
I think you've got an "XY problem" here: you want to accomplish task X (which you haven't named here) and you think that implementation Y (in this case, a child VM having a dependency on its parent) is the way to do it, even though Y might not be the best (or even a good) way to do it.
What's the actual problem you're trying to solve? If you need to access the parent property from within a child binding, Knockout's binding context ($root, $parent, $parents[], etc.) will let you do it, e.g.
<div data-bind="with:modelA">
<p>prop2 is <span data-bind="text:prop2"></span>
and prop1 from the main model is
<span data-bind="text:$root.prop1"></span>
</p>
</div>
In this case you could use $parent in place of $root since there's only one level of nesting.

"this" in JavaScript. reference to an object inside a factory

I wrote some classes in javascript and i wrote a few FunctionFactories for them. But I think that i have done some things wrong.
I renamed some things of my code, that you can understand it better.
So the first class is the "root"-class. this class has children, which i add later.
function templateRoot(){
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
The attribute "addBase" gets a function which is delivered by addBaseFactory...
function addBaseFactory(that){
return function(){
var newBase = new base(that.children.length, that.id);
that.children.push(newBase);
};
}
...and the base class which is used to generate a object in "addBase" looks like this:
function base(count, parent){
this.id = parent+"_base"+count;
this.parent = parent;
this.children = [];
this.remove = function (){
$('#'+this.id).remove();
};
this.render = baseRenderFactory(this);
this.addChild = addChildFactory(this);
this.getBaseButtons = function(){
var addAttributeButton = new $("<button>+ Attribute</button>").button();
var addTextButton = new $("<button>+ Text</button>").button();
return [addAttributeButton, addTextButton];
};
}
The problem now is. When i debug the code and set a breakpoint within the "render" function of the root-object. Then i can see, that "this" is not the root but the "base" object. And i cannot figure out why it is like that because the "root" object is the owner of this function, and my base has an own render function which is not called directly there.
So even the "this" in the line
$.each(this.children,function(i,obj){
Refers to the "base" object. But the "this" is inside the "root" object...
Hope you can help me :-)
EDIT:
The code to let it run:
var test = new templateRoot();
test.addBase();
test.render();
EDIT 2:
"that" in "addBaseFactory" refers to the correct "base" object.
I found your explanation pretty confusing, so I may have misinterpreted what you're trying to do, but I think you expect this within your nested functions to the same object as the this in the outer templateRoot() function. That's not how this works in JavaScript. Nested functions don't inherit the same this as the containing function - each function has its own this object that is set depending on how the function is called.
Here's one possible solution, which uses the fact that nested functions can see variables from their containing function(s):
function templateRoot(){
var self = this; // save a reference to this for use in nested functions
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(self.children,function(i,obj){
self.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+self.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
A detailed explanation about how this works in JS can be found at MDN.
Wouldn't this render its childerens children, since jquery would send each child as this?
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
Btw in what scope is addBaseFactory called? Because I think the "this" in the base, will refer to that scope.

Categories

Resources