Search code examples
angularjsangularjs-directiveangularjs-ng-options

Fill select with ng-options inside Directive with isolated scope


i can't figure out why the ng-options directive doesn't work in my example:

http://plnkr.co/edit/wwAIDHl6U7YaScRzIKAY

app.coffee

app = angular.module('plunker', [])

app.controller 'MainCtrl', ($scope) ->
  $scope.query = {}
  $scope.types = ["Test", "Demo", "Numeric", "ABC"]
  $scope.items = [{typ: "Test"},{typ: "Test"},{typ: "Demo"}, {typ: "Numeric"}]

app.directive 'colFilterable', ($parse) ->
  {
    restrict: 'A',
    scope: {
      colFilterable: '=',
      filterableKey: '@',
      filterableOptions: '='
      filterableOptionsArg: '@',
    },
    compile: (el, $scope) ->
      if $scope.filterableKey
        key = $scope.filterableKey
      else
        key = el.text().toLowerCase()

      options = 'label for value in filterableOptions'
      if $scope.filterableOptionsArg
        options = $scope.filterableOptionsArg

      el.append('<select class="col-filterable" ng-model="filter" ng-options="' + options + '"><option></option></select>')

      return ($scope, el, attrs) ->
        $scope.$watch 'filter', () ->
          $scope.colFilterable[key] = $scope.filter
  }

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script data-require="angular.js@1.2.x" src="https://code.angularjs.org/1.2.25/angular.js" data-semver="1.2.25"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <h2>DEMO</h2>
    <table>
      <thead>
        <tr>
          <th col-filterable="query" filterable-options="types">
          Typ
          </th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="item in items | filter: query">
          <td>{{item.typ}}
        </tr>
      </tbody>
    </table>

    <h2>DEBUG</h2>
    <p>{{ types }}</p>
    <p>{{ query }}</p>
  </body>

</html>

I'm appending the select with the ng-options (which can be passed in, but defaults to label for value in filterableOptions) in the compile function and return a link function that watches the ng-model and sets the data-binded $scope.colFilterable to the value of $scope.filter

I can't use a template since i want to retain what is currently inside the element and just want to append this select which in return should filter the resulting table

edit:

okay, seems that the $scope.$watch 'filter' wouldn't work either way and the result wouldn't get propagated to the parent scope, so i rewrote it to use the change event http://plnkr.co/edit/qzaf1sBoFuVD8fow0tfA

el.find('select').bind 'change', (event) ->
  $scope.colFilterable[key] = this.value
  $scope.$apply()

this works if i manually write the option elements


Solution

  • okay, i figured it out by using the template and the link function instead of the compile function. this is my solution now

    app.directive 'colFilterable', ($parse) ->
      {
        restrict: 'A',
        scope: {
          colFilterable: '=',
          filterableKey: '@',
          filterableOptions: '='
          filterableOptionsArg: '@',
        },
        template: (el, attr) ->
          options = 'value as value for value in filterableOptions'
          if attr.filterableOptionsArg
            options = attr.filterableOptionsArg
    
          return el.html() + '<select class="col-filterable" ng-model="filter" ng-options="' + options + '"><option value="">Alle</option></select>'
    
        link: ($scope, el, attr) ->
          if attr.filterableKey
            key = attr.filterableKey
          else
            key = el.text().toLowerCase().trim()
    
          el.find('select').bind 'change', (event) ->
            if $scope.filter
              $scope.colFilterable[key] = $scope.filter
            else
              delete $scope.colFilterable[key]
            $scope.$apply()
      }
    

    http://plnkr.co/edit/50shUSLVTI0NLjqMJv4h