Giter Site home page Giter Site logo

oasisdigital / angular-material-search-select Goto Github PK

View Code? Open in Web Editor NEW
53.0 5.0 21.0 13.97 MB

Angular-material search-select widget with observable option list

Home Page: https://oasisdigital.github.io/angular-material-search-select/

License: MIT License

TypeScript 65.14% JavaScript 5.96% HTML 26.27% CSS 2.64%
angular material-design autocomplete

angular-material-search-select's Introduction

Angular Material Search-select control

This library implements a "search select" or "select with search" control, to match (and building on) Angular Material. To provide the UI, the Material mat-autocomplete control is used; but the result is not quite an autocomplete per se, it really is a search select, intended to pick an item from a list.

Video demo and explanation

Video explaining search-select

How it works, and why

This control handles cases where the displayed 'name' does not match the internal ID. As a developer using this control, you supply methods to translate from ID to displayed name, and to search data during selection.

Both directions of data lookup/search are handles asynchronously, suitable for a database-based, long list of searchable items.

We have found this problem comes up regularly on our projects, and have finally created this reusable solution. This repository/project contains both the component itself, and screens that demonstrate its use.

Try it

Demo running at:

https://oasisdigital.github.io/angular-material-search-select/

NPM package:

https://www.npmjs.com/package/@oasisdigital/angular-material-search-select

To use this in a project

npm install --save @oasisdigital/angular-material-search-select

There are two ways to consume this code. You can see examples of them in the various tabs of the demo.

  1. Use the provided search-select component, as shown in "simple", "Long slow", "real API".
  2. Write a custom component using only the base class provided in this package; your component will provide all the visuals, but typically doesn't need to provide any code at all. For an example of this, look in the source code, directory demo-5-multicolumn and the corresponding multi-column demo tab.

For better or worse, various aspects of HTML, CSS, Angular, and other tools conspire to frequently require option 2. This is the only way to provide full flexibility in the appearance of the component, to match your application's needs.

Regardless of which layout approach you choose, at run time you must provide a DataSource, which is similar conceptually to the DataSource concept used by a mat-table; that interface defines (and application code must provide) methods:

  • Method which searches for a values, and provides corresponding display text.
  • Method which takes a search query fragment and provides a list of match candidates.
  • Optionally, a match method that can widen the definition of a matching entry - for example, make it a user can type an entry that varies from a list entry only in upper/lowercase.

Study the example code in the various demo-N-* for more about these methods. It shows several ways to provide such a data source.

Also an example of how to publish a component

In addition to being a useful component, we have been using this repository as an example of how to publish a Angular component to NPM. You can see these patterns in use here, and borrow them for other components:

  • Start with an Angular CLI project.
  • Add a module inside, for the components to be packaged.
  • Make the overall application provide a demo, test environment, etc. for the components in the to-be-packaged module.
  • Use ng-packagr: outsource the complexity in bundling to that brilliant package.

Who?

Primary authors:

  • Kyle Cordes, twitter @kylecordes
  • Paul Spears, twitter @TheEvergreenDev

We both work at Oasis Digital / Angular Boot Camp.

angular-material-search-select's People

Contributors

craigeddy avatar matthewdfleming avatar melroy89 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

angular-material-search-select's Issues

this.myService is undefined when using Search Select dataSource

Hi,

I'm trying to use my own Angular service (like I already do for all my components) for my API call requests instead of directly calling HttpClient within the component.

Using a service is just a common practice, I don't know why this is not working. I'm getting: this.ledgerService is undefined.

  this.dataSource = {
      displayValue(value: any): Observable<OptionEntry | null> {
        ....
        return this.ledgerService.getOne(value) <!---------- Here is the problem somehow
        .pipe(
          map((ledger: Ledger) => ({
            value: ledger.ledger_id,
            display: `${ledger.ledger_number} - ${ledger.ledger_name}`,
            details: {}
          }))
       .....

Here you see my full code:
https://gitlab.melroy.org/snippets/13

Why is this not working but using HttpClient service is working in your example ๐Ÿค• ?

Kind regards,
Melroy van den Berg

Peer dependency problem

Since npm v7 npm install fails due to an incorrect peer dependency on your project with @angular/common.

Error message:

npm ERR! While resolving: [email protected]
npm ERR! Found: @angular/[email protected]
npm ERR! node_modules/@angular/common
npm ERR!   @angular/common@"^16.0.2" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/common@"^9.0.2" from @oasisdigital/[email protected]
npm ERR! node_modules/@oasisdigital/angular-material-search-select
npm ERR!   @oasisdigital/angular-material-search-select@"^1.1.1" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 

Please, try to resolve this issue.

Regarding Search select with real (remote) API data

Thanks for useful demo.

I need Out Put Like below
AccountName
PrimaryPhone EmailAddress

I want to Break the line after first field how toimplement.

I am using the same code then output shown below.

search(term: string): Observable<OptionEntry[]> {
console.log('searching for', term);
return http.get<Account[]>(apiURL, {
params: {
q: term || '',
_sort: 'AccountName,EmailAddress'
}
}).pipe(
map(list => list.map(e => ({
value: e.AccountID,
display: ${e.accountName}${e.primaryPhone }(${e.emailAddress}),
details: {}
}))));
}
};

Current Out Put :
AccountName PrimaryPhone EmailAddress

Please help me for this.

Make it easier to see and manipulate the value and demos

It would be better if the demo screens would directly bind the control value into a form input, so when experimenting we could just type in a different value, this would replace the current demo mechanism where you type of value and then click "set".

However, this runs directly into an Angular bug:

angular/angular#13792

The essence of this bug is that value changes from one form control do not propagate into another form control pointing to the same FormControl object with reactive forms. This is a frustrating bug, I hope it gets fixed soon.

Avoid timeout and others

Is it possible to avoid the setTimeout? As you plan a future PR in material 2 repo, the team avoids using setTimeout's.

https://github.com/OasisDigital/angular-material-obs-autocomplete/blob/b5e1a5fdd43b7a8e0391760e84acb2b26c97fc52/src/app/obs-autocomplete/obs-autocomplete-base.ts#L42-L49

You could take a look in MatAutocompleteTrigger.panelClosingActions as it's shown over here: angular/components#3334 (comment)

Also, there are recommendations about coding in Material repo: https://github.com/angular/material2/blob/master/CODING_STANDARDS.md

I've noticed the code style is a very important aspect among the guys developing Material. Tests are mandatory. You'll have to write them.

And, above all that... ask them whether you should be doing it before spending time preparing your PR. Not all features in Material Repo are opened for direct contribution. This kind of feature seems to me it's something that should be part of the project core. So there's a guideline that any core feature must be exhaustively discussed (internally to Google). Force selection is in this step (you can be sure that a topic with more than 40 comments is not being ignored by the project leader, Jeremy).

Rejecting PR's is not so frequent, but if your work is out of sync with their timeline, it can be frozen for months or even years awaiting for a merging action (and it's possible that you'll have to rebase it then).

Dispite of all this bureaucracy thing, I think your component is very nice, and your examples are really great (but the setTimeout thing... I don't like the idea of counting on timing to take actions - even in this case, where aparently you are just pushing the subscription to the end of the line in the event loop).

[edited]: shouldn't it be this.selectedValueSub = this.selectedValue.pipe(...

Select drop-down list not fully visible

Hi,

The select list (drop-down/up list), is not always fully visible. And appears also outside the browser's view port.

This is an example of what I mean:
image

You tried in your demo to 'solve' this by adding some empty div below the select element. Which is not ideal in my situation, furthermore it's also not fixing my problem, unless the end-user scrolls-down the page first (otherwise this extra space will not help you)....

Kind regards,
Melroy van den Berg

Trigger displayValue without using FormControl

I know you can set a new (default) value via the formControl reactive forms.

By creating a new FormControl:

ours = new FormControl(...);

And set the value via:

this.ours.setValue(3);

But since I don't want to provide any input element for the end-user to set a value, but rather my components knows which is the current value to set as default (retrieved from the database), I have the feeling this FormControl is overkill for me.

Is there another way to trigger displayValue(..) without using FormControl?

Thanks in advance.

Kind regards,
Melroy

Upgrade to Angular 7

Could you please upgrade this to Angular 7 dependencies.. Everything seems to work fine using Angular 7 just getting build warnings.

Make it possible to use the search-select within a mat-form-field element

Hi Kyle,

Let's start with an excuse that I maybe spam too much, which is not my intention at all โค๏ธ , but hopefully you agree this item is a real issue.

I would like to use search-select within the Angular Material mat-form-fied, just like my normal mat-select elements.

When I try to use your search-select within a mat-form-field, I currently get the error:

mat-form-field must contain a MatFormFieldControl

For example it would be nice if I still could this this search-select like so:

 <mat-form-field>
        <search-select ...></search-select>
        <mat-icon matPrefix>credit_card</mat-icon>
 </mat-form-fied>

Kind regards,
Melroy van den Berg

Not working - Upgraded to Angular 6.0 & RxJS 6.x

Angular 6.0 is out! Maybe a good idea to upgrade as well?
Together with RxJS (try to run without rxjs-compat package).

Since I can't get it working anymore when I use Angular 6.0...

Kind regards,
Melroy

Add Angular 9 support

Hello, i have been facing an error while angular 9 upgrade process of my project.

`Compiling @oasisdigital/angular-material-search-select : es2015 as esm2015
Error: Error on worker #5: Error: Failed to compile entry-point @oasisdigital/angular-material-search-select (es2015 as esm2015) due to compilation errors:
node_modules/@oasisdigital/angular-material-search-select/fesm2015/oasisdigital-angular-material-search-select.js:203:18 - error NG1010: Value at position 1 in the NgModule.imports of SearchSelectModule is not a reference: [object Object]

203 imports: [
~
204 CommonModule,
~~~~~~~~~~~~~~~~~~~~~~~~~
...
211 ReactiveFormsModule
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
212 ],
~~~~~~~~~

at ClusterWorker.compile (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/main.js:177:27)
at Worker.<anonymous> (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/worker.js:44:42)
at Worker.emit (events.js:321:20)
at process.<anonymous> (internal/cluster/worker.js:32:12)
at process.emit (events.js:321:20)
at emit (internal/child_process.js:881:12)
at processTicksAndRejections (internal/process/task_queues.js:85:21)
at ClusterMaster.onWorkerMessage (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:158:27)
at /Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:46:95
at ClusterMaster.<anonymous> (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:238:57)
at step (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/tslib/tslib.js:136:27)
at Object.next (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/tslib/tslib.js:117:57)
at /Users/umutcelik/code/magistum/magistum-pwa/node_modules/tslib/tslib.js:110:75
at new Promise (<anonymous>)
at Object.__awaiter (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/tslib/tslib.js:106:16)
at EventEmitter.<anonymous> (/Users/umutcelik/code/magistum/magistum-pwa/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:232:32)
at EventEmitter.emit (events.js:321:20)`

Make compatible with @angular/material version 6

running ng update @angular/material on a angular material 5 project will give error:

Package "angular-material-obs-autocomplete" has an incompatible peer dependency to "@angular/material" (requires "^5.0.1", would install "6.0.1").

Question: Can I invoke setValue on search-select component from Observable completion handler?

I have a component which has a search-select component as a child and in the ngOnInit for my component, I am initializing the datasource of the search-select. I want to programmatically set the selected value in the search-select once I have loaded all of my data from the external data source. I have tested setting the value in the search-select from a button click and it works just fine, but I have been unable to successfully set the value from either ngOnInit or ngAfterViewInit of my component via a completion handler that I set up on the observable that is supplying my data. Is there a known issue with this or an alternate way for me to set the value programatically? In my case, I can't set the value triggered on an event from the user, the selected value is coming from the server.

In the code below, this.formField is an object with metadata about the FormControl (such as the id of the control, the value that should be selected, etc.) and in this case the FormControl is the search-select component bound via a formControlName directive. All other aspects of our use of the search-select are working fine, it's just this one problem of setting the select value upon initialization that is causing us problems

  updateAutoCompleteDataSource() {
    this.valuesSubscription = this.screenService.getFieldValues(this.formField.id).subscribe((data) => {
      const values: Array<string> = data;
      const fld = this.formField;
      console.log('asynchronously received ' + values.length + ' items for ' + fld.id);
      
      this.autoCompleteDataSource = {
        displayValue(value: any): Observable<OptionEntry | null> {
          console.log(`being asked to display value for: ${value}`);
          const display = <string>value;
          return of({
            value,
            display,
            details: {}
          });
        },

        search(term: string): Observable<OptionEntry[]> {
          console.log(`autocomplete searching for '${term}' on field '${fld.id}' with ${values.length} values`);
          const lowerTerm = typeof term === 'string' ? term.toLowerCase() : '';
          return of(values
            .filter((c: any) => c.toLowerCase().indexOf(lowerTerm) >= 0)
            .slice(0, 1000).map((v: any) => ({
              value: v,
              display: v,
              details: {}
            })));
        }
      };
    },
    (error) => {},
    () => {
      console.log(`Attempting to set value of '${this.formField.value}' on field '${this.formField.id}'`);
      this.formGroup.get(this.formField.id).setValue(this.formField.value);
    }
  );
  }

In the completion handler of the Observable above, I am attempting to set the value. Thanks for your review.

Can't bind to 'formControl' since it isn't a known property of 'search-select'.

Hi,

I try to use your project. But I'm getting an error and I don't know what to do.

Expected behavior: I can use the search-select together with the formControl attribute.

Actual behavior: I'm getting a blocking error when I try to use search-select within my HTML.

I did add the SearchSelectModule to my own module file (my.module.ts) like so:

import { MyDetailComponent } from './my-detail/my-detail.component';
import { SearchSelectModule } from '@oasisdigital/angular-material-search-select';

@NgModule({
  imports: [
    ....
    SearchSelectModule
  ], 
  declarations: [MyDetailComponent, ...]

And defined the local variables in one of the components: my-detail/my-detail.component.ts:

import { FormControl, Validators } from '@angular/forms';
import { OptionEntry, DataSource } from '@oasisdigital/angular-material-search-select';
...

export class MyDetailComponent implements OnInit {
  ours = new FormControl(null, [Validators.required]);
  dataSource: DataSource;

This is the only error I get:

Error: Template parse errors:
Can't bind to 'formControl' since it isn't a known property of 'search-select'.
1. If 'search-select' is an Angular component and it has 'formControl' input, then verify that it is part of this module.
2. If 'search-select' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("
          placeholder="Company"
          width="95%"
          [ERROR ->][formControl]="ours"
        [dataSource]="dataSource"></search-select>
      </mat-form-field>
"): ng:///MyModule/MyDetailComponent.html@41:10

What do I wrong!? There is no compile error.

Kind regards,
Melroy van den Berg

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.