1

I am building up a custom select box with multi select as like angular material chips..

HTML

<div class="autocomplete">
    <div class="chips-input-container">
        <div class="col-md-4">
            <div class="user-chip" *ngFor="let user of userSelects">
                {{user.name}}
                <span (click)="deleteSelects(user)" class="delete-icon-chip">&#10006;</span>
            </div>
        </div>

        <div class="col-md-4 form-label-group">
            <input name="suggestion" type="text" id="autocomplete-input" class="form-control" placeholder="User" (click)="suggest()"
             [(ngModel)]="userSelectsString" (keyup)="onKey($event)" id="autocomplete-input">
            <label for="autocomplete-input" class="emailBox"></label>
            <label class="fa fa-caret-down input-icon"></label>
        </div>
    </div>

    <ul id="autocomplete-results" class="autocomplete-items" *ngIf="show">
        <li *ngFor="let s of suggestions" [ngClass]="isSelected(s) ? 'selected-suggestion' : ''" (click)="selectSuggestion(s)">{{ s.name }}</li>
    </ul>
</div>

TS:

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
   suggestion: string = '';
  typeahead: FormControl = new FormControl();
  openSelectBox: boolean = false;

  fieldHistory: string[] = [];

  inputValue: string;
  autocomplete_results: any;
  input = document.querySelector('#autocomplete-input');
  userSelectsString = '';
  name = 'Angular';
  userSelects = [];
  suggestions = [{"id":"001","name":"mango"},{"id":"002","name":"apple"},{"id":"003","name":"banana"},{"id":"004","name":"pine"},{"id":"005","name":"orange"},{"id":"006","name":"chery"},{"id":"007","name":"watermelon"},{"id":"008","name":"grapes"},{"id":"009","name":"lemon"}];

  show: boolean = false;

  suggest() {
    this.show = true;
  }

  isSelected(s:any) {
   return this.userSelects.findIndex((item) => item.id === s.id) > -1 ? true : false;
  }

  selectSuggestion(s) {
    this.userSelects.find((item) => item.id === s.id) ? 
    this.userSelects = this.userSelects.filter((item) => item.id !== s.id) :
    this.userSelects.push(s);
    this.show = false;
  }

  deleteSelects(s) {
    this.userSelects = this.userSelects.filter((item) => item.id !== s.id);
  }

  assignToNgModel() {
    this.userSelectsString = '';
    this.userSelects.map((item) => this.userSelectsString += item.name + ' ');
  }

  onKey(e) {
    this.inputValue = e.target.value;

    if (this.inputValue.length > 0) {
      var people_to_show = [];

      this.autocomplete_results = document.getElementById("autocomplete-results");
      this.autocomplete_results.innerHTML = '';
      people_to_show = this.autocomplete(this.inputValue);

      for (let i = 0; i < people_to_show.length; i++) {
        this.autocomplete_results.innerHTML += '<li>' + people_to_show[i] + '</li>';
      }
      this.autocomplete_results.style.display = 'block';
    } else {
      people_to_show = [];
      this.autocomplete_results.innerHTML = '';
    }
  }

  autocomplete(val) {
    var people_return = [];
    for (let i = 0; i < this.suggestions.length; i++) {
      if (val === this.suggestions[i].slice(0, val.length)) {
        people_return.push(this.suggestions[i]);
      }
    }
    return people_return;
  }
}

As of selection and deletion part, everything works fine but when i implemented autocomplete, i am unable to get result as i am using slice for the array of objects.

My data is:

  suggestions = [{"id":"001","name":"mango"},{"id":"002","name":"apple"},{"id":"003","name":"banana"},{"id":"004","name":"pine"},{"id":"005","name":"orange"},{"id":"006","name":"chery"},{"id":"007","name":"watermelon"},{"id":"008","name":"grapes"},{"id":"009","name":"lemon"}];

In the for loop, i am getting error as Property 'slice' does not exist on type '{ "id": string; "name": string; }'. in the linethis.suggestions[i].slice(0, val.length),

  for (let i = 0; i < this.suggestions.length; i++) {
      if (val === this.suggestions[i].slice(0, val.length)) {
        people_return.push(this.suggestions[i]);
      }
    }

If i give any inside suggestions: any = [{"id":"001","name":"mango"},...}], it is showing slice is not a function.

Kindly help me to achieve the result of autocomplete.

Stackblitz: https://stackblitz.com/edit/angular-euuvxw

Maniraj Murugan
  • 8,868
  • 20
  • 67
  • 116
  • When you do `this.suggestions[i].slice` your trying to slice an object, not an array – user184994 Aug 23 '18 at 11:21
  • slice() use with array type not with object. – mittal bhatt Aug 23 '18 at 11:25
  • This is unrelated to the question, but I hope you're aware that adding strings into `innerHTML` without escaping can lead to security problems if they come from a source that you don't control and contain HTML code. You can [escape the HTML](https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript). – Matt McCutchen Aug 23 '18 at 11:33

2 Answers2

3

You probably want to slice the name, not the entire suggestion object. And looking at how you are using the result, you probably want to push only the name into people_return too:

for (let i = 0; i < this.suggestions.length; i++) {
  if (val === this.suggestions[i].name.slice(0, val.length)) {
  //                              ^^^^
    people_return.push(this.suggestions[i].name);
  }
}
Matt McCutchen
  • 28,856
  • 2
  • 68
  • 75
  • It is showing the result list as ```[object Object]```, if i type ```man``` for mango in input box.. – Maniraj Murugan Aug 23 '18 at 11:26
  • But i couldn't get the list view as i have shown in the stackblitz.. Is it because i am using javascript i am unable to get the result like onclick? – Maniraj Murugan Aug 23 '18 at 11:39
  • It would be much more helpful if you help me to fix that part, as i am beginner i couldn't move forward with it.. I am sorry for the same.. If you update that also it would be better solution for me.. – Maniraj Murugan Aug 23 '18 at 11:41
  • I went to the stackblitz and just saw a page with "Hello Angular! Start editing to see some magic happen :)". Did you save the stackblitz after your latest changes? – Matt McCutchen Aug 23 '18 at 11:42
  • It looks like the list styling is generated by your Angular template, and when you overwrite the HTML, you are losing the styling. I messed around to try to make the autocomplete display go through the Angular template and got something working: https://stackblitz.com/edit/angular-7jepkn . Caveat: I don't know Angular at all, so my solution may violate Angular expectations and cause problems in the future. – Matt McCutchen Aug 23 '18 at 11:54
  • Thank you @Matt for your valuable time for helping me.. It is useful for me. – Maniraj Murugan Aug 23 '18 at 11:58
0

You need to use slice() in array type value. In your case it is this.suggestions so use this.suggestions.slice(0, val.length)

Ankit Agarwal
  • 30,378
  • 5
  • 37
  • 62