Asked  7 Months ago    Answers:  5   Viewed   60 times

Without using a service or constructing watchers in the parent controller, how would one give children states access to the main controller's $scope.

  .state("main", {
      controller:'mainController',
      url:"/main",
      templateUrl: "main_init.html"
  })  
  .state("main.1", {
      controller:'mainController',
      parent: 'main',
      url:"/1",
      templateUrl: 'form_1.html'
  })  
  .state("main.2", {
      controller:'mainController',
      parent: 'main',
      url: "/2",
      templateUrl: 'form_2.html'
  })  

I'm not able to access the mainController scope in child state--or rather I'm getting another instance of that scope--not what I want. I feel I'm missing something simple. There is a shared data config option in the state object but I'm not sure if this should be used for something like this.

 Answers

59

I created working plunker, showing how to use $scope and UI-Router.

The state definition is unchanged:

$stateProvider
    // States
 .state("main", {
      controller:'mainController',
      url:"/main",
      templateUrl: "main_init.html"
  })  
  .state("main.1", {
      controller:'mainController',
      parent: 'main',
      url:"/1",
      templateUrl: 'form_1.html'
  })  
  .state("main.2", {
      controller:'mainController',
      parent: 'main',
      url: "/2",
      templateUrl: 'form_2.html'
  })  

But each state can have different controller. Why? because each view of each state gets new instance of defined controller. So while we have mainController like the one below, we can be sure, that if we navigate to state 'main.2' it will be instantiated twice.

controller('mainController', function ($scope) {
  $scope.Model = $scope.Model || {Name : "xxx"};
})

But what we can see here, is that we check if $scope.Model already exsits... and if not (Parent state) we instantiate it with new intance {Name : "xxx"}.

Well, what I am saying is: only parent state will init the $scope.Model. All others will get that already filled. How? Well here is the answer:

Scope Inheritance by View Hierarchy Only

Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).

It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.

So, as stated in the documentation. Because our child views are nested in the parent view, the scope is inherited.

Understanding Scopes

In AngularJS, a child scope normally prototypically inherits from its parent scope.
...

Having a '.' in your models will ensure that prototypal inheritance is in play.

// So, use
<input type="text" ng-model="someObj.prop1"> 
// rather than
<input type="text" ng-model="prop1">.

And that's it. We get inheritance from UI-Router views and angular scopes, and because we smartly used a reference type (Model), i.e. do have '.' dot in ng-model definition - we can share data now

NOTE: having dot '.' in the ng-model="Model.PropertyName simply means, that there is a reference object Model {} with some property: PropertyName

Check the working example here

Tuesday, June 1, 2021
 
radmen
answered 7 Months ago
29

You can do a $rootScope.$broadcast on items that you need to sync across directive.

Or you can pass a object to your directive1 isolated scope, which would act as a communication mechanism. On this object if you change sub property like tablename, that would affect in the parent scope.

Something like

One23SRCApp.directive('directive1',function() {
    return {
        restrict: "A",
        scope:{tableconfig:'='},
        link: function (scope, element, attrs) {
           scope.tableconfig.tablename= "table";
        }
    };
});


One23SRCApp.directive('directive2',function() {
    return {
        restrict: "A",
           link: function (scope, element, attrs) {
           var tablename = scope.tableconfig.tablename;
        }
    };
})

The HTML becomes

<table directive1 tableconfig='tableconfig'>
  <tr>
     <td>column1</td>
   <td>column1</td>
   </tr>
</table>

Your controller should have this object defined

$scope.tableconfig={};

Saturday, July 17, 2021
 
brombeer
answered 5 Months ago
75

We expect that the router would be able to maintain the original URL and bring the user back to the original page.

That is the misunderstanding.

Here is the sequence of events:

  1. The user types http://example.com/dashboard/profile/ into the location bar.
  2. The browser sends a GET request to the server for that URL.
  3. Your server responds with a 301 redirect response.
  4. The browser sees that response and sends a new GET request to http://example.com/dashboard/.
  5. The server responds with your Angular page.
  6. The Angular application starts up and looks at window.href to see what the current route is. It sees the root route and responds appropriately.

In other words, when you redirect you lose the original URL.

The solution is simple: instead of redirecting, simply return your page in response to any (valid) URL. That way the requested URL is maintained, and when Angular starts up it will be able to figure out the right route. (This assumes that routing is set up properly in Angular, but it sounds like you have that working.)

The implementation is also simple. Just change your Django urls.py from something like this:

urlpatterns = [
    url(r'^dashboard/$', my_view),
]

to something like this:

urlpatterns = [
    url(r'^dashboard/.*$', my_view),
]
Thursday, August 12, 2021
 
Adam Rackis
answered 4 Months ago
81

For me, none of the answers worked (i use ng-grid v2.0.14).

The selected answer works probably because data is either not big or is not loaded through an ajax call otherwise you can't select a row "before" ngGridEventData since the event is fired when the rows are rendered and you can't select a row if it hasn't been rendered yet.
Should any of these conditions fail or the grid take too much time than usual to render then the selected answer will not work.

I have a scrollable grid with about 2000 rows, but I haven't any restriction on listening to the ngGridEventData so i worked on that, although it has a weird behavior for me: The ngGridEventData fires exactly 4 times for me, twice before data arrives from the ajax call and twice after.
I used this jquery plugin http://benalman.com/projects/jquery-throttle-debounce-plugin/ (it can be used even without jQuery though) to make it so that the function was called only once.

And since this is not enough, the "selectRow/selectItem" function triggers the "afterSelectionChange" event twice (the first time with the wrong row, for some reason). This is what i had to do to make sure that the event is fired only once and only for the correct row.

This is what happens for me:

  • ngGridEventData (no afterSelectionChange triggers, probably because there are no rendered rows)
  • ngGridEventData (no afterSelectionChange triggers, probably because there are no rendered rows)
  • Ajax call to retrieve data
  • delay (probably rendering)
  • ngGridEventData
  • afterSelectionChange x2
  • ngGridEventData
  • afterSelectionChange x2

So i had to use this:

  • debounce to make sure the function is called only once during the delay (the timeout is low since the calls are close to one another in pair, and the rendered rows checks makes sure the first call is not the one being used)
  • check that the rendered rows are > 0 to make sure the first 2 events are not triggered on slow sistems (or slow connection) where the delay and data load might take some time
  • Optionally use rowItem.selected to avoid another "bug" since the afterSelectionChange event fires twice even when selecting a row (once for the row being unselected and once for the selected row)
  • use the fireOnlyOnce variable to avoid calling twice the afterSelectionChange function.

Here's a sample code:

$scope.fireOnlyOnce=true;
$scope.gridOptions = {
    //Stuff
    afterSelectionChange: function (rowItem) {
        if($scope.fireOnlyOnce){
            if(rowItem.selected){
                //Do stuff
            }
        } else {
            $scope.fireOnlyOnce=true;
        }
    }
};

$scope.$on('ngGridEventData', jQuery.debounce(100, function (row, event){   
    var renderedRows = row['targetScope'].renderedRows.length;
    if(renderedRows>0){
        $scope.fireOnlyOnce=false;
        $timeout(function(){$scope.gridOptions.selectRow(2, true)});
    }
}));
Thursday, August 12, 2021
 
danjah
answered 4 Months ago
89

UI-Router supports data (Model) sharing among state families. The detailed explanation could be found here

How do I share $scope data between states in angularjs ui-router?

Where we can see, that we need to introduce a Model, a cluster, a reference object.

// controller of the parent state 'services'
.controller('ServicesCtrl', ['$scope',
    function($scope) {
        $scope.Model = { prop1 : value1, ...};
}])

Because now each child state will prototypically inherit that reference to $scope.Model... we can access it in any child state controller

.controller('ServiceChildCtrl', ['$scope',
    function($scope) {
        $scope.Model.prop1 = differentValue;
}])

Check it in action in this working plunker

Sunday, August 29, 2021
 
Roola
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share