3

I am using Angular ui-select to create a "search-and-select component". When the user types into the search field, it immediately filters a list of items as a free-text filter. Additionally, the user can select options from the dropdown, which are used as exact search terms in the filter (e.g. filter by a category).

I have created an additional directive that extends <ui-select> in order to access the $select.search value from that scope. This variable contains the free-text typed by the user. My question is how can I pass this to the parent controller?

Ideally, I'd like something like the following:

<ui-select
  my-ui-select
  on-search-changed="searchChanged(newValue, oldValue)"
  multiple
  ng-model="ctrl.myModel">
    <!-- ... -->
</ui-select>

My custom directive would call the on-search-changed callback with the free text values. The problem is that I cannot define a scope for my-ui-select directive because it would conflict with the ui-select scope.

How can I pass a callback method to my custom directive, while still being able to access the ui-select scope? Or is there a better way to achieve what I'm after?

I created a Plunker based on the ui-select examples. I have defined the myUiSelect directive which uses console.log to output the search terms. What I want is to call the DemoCtrl.searchChanged method from there.

Sampo
  • 4,308
  • 6
  • 35
  • 51

2 Answers2

0

In your custom directive where you call on-search-changed callback you could emit an event with $emit. The documentation about $emit:

Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.

So you can do this:

$scope.$emit("mySelectEventThing", "Hey this is the value I want to emit");

And in your parent scope/controller you could listen to the events:

$scope.$on("mySelectEventThing", function(event, theValue) {
    alert(theValue); // alert with 'Hey this is the value I want to emit'
});

$emit broadcasts upwards to the parent scopes. $broadcast sends downwards to all child scopes. $broadcast cost a bit more performance if you have alot scopes. You probably would not need this.

Emitting event's is the only way in Angular to pass data to another scope properly.

You also could reach the parent scope by using $scope.$parent, but this is very bad practice because you can't ensure that you will reach the expected scope.

com2ghz
  • 2,706
  • 3
  • 16
  • 27
0

I think you should change your approach, instead of extending ui select in your directive, you should wrap it inside yoru directive and pass the function from your controller to be called in directive.

Code for the directive

app.directive('myUiSelect', function() {
    return {
    templateUrl:'myUiSelect.html',
    scope:{
      onSearchChanged:'=',
      selectedItem:'=',
      items:'='
    },
    link: function(scope, element, attrs, $select) {
    scope.selectedItemModel = {
        selectedItem:[]

    }
    scope.onSelectCallback = function(item, selectedItems){

    scope.onSearchChanged(scope.selectedItemModel.selectedItem, item)

   }
  }
 };
})

html partial(myUiSelect.html)

<ui-select multiple 
       ng-model="selectedItemModel.selectedItem" 
       theme="bootstrap" 
       ng-disabled="ctrl.disabled" 
       sortable="true"
       on-select="onSelectCallback($item, $model)"
       close-on-select="false" 
       style="width: 800px;">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in items | propsFilter: {name: $select.search, age: $select.search}">
  <div ng-bind-html="person.name | highlight: $select.search"></div>
  <small>
    email: {{person.email}}
    age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
  </small>
 </ui-select-choices>
 </ui-select>

Here is the working Plunker

Please let me know if my understanding of the question is wrong

AbhiGoel
  • 201
  • 1
  • 6