23

Here is a real-life Angular problem I can't wrap my head around. I love Angular, but this issue is bugging me a lot right now.

What is the best practice to extend an existing controllers's functions, and use the extended controller on an other page of my application? In other words: How to do controller inheritance in Angular?

Edited out - 23/09/2014, dont think the description of my original usecase helps the visitors to understand it better what I'm after here. I think it disctracts people from the real issue.

Mihaly KR
  • 2,363
  • 2
  • 19
  • 20
  • See http://stackoverflow.com/a/21465181/149060 where I discuss exactly this issue and the use of extend – Pauli Price Jan 31 '14 at 15:31
  • Thanks, so you suggest angular extend, possibly with a service for serving the abstract baseclass. Sounds good. – Mihaly KR Jan 31 '14 at 16:09
  • With angular.extend, another problem arises: I can't redefine public functions of the base class. See here: http://plnkr.co/edit/Omi2iQATZOKcpixJ10rS?p=preview. – Mihaly KR Feb 08 '14 at 10:46
  • I'd probably say no, but is using prototypes in Angular 'allowed'? Cause I could do real inheritance, if class prototyping, and exessive use of .prototype would be a good thing in Angular. – Mihaly KR Feb 08 '14 at 10:50

2 Answers2

44

After half a year I think I understand completely what's going on. As it is pointed out in a comment to this post, the simplest answer is services.

In the most optimal case, all your scope variables are values gathered from a factory/service. Still, you might want to use the exact same controller with one extra function: $scope.someFunction(){}, and keep the rest. In this case, you do have a 'thin' controller logic, which is the desirable controller design - but may still end up a hundred or more lines of code. You don't want that being duplicated in an other controller, just because you need some extra controller logic (like $scope.someFunction() )

What do you do then?

The answer is this:

  1. Make sure that you did everything in order to solve the situation with factories
  2. if you are abolutely certain you did, go for the controller injection:

    .controller('childController', function ($scope, $controller) {
      'use strict';
      $controller('parentController', {$scope: $scope});
      $scope.someFunction=function(){}
    })
    

It's that simple. -- again, usually, things can be solved with factories..

Hope you find this useful ;)

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Mihaly KR
  • 2,363
  • 2
  • 19
  • 20
  • Yes, in Angular you inject a service to add functionality. It's like mixin in Ruby or other languages. – Bernard Sep 23 '14 at 10:05
  • Yes, the straight-forward answer is to inject a service. But what if you populate the scope with all the services return values, and you get a controller, which you want to extend later with a single $scope.someFunction() – Mihaly KR Sep 23 '14 at 10:18
  • We use the "thin controller" pattern. A controller for us is basically just a way to hook up the model (a service) to the view. All validation and business logic is done on the model, not the controller. – Bernard Sep 23 '14 at 10:59
  • 1
    Exactly. What if that thin controller is 100s of lines of code, because it is attached to a rich service. Would you want to have those lines duplicated, just because you'd need an other controller in which you want to use an other factory too. Practical example: You've got products in your webshop. when you search, it lists products (populates $scope.product). Now at an other place, you need the exact same list, with $scope.addToFavorites() function to add a product to your fav list. It means that favoritesFactory() must be injected in the second controller, the rest should remain the same. – Mihaly KR Sep 23 '14 at 12:50
  • You wouldn't want to have those 100s of lines duplicated. You need the same controller, and extend it with $scope.addToFavorites=function(id){favoritesService.add(id)}. – Mihaly KR Sep 23 '14 at 12:51
  • And what if you're using controllerAs notation instead of the $scope? I use ´var vm = this´ and only $scope for other purposes ($scope.on,...). Also, what about internal variables and functions in the controller? Do they get inherited as well? I'm refering to ´var myVar = false´, for example, in my controller, I presume this is also not inherited, right? – David Jan 27 '16 at 08:52
  • 1
    What is the 'use strict' for here? – JoannaFalkowska Apr 11 '16 at 08:46
  • Can I override an existing controller's function after extending it? – Pushpendra Dec 01 '16 at 12:10
  • Yes, you can @Pushpendra – Mihaly KR Dec 12 '16 at 15:28
-1

Abstract

The problem you are referring to, looks to me like generic code reuse problem for which there is standard solution in the field of computer studies (invented back in previous century), i.e. inheritance. Unfortunately due to numerous reasons OOP didn't obtain wide adoption in JavaScript community, one of them is the fact that it wasn't core feature of the language. Another one is the fact that developers are open to creating their own approaches at solving the same problem. And by creating these different approaches developers partition the community into parts that deal with inheritance in different ways.

It leads to blurring of whole idea of OOP and makes people scared of those three letters. In my practice I've met dozen's of people saying that JavaScript isn't made for OOP and raising points like "JavaScript it's a functional language", or "inheritance is an anti-pattern" or "Don't do things for which JavaScript wasn't intended for". But when you ask these people what OOP is?... Typically none of them can provide an answer. So having an opinion is great but it has to be attached to some investigation.

Answer

So answering your question, I would propose using John Resig's JavaScript inheritance approach, which described in detail within the following SO post: angularjs with oop inheritance in action

Community
  • 1
  • 1
Lu4
  • 14,873
  • 15
  • 79
  • 132
  • I'd strictly go against John Resig's solution proposition. Pls see the original question's edited-out part, which specifically deals with his solution. You cannot guarantee that you see the parent controller's scope - think about isolated directives. – Mihaly KR Nov 17 '15 at 09:17
  • I think there's no such problem, angular scopes has nothing to do with organizing / extending your code, which is OOP responsibility. The approach has nothing to do with going against anything, this approach opens possibilities rather than imposes restrictions, it's up to you how you will use it. From OOA point of view one can not simply substitute the concept of inheritance with an ordinary singleton (i.e. service). By doing so you will get bad architecture... – Lu4 Nov 17 '15 at 09:40
  • why do you think Angular is OOP? It is not, and should not be treated as such. This is exactly my point: If you go that way (namely trying to solve an object-oriented concept in a non object-oriented environment), you go against the concept of the framework. I do think that controller scope inheritance has it's place in a proper Angular codebase, but it never should be the answer to such a general question. – Mihaly KR Nov 17 '15 at 10:44
  • Mihalay, what is the basis for your opinion, for how long have you been working with OOP in general and OOP + JavaScript + Angular to make such judgements? Don't get me wrong, but reading your answer leaves nothing but a smile... You are mixing concepts. I was never saying that Angular is OOP because they are inherently different things like "sweet" and "soft". Both of them are solving different problems. You can't "... solve object oriented concept ...", and there's no such thing as "object-oriented environment”. And eventually what is the concept of framework? Not using OOP? It sounds funny – Lu4 Nov 17 '15 at 15:14
  • You might be surprised, but Angular 2.0 will not support scopes, and as such it is an obsolete concept already. You might get even more surprised if you will visit https://angular.io/ you will see an example in ES6 which uses ES6 “class” which comes together with all the OOP inheritance, encapsulation and polymorphism out of the box. Why do you think it happens? Because you can’t answer one of the most complex questions in programming by going absolute and just saying “don’t do that, it’s bad“ you have to provide explanation for your opinion… So don't perceive me as an enemy I'm trying to help – Lu4 Nov 17 '15 at 15:15
  • Long enough. If you're familiar with main js frameworks, and have been present in the progress javascript made in the past 10 years, then you should have understood what one means when mentioning OO concept in js and js frameworks. There was many Js framework trying to give JS code an OO structure, inherently failing of course bc of the nature of the language. Of course there no such thing as "OO environemnt" in the strict sense, but when I mention non-oo env, I mean that Angular is not one of those. Don't think I mixed concepts here. – Mihaly KR Nov 18 '15 at 10:11
  • A2.0 and ES6 - where did that come from? You might be suprised how many other languages don't support scopes :)) Mixing concepts - thy name is .... well, Lu4. – Mihaly KR Nov 18 '15 at 10:15
  • Joke aside, this SO ticket is a very well defined question with Angular1.x. I'm not answering a general question with an aboslute reply, and as I mentioned scope inheritance has it's place in Angular, but usually not in this case. Your help is appreciated, and thanks for contributing. I just think that more often than not, your answer is not the right one, and misguides Angular newbies and helpseekers. – Mihaly KR Nov 18 '15 at 10:23