Brakebein/ngx-tagify

updating the whitelist on scroll event of the suggestion dropdown

Arnaves opened this issue · 12 comments

Brakebein can you guide me in updating the whitelist on scroll event of the suggestion dropdown

I'm not sure what you are extactly trying to achieve. You can listen to the dropdown scroll event as provided by Tagify. However, it will only fire if there is a scrollbar within the dropdown.

settings: TagifySettings = {
  callbacks: {
    'dropdown:scroll': (event) => {
      // do something with respect to the scroll percentage
      console.log(event.detail.percentage);
    }
  }
}

The dropdown:scroll event won't fire, if there is nothing to scroll. If I understand your needs correctly, you have (maybe) 3 suggestion items and want to update them, when the user uses his mouse wheel, for example. Then you need to listen to the wheel event of the dropdown element. Use the TagifyService to gain full access to the Tagify instance.

ngAfterViewInit() {
  
  this.tagifyService.get('test').DOM.dropdown.addEventListener('wheel', (event) => {
    if (event.deltaY > 0) {
      // scrolled down
    } else if (event.deltaY < 0) {
      // scrolled up
    }
  });

  // or with rxjs
  fromEvent(this.tagifyService.get('test').DOM.dropdown, 'wheel')
    .subscribe((event) => {
      console.log(event.deltaY);
    });

}

Does this lead you into the right direction?

Yes sir,
settings: TagifySettings = {
callbacks: {
'dropdown:scroll': (event) => {
// do something with respect to the scroll percentage
console.log(event.detail.percentage);
}
}
}
This will do it and scroll is always active in my case.
Also how do i detect the scroll has hit the end of scroll bar to call the next set of data.

If event.detail.percentage equals or is above 100, then you hit the end of the scrollbar. Then you can load the next set of suggestions.

Here is some code to start with. If the user scrolled down to the end of the dropdown, then the next 10 suggestion items will be appended to the list.

private suggestionList = ['A# .NET', 'A# (Axiom)', 'A-0 System', 'A+', '...']; // sample list from https://yaireo.github.io/tagify/
private length = 0;

whitelist$ = new BehaviorSubject<string[]>([]);

ngAfterViewInit() {

  // called once to set the first 10 suggestions
  this.loadSuggestions();

  // listen to scroll event
  // throttled so loadSuggestions() won't called twice accidently
  fromEvent(this.tagifyService.get('test'), 'dropdown:scroll')
    .pipe(throttleTime(50, asyncScheduler, { leading: false, trailing: true }))
    .subscribe((event: CustomEvent<Tagify.DropDownScrollEventData>) => {
      if (event.detail.percentage >= 100) {
        this.loadSuggestions();
      }
    });

}

private loadSuggestions() {

  // increase by 10
  this.length += 10;

  // set new whitelist
  this.whitelist$.next(this.suggestionList.slice(0, this.length));

  // update rendererd suggestion list
  const tagify = this.tagifyService.get('test');
  tagify.dropdown.refilter.call(tagify);

}

settings: TagifySettings = {
maxTags: 3,
enforceWhitelist: true,
placeholder: 'Start Typing...',
autoComplete:{
enabled:true,
rightKey:false,
},
editTags: false,
dropdown: {
maxItems: Infinity, // <- mixumum allowed rendered suggestions
classname: 'tags-look', // <- custom classname for this dropdown, so it could be targeted
enabled: 0, // <- show suggestions on focus
closeOnSelect: false, // <- do not hide the suggestions dropdown once an item has been selected
},
callbacks: {
'dropdown:scroll': (e) => {
// do something with respect to the scroll percentage
console.log(e.detail.percentage);
},
},
};

there is no output on scroll do I need to add something to HTML element as well?

Yes, I also recognized it. That's why I listened to it with fromEvent in ngAfterViewInit. For me, it seems to be a bug in the Tagify library. I cannot find dropdown:scroll among the other event names: https://github.com/yairEO/tagify/blob/master/src/tagify.js#L66

You could open an issue there regarding this missing event name.

So
ngAfterViewInit() {

// called once to set the first 10 suggestions
this.loadSuggestions();

// listen to scroll event
// throttled so loadSuggestions() won't called twice accidently
fromEvent(this.tagifyService.get('test'), 'dropdown:scroll')
.pipe(throttleTime(50, asyncScheduler, { leading: false, trailing: true }))
.subscribe((event: CustomEvent<Tagify.DropDownScrollEventData>) => {
if (event.detail.percentage >= 100) {
this.loadSuggestions();
}
});

}

this will work?

Well, it worked in my small testbed.

ngAfterViewInit() {
const tagifyques = this.tagifyService.get('tagsques');
fromEvent(tagifyques, 'dropdown:scroll')
.pipe(throttleTime(50, asyncScheduler, { leading: false, trailing: true }))
.subscribe((event: CustomEvent<Tagify.DropDownScrollEventData>) => {
if (event.detail.percentage >= 100) {
this.appendWhitelistquesn();
}
});

}
errors:
         const tagifyques: Tagify<TagData>

Argument of type 'Tagify' is not assignable to parameter of type 'FromEventTarget'.
Type 'Tagify' is not assignable to type 'JQueryStyleEventEmitter'.
Types of property 'on' are incompatible.
Type '<K extends keyof EventDataMap>(event: K, callback: (event: CustomEvent<EventDataMap[K]>) => void) => Tagify<...>' is not assignable to type '(eventName: string, handler: Function) => void'.
Types of parameters 'event' and 'eventName' are incompatible.
Type 'string' is not assignable to type 'keyof EventDataMap'.

No overload matches this call.

Overload 1 of 5, '(observer?: PartialObserver | undefined): Subscription', gave the following error.
Argument of type '(event: CustomEvent<Tagify.DropDownScrollEventData>) => void' is not assignable to parameter of type 'PartialObserver | undefined'.
Property 'complete' is missing in type '(event: CustomEvent<Tagify.DropDownScrollEventData>) => void' but required in type 'CompletionObserver'.
Overload 2 of 5, '(next?: ((value: unknown) => void) | undefined, error?: ((error: any) => void) | undefined, complete?: (() => void) | undefined): Subscription', gave the following error.
Argument of type '(event: CustomEvent<Tagify.DropDownScrollEventData>) => void' is not assignable to parameter of type '(value: unknown) => void'.
Types of parameters 'event' and 'value' are incompatible.
Type 'unknown' is not assignable to type 'CustomEvent<DropDownScrollEventData>'.ts(2769)

I'm not sure, why TypeScript is moaning about this. It doesn't do so in my example code. Tagify doesn't fully match the type that is expected by fromEvent.

You can try to add @ts-ignore above this line to ignore type checking:

// @ts-ignore
fromEvent(this.tagifyService.get('test'), 'dropdown:scroll')

Or set "strict": false in your `tsconfig.json:

"compilerOptions": {
  "strict": false
}

Alternatively, you can still listen to the event this way:

this.tagifyService.get('test').on('dropdown:scroll', event => {
  console.log(event.detail.percentage);
});

this.tagifyService.get('test').on('dropdown:scroll', event => {
console.log(event.detail.percentage);
});
this worked with ngAfterViewInit() though the appending happens once, when the dropdown is closed and opened again the append again works

another help ,how to add the first suggestion on pressing tab

I think you would need to override the respective dropdown callback. The code for case 'Tab' would have to be the same as 'Enter', alternatively Tab could fall through to Enter.

https://github.com/yairEO/tagify/blob/master/src/parts/dropdown.js#L303-L376

You can try to override this way (I haven't tested it this time):

const tagify = this.tagfiyService('test');

// @ts-ignore - because dropdown.events.callback is not on the type declarations
tagify.dropdown.events.callback.onKeyDown = function (e) {
  // no arrow function, since `this` is used and refers to the tagify instance

  // copy code and change `case 'Tab':` appropriately
}

Since this is all @yaireo/tagify related and less ngx-tagify, you might want to address your issues there: https://github.com/yairEO/tagify/issues The developer knows his code best.

ngx-tagify is only a wrapper for Angular, it only exposes the most common things. That's why there is the TagifyService to get the core Tagify instance, for those who want to deeper customize it. In this case, you can directly refer to @yaireo/tagify