Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Good practice for accessing parent controller properties #457

Closed
Bekt opened this issue Jul 28, 2015 · 13 comments
Closed

Good practice for accessing parent controller properties #457

Bekt opened this issue Jul 28, 2015 · 13 comments
Labels

Comments

@Bekt
Copy link

Bekt commented Jul 28, 2015

Using the controllerAs syntax, what is a good practice to access/modify simple properties of a parent controller? Please see the example below. I do not like accessing them via $scope.$parent.parent. How do you guys go about similar situations without creating a service for such a trivial logic.

<div ng-controller="Parent as parent">

  <div ng-if="parent.showMessage">
    Some simple message.
  </div>

  <div ng-controller="ChildOne as childOne"></div>

</div>
app.controller('Parent', function () {
    var self = this;
    self.showMessage = true;
});

app.controller('ChildOne', function ($scope) {
    var self = this;
    self.foo = 'bar';
    // Some simple logic.
    if (self.foo === 'bar') {
        $scope.$parent.parent.showMessage = false;
    }
});
@dietergeerts
Copy link

Can you give a real example? I can only think of situations where the parent already know the object the child controllers will use/modify, and that you give that object to the child controllers through data-ng-model. If you do this, the parent controller can access the object. This way, your controllers don't need to know each other, or each others' scope.

Or are you doing something different than what I think it is?

@Bekt
Copy link
Author

Bekt commented Jul 28, 2015

@dietergeerts thanks for your input. In your example, passing a primitive value down to a child using ng-model does not have any affect when the child modifies that value.

I basically need to show the first div if some simple logic is true in the child.

(ChildOne is attached to a route, so I can't just stick the first div in the child)

@dietergeerts
Copy link

@Bekt, it does can have that effect, as data-ng-if could be ="parent.object.property == 'some_value'", of course depending on what the logic is and how complicated that logic needs to be.

If they are set through routes with ui-router, and they are child routes, then they both can use the object.

Generally, it's a very bad idea to have to ask the parent for something, as that means that the controller isn't self-contained, which they need to be. There usually is a far better way to do such 'communication'. That's why I was asking for the actual situation you have, as there could be better ways to do what you want.

@johnpapa
Copy link
Owner

i like to keep things decoupled. BUT there are few absolutes ... in general my directives, my controllers, my services ... I like them to be self contained and if they need external aspects, I inject them or bind them. I don't like to rely on something to be used in a specific context. Make sense?

@dbabaioff
Copy link

I would solve this uisng a diretive to encapsulate the child controller, like this:

<div ng-controller="Parent as parent">

  <div ng-if="parent.showMessage">
    Some simple message.
  </div>

  <div child-one parent="parent"></div>

</div>
app.controller('Parent', function () {
    var self = this;
    self.showMessage = true;
});

app.directive('childOne', function() {
    return {
        controllerAs: 'childOne',
        controller: function($scope, $attrs) {
            var parent = $scope.$eval($attrs.parent);

            var self = this;
            self.foo = 'bar';
            // Some simple logic.
            if (self.foo === 'bar') {
                parent.showMessage = false;
            }
        }
    };
});

Or to use a ng-init on the second contoller.

<div ng-controller="ChildOne as childOne" ng-init="childOne.setParent(parent)"></div>

@leandrocosta
Copy link

Using a more component-oriented approach:

<html ng-app="myApp">
  <body>
    <div ng-controller="ParentCtrl as vm">
      <my-directive on-foo-eq-bar="vm.updateMessage(msg)"></my-directive>
    </div>

    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script>
      angular.module('myApp', [])
        .controller('ParentCtrl', function($window) {
          var vm = this;
          vm.updateMessage = function(msg) {
            $window.alert(msg);
          };
        })
        .directive('myDirective', function() {
          return {
            scope: {
              onFooEqBar:'&'
            },
            controllerAs:'vm',
            controller: function($scope) {
              var self = this;
              self.foo = 'bar';
              // Some simple logic.
              if (self.foo === 'bar') {
                $scope.onFooEqBar({msg: false});
              }
            }
          };
        });
    </script>
  </body>
</html>

@Bekt
Copy link
Author

Bekt commented Jul 30, 2015

@dbabaioff @leandrocosta: thanks, interesting suggestions.

I ended up going with $on on the parent and $broadcast on the children.

@johnpapa
Copy link
Owner

johnpapa commented Aug 6, 2015

good conversation, but nothing to add here to the guide, IMO

@johnpapa johnpapa closed this as completed Aug 6, 2015
@Bekt
Copy link
Author

Bekt commented Aug 6, 2015

👍

@danielkereama
Copy link

danielkereama commented Jan 24, 2018

in your parent controller subscribe to an event on $rootScope

$rootScope.$on('myEvent', function(data){/*do something*/})

then in the child, fire the event using $rootScope

$rootScope.$emit('myEvent', someData);

@dietergeerts
Copy link

Imho, it's far better to have 2 components instead of controllers, and inject properties into the child component if needed, and if you want to set them, output something from the child to the parent. Far more performant and easier to reason about + easier to unit test.

Using $rootScope and eventing is not done anymore nowadays.

@anurag1209
Copy link

You can check if the property exists and then assign the value as shown below..

if ($scope.$parent.showMessage){
var parent = $scope.$parent;
while(!parent.hasOwnProperty('showMessage ')){
parent = parent['$parent'];
}
parent.showMessage = false;
}

@texonidas
Copy link

Sorry to necro this, but just in case anyone else lands here, my solution would be to convert the child controller directly to a component, then bind an update function from the parent to the child, as follows:

<div ng-controller="Parent as parent">

  <div ng-if="parent.showMessage">
    Some simple message.
  </div>

  <child-one update-parent="parent.showMessage = value"></child-one>

</div>
app.controller('Parent', function () {
    var self = this;
    self.showMessage = true;
});

app.component('childOne', /* component def stuff including updateParent: '&' in bindings */);

function childOneController() {
    var self = this;
    self.foo = 'bar';
    // Some simple logic.
    if (self.foo === 'bar') {
        self.updateParent({value: false});
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants