Roy Roy - 5 months ago 33
AngularJS Question

Two way Binding in a custom directive without using NgModel. Possible?

I am implementing a dropdown for which I am writing my own directive. I am not using any kind of input element hence not using ngModel. Is two way binding possible with custom attributes?



var mainApp = angular.module('mainApp', []);

mainApp.directive('tableDropdown', ['$timeout',
function($timeout) {
return {
restrict: 'C',
scope: {
selectedFilter: '=?'
},
link: function(scope, elem, attrs) {
$timeout(function() {
angular.element(elem).find('li:first-child').addClass(angular.element(elem).find('li').hasClass('selected') ? '' : 'selected');
scope.selectedFilter.cycleStatus = null;
angular.element(elem).find('li').click(function(e) {
if (angular.element(this).closest('ul').hasClass('active')) {
angular.element(this).closest('ul').removeClass('active');
scope.selectedFilter.selected = angular.element(this).attr('value');
} else {
angular.element(this).closest('ul').addClass('active');
scope.selectedFilter.selected = null;
}
angular.element(this).addClass('selected').siblings().removeClass('selected');
})
}, 0);
}
}
}
])

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<th ng-app="mainApp" ng-init="datefilter.selected=null">
--{{datefilter.selected}}
<ul class="tableDropdown" selected-filter="datefilter.selected">
<li value="null" class="default"><span>Cycle Status</span>
</li>
<li value="completed"><span>Completed</span>
</li>
<li value="cancelled"><span>Cancelled</span>
</li>
</ul>
</th>





Please note I did not add the CSS which makes it look like a dropdown. I didn't think it was necessary.

I want to get the selected value in
datefilter.selected
and then use it to do something else. Is that even possible? If not are there any workaround?

Answer

For the time being, disregarding your code to setup the classes, you can use the below code to change datefilter.selected.cycleStatus from inside the directive and it would reflect in the UI.

UI:

--{{datefilter.selected.cycleStatus}}
  <ul class="tableDropdown" selected-filter="datefilter.selected">
    <li value="null" class="default"><span>Cycle Status</span>
    </li>
    <li value="completed"><span>Completed</span>
    </li>
    <li value="cancelled"><span>Cancelled</span>
    </li>
  </ul>

Code:

scope: {
    selectedFilter: '='
  },
link: function(scope, elem, attrs) {

        $timeout(function() {
          scope.selectedFilter.cycleStatus = null;
          elem.find('li').click( 
            function(e) {
              scope.selectedFilter.cycleStatus =angular.element(this).attr('value');
              scope.$apply();
            });


        }, 0);
      }

Note the scope.$apply() , which is responsible for changing the value in the UI automatically (otherwise it might reflect late after some other element forces the digest cycle)

Here is an example fiddle: http://jsfiddle.net/Lvc0u55v/5503/

Comments