2

I'm having a problem understanding scope. ui-bootstrap creates a new scope when you use tabset, I get that. I thought parent methods were available to child scopes? Here is a jsbin showing my problem.

JSBin

I'm sure there is something simple I'm missing, but I can't see it.

Code inline in case JSBin acts up:

angular.module('app', ['ui.bootstrap'])
.controller('MainController', function($scope) {
  $scope.filters = [];
  $scope.search = '';
  $scope.providerVersions = [
    { name:'SomeOS', type:'pv' },
    { name:'OtherOS', type:'pv' }
  ];
  $scope.scripts = [
    { name:'Something', pv:'SomeOS', type:'script' },
    { name:'Somebody', pv:'SomeOS', type:'script' },
    { name:'Other thing', pv:'OtherOS', type:'script' },
    { name:'Other body', pv:'OtherOS', type:'script' }
  ];

  $scope.addFilter = function(f) {
    $scope.filters.push({ name:f.name, type:f.type });
  };

  $scope.remFilter = function(i) {
    $scope.filters.splice(i,1);
  };

  $scope.filterByName = function(n) {
      var name = n.name.toLowerCase();
      var search = $scope.search.toLowerCase();
      return name.indexOf(search) > -1;
  };

  $scope.filterByFilters = function(f) {
    if ($scope.filters.length===0) { return true; }

    var byName = _.where($scope.filters, { type:'script' });
    if (byName.length > 0) {
      return _.contains(_.pluck(byName, 'name'), f.name);
    }
    return _.contains(_.pluck($scope.filters, 'name'), f.pv);
  };
});

HTML

<body ng-app="app">
  <div ng-controller="MainController">
    <h3>This works</h3>
    <p>Filters on both name and role as expected.</p>
    <div ng-repeat="s in scripts|filter:filterByFilters">
      {{ s.name }}
    </div>
    <form name="searchForm">
      <input type="text" class="form-control" placeholder="Filter by name or role" ng-model="search">
    </form>
    <span ng-repeat="f in filters"><a ng-click="remFilter($index)">{{ f.name }}</a><span ng-if="!$last">, </span></span>
    <ul ng-show="search.length>0" class="dropdown-menu" style="display:block; position:static;">
      <li class="dropdown-header">Name</li>
      <li ng-repeat="s in scripts|filter:filterByName"><a  ng-click="addFilter(s)">{{ s.name }}</a></li>
  <li class="divider"></li>
      <li class="dropdown-header">Role</li>
      <li ng-repeat="p in providerVersions|filter:search"><a ng-click="addFilter(p)">{{ p.name }}</a></li>
</ul>

    <h3>This does not work</h3>
    <p>Only filters on role. It does not call $scope.filterByName.</p>
    <tabset>
      <tab heading="Scripts">
        <div ng-repeat="s in scripts|filter:filterByFilters">
      {{ s.name }}
    </div>
    <form name="searchForm">
      <input type="text" class="form-control" placeholder="Filter by name or role" ng-model="search">
    </form>
    <span ng-repeat="f in filters"><a ng-click="remFilter($index)">{{ f.name }}</a><span ng-if="!$last">, </span></span>
    <ul ng-show="search.length>0" class="dropdown-menu" style="display:block; position:static;">
      <li class="dropdown-header">Name</li>
      <li ng-repeat="s in scripts|filter:filterByName"><a  ng-click="addFilter(s)">{{ s.name }}</a></li>
  <li class="divider"></li>
      <li class="dropdown-header">Role</li>
      <li ng-repeat="p in providerVersions|filter:search"><a ng-click="addFilter(p)">{{ p.name }}</a></li>
</ul>
      </tab>
    </tabset>
  </div>
</body>
2
  • Tabs are probably implemented as directives which have isolated scopes and do not form a parent/child relationships with other scopes. Beyond that, it seems you've shared a lot of code, but I'm not sure what the problem. Commented Apr 29, 2014 at 20:15
  • You are correct Tabs is a directive. I have functions on the parent scope that I need to call in those drop downs. That scope is transcluded I believe. Commented Apr 29, 2014 at 20:22

2 Answers 2

2

Scopes are interesting and confusing. All scopes, except $rootScape, have ancestors. I don't know for certain but I assume that the ui-bootstrap tabset creates its own isolate scope. That isolate scope does have a parent, but you've "isolated" it so it can't see any attributes of an ancestor scopes unless you've specifically included them within the directive. Should an isolate scope have a child scope that is not an isolate scope, it can see and manipulate its parents attributes but nothing farther back the ancestor chain. Events can pass freely up and down the chain and you should be very careful using them since, when dealing with isolate scopes, may cause side effects you aren't expecting.

If the above was just a bunch of blather, go to https://docs.angularjs.org/guide/directive and reread the info there - maybe that will make it more clear.

Here's probably one of the best dissertation on scope you'll fnd in a quick, concise and clear explanation - What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Sign up to request clarification or add additional context in comments.

Comments

0

I was able to work around this. Honestly I don't know why this works, but it does. Maybe somebody else can fill in the gaps for me. What I did was instead of trying to filter my list, I changed the filter function to return an array, and I use that result for ng-repeat to iterate.

Old filter function

$scope.filterByName = function(n) {
  var name = n.name.toLowerCase();
  var search = $scope.search.toLowerCase();
  return name.indexOf(search) > -1;
};

New filter function

$scope.filterByName = function(list, srch) {
        var ret = [];

        _.each(list, function(l) {
            if (l.name.toLowerCase().indexOf(srch.toLowerCase()) > -1) {
                ret.push(l);
            }
        });

        return ret;
    };

Old ng-repeat

<li ng-repeat="s in scripts|filter:filterByName">

New ng-repeat

<li ng-repeat="s in filterByName(scripts, search)">

2 Comments

why are you using underscore's foreach which is just a synomym for javascript foreach? You're writing an angular app, and are in the angular context, shouldn't you be using angular.foreach?
Hey mortashi, honestly? Habit. I've gotten really comfortable with underscore so I use it all over the place. For this project I do not need to support IE8 so I could use javascript's each, but in real world use for this particular app I have not noticed a performance difference. Thanks for the call out, I should not be lazy coding by habit like this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.