Tips for conducting a worldwide search in Angular 2?

I'm currently navigating my way through angular2 development and I am aiming to conduct a comprehensive search within an array of JSON objects. To illustrate, consider this sample array:

invoiceList = 
  [
    {
      invoiceNumber: 1234, 
      invoiceSupplier: "test", 
      invoiceStatus: "Import error", 
      invoiceCategory: "invoice with GR", 
      date: "22/01/2017", 
      amount : 134527
    },
    ...
  ];

I envision carrying out my search in the following manner:

The crux of the issue lies in:

  • I wish to only search based on certain values (e.g., status, supplier name, number...) while displaying OTHER fields (such as date, net amount etc..).
  • I aim to sort the final results based on specific criteria (e.g., number, supplier, date and amount). However, I find myself at a loss on how to actualize this in angular2.
  • And lastly, is there an equivalent of ng-show in angular2? Essentially, I desire to exhibit the table solely upon clicking the search button, and have it disappear when cancel is clicked.

In angular 1, accomplishing these tasks was simpler using filters, 'orderBy', among other constructs. Yet, with angular2, it seems less straightforward and has left me feeling quite bewildered. Could you please assist me in resolving these issues???

Here is the code snippet for my component :

  import { Component, OnInit } from '@angular/core';

  @Component({
    selector: 'app-search',
    templateUrl: './search.component.html'
  })
  export class SearchComponent implements OnInit {

  invoiceList = [{invoiceNumber:  1234, invoiceSupplier : "test", invoiceStatus : "Import error", invoiceCategory: "invoice with GR", date : "22/01/2017", amount : 134527}, 
  {invoiceNumber:  15672, invoiceSupplier : "test11", invoiceStatus : "Import error", invoiceCategory: "invoice without PO", date : "02/01/2017", amount : 134.3},
  {invoiceNumber:  99863, invoiceSupplier : "test22", invoiceStatus : "Other Document", invoiceCategory: "invoice with GR", date : "10/12/2016", amount : 189},
  {invoiceNumber:  24561, invoiceSupplier : "test50", invoiceStatus : "Other Document", invoiceCategory: "invoice without PO", date : "15/01/2017", amount : -134527},
  ];


    constructor() { }

    ngOnInit() {
    }

  }

and my html code :

  <form>
    <table>
      <tr>
        <td>Invoice Number :</td>
        <td>
          <input name="invoiceNumber">
        </td>

        <td>Invoice Supplier Name :</td>
        <td>
          <input name="invoiceSupplier">
        </td>
      </tr>

      <tr>
        <td>Invoice Status :</td>
        <td>
          <select name="invoiceStatus">

            <option></option>
            <option> Import error </option>
            <option> Invoice control required </option>
            <option>Rejected from SAP </option>
            <option>Other Document </option>
            <option> Invoice created manually in SAP </option>
            <option>To be integrated in SAP </option>
            <option> Integrated in SAP </option>
            <option> Booked in SAP </option>
            <option>Paid in SAP</option>
          </select>
        </td>

        <td>Invoice Category :</td>
        <td>

          <select name="invoiceCategory">
            <option></option>
            <option>Invoice with GR</option>
            <option>Invoice without GR</option>
            <option>Invoice without PO</option>

          </select>
        </td>
      </tr>

      <tr>
        <td>Order :</td>
        <td>
          <select name="order">

            <option> Number </option>
            <option> Supplier </option>
            <option> Date </option>
            <option> Net Amount </option>
          </select>
        </td>
      </tr>

      <tr>
        <td>
          <button type="submit">Search</button>
        </td>
        <td>
          <div class="detail">
            <button type="reset">Cancel</button>
          </div>
        </td>
      </tr>

    </table>
  </form>

Despite not having made significant progress yet, I am really finding my footing in angular2 to be challenging. Any guidance or assistance would be greatly appreciated!

Many thanks in advance!

Answer №1

Completed Successfully!! - View on PLUNKER

@Component({
  selector: 'app-search',
  templateUrl: 'src/search.component.html'
})
export class SearchComponent{

 invoiceList = [
  {invoiceNumber:  1234, invoiceSupplier : "test", invoiceStatus : "Import error", invoiceCategory: "invoice with GR", date : "22/01/2017", amount : 134527}, 
  {invoiceNumber:  15672, invoiceSupplier : "test11", invoiceStatus : "Import error", invoiceCategory: "invoice without PO", date : "02/01/2017", amount : 134.3},
  {invoiceNumber:  99863, invoiceSupplier : "test22", invoiceStatus : "Other Document", invoiceCategory: "invoice with GR", date : "10/12/2016", amount : 189},
  {invoiceNumber:  24561, invoiceSupplier : "test50", invoiceStatus : "Other Document", invoiceCategory: "invoice without PO", date : "15/01/2017", amount : -134527},
 ];

 invoiceCats = [];
 invoiceStatuses = [];
 invoiceNumbers = [];
 invoiceFields = Object.keys(this.invoiceList[0]);

 invoiceStatus = "";
 invoiceCategory = "";
 invoiceNumber;
 invoiceSupplier;
 order = this.invoiceFields[0];

 searchResults = [];

 constructor() { 
   this.invoiceList.forEach(i => {
     if(this.invoiceCats.indexOf(i.invoiceCategory) === -1) {
      this.invoiceCats.push(i.invoiceCategory);
     }

     if(this.invoiceStatuses.indexOf(i.invoiceStatus) === -1) {
      this.invoiceStatuses.push(i.invoiceStatus);
     }

     this.invoiceNumbers.push(i.invoiceNumber);
   })
 }

 ngOnInit() {
 }

 searchNow(e) {
   e.preventDefault();
   this.searchResults = this.invoiceList.filter(i => {
     return (this.invoiceStatus ? i.invoiceStatus === this.invoiceStatus : true) 
        &&  (this.invoiceNumber ? i.invoiceNumber === this.invoiceNumber : true)
        &&  (this.invoiceSupplier ? i.invoiceSupplier === this.invoiceSupplier : true)
        &&  (this.invoiceCategory ? i.invoiceCategory === this.invoiceCategory : true)
   }).sort((a, b) => {
      if(['invoiceNumber', 'amount'].indexOf(this.order) > -1) return a[this.order] - b[this.order];
      if(['invoiceSupplier', 'invoiceStatus', 'invoiceCategory'].indexOf(this.order) > -1) return (a[this.order] == [a[this.order], b[this.order]].sort()[1] ? 1 : -1);
      if(this.order === 'date') {
        const x = new Date(a.date.split('/').reverse().join('/'));
        const y = new Date(b.date.split('/').reverse().join('/'));
        return x.getTime() - y.getTime();
      }
   })
 }

}

Note: The code inside constructor is just for generating metadata.

<form (submit)="searchNow($event)">
    <table>
      <tr>
        <td>Invoice Number :</td>
        <td>
          <input name="invoiceNumber" [(ngModel)]="invoiceNumber" type="number">
        </td>

        <td>Invoice Supplier Name :</td>
        <td>
          <input name="invoiceSupplier" [(ngModel)]="invoiceSupplier" type="text">
        </td>
      </tr>

      <tr>
        <td>Invoice Status :</td>
        <td>
          <select name="invoiceStatus" [(ngModel)]="invoiceStatus">
            <option value="">Any</option>
            <option *ngFor="let iS of invoiceStatuses" [value]="iS">{{iS}}</option>
          </select>
        </td>

        <td>Invoice Category :</td>
        <td>

          <select name="invoiceCategory" [(ngModel)]="invoiceCategory">
            <option value="">Any</option>
            <option *ngFor="let iC of invoiceCats" [value]="iC">{{iC}}</option>
          </select>
        </td>
      </tr>

      <tr>
        <td>Order By:</td>
        <td>
          <select name="order" [(ngModel)]="order">
            <option *ngFor="let iF of invoiceFields" [value]="iF">{{iF}}</option>
          </select>
        </td>
      </tr>

      <tr>
        <td>
          <button type="submit">Search</button>
        </td>
        <td>
          <div class="detail">
            <button type="reset">Cancel</button>
          </div>
        </td>
      </tr>

    </table>
  </form>

  <div>
    <ul>
      <li *ngFor="let i of searchResults">Number: {{i.invoiceNumber}},  Supplier: {{i.invoiceSupplier}}, Date: {{i.date}}</li>
    </ul>
  </div>

Note: There are several ways to work with forms in Angular2, feel free to explore and utilize the one that suits your needs best.

Answer №2

Providing a foundational framework...

import { Component, OnInit } from '@angular/core';

export class Invoice {
    invoiceNumber: number;
    invoiceSupplier: string;
    invoiceStatus: string;
    invoiceCategory: string;
    date: string;
    amount: number
}

@Component({
    moduleId: module.id,
    selector: 'my-app',
    template: `
        <div></div>
    `,
    styleUrls: []
})

export class AppComponent implements OnInit {

    ngOnInit(): void {

        this.invoiceList = [ {invoiceNumber: 1231, invoiceSupplier: "test1", invoiceStatus: "Import error3", invoiceCategory: "invoice with GR1", date: "22/01/2017", amount : 134527 },
                             {invoiceNumber: 1232, invoiceSupplier: "test1", invoiceStatus: "Import error2", invoiceCategory: "invoice with GR2", date: "22/01/2017", amount : 134527 },
                             {invoiceNumber: 1233, invoiceSupplier: "test2", invoiceStatus: "Import error1", invoiceCategory: "invoice with GR1", date: "22/01/2017", amount : 134527 },
                             {invoiceNumber: 1234, invoiceSupplier: "test3", invoiceStatus: "Import error3", invoiceCategory: "invoice with GR3", date: "22/01/2017", amount : 134527 },
                          ];

        //this.invoiceFilter.invoiceNumber = 1234;
        //this.invoiceFilter.invoiceSupplier = "test2";
        this.invoiceFilter.invoiceCategory = "invoice with GR2";

        let filterdeInvoices = this.filterInvoices(this.invoiceList, this.invoiceFilter);
        console.log(filterdeInvoices);
        this.fieldToSortBy = "invoiceNumber";
        let sortedInvoices = this.sortInvoices(filterdeInvoices, this.fieldToSortBy);
        console.log(sortedInvoices);
    }

    invoiceFilter = new Invoice();
    fieldToSortBy: string;
    invoiceList: Invoice[];

    filterInvoices(invoiceList:Invoice[], invoiceFilter: Invoice): Invoice[]         {        
        return invoiceList.filter((invoice) => (invoiceFilter.invoiceNumber ? invoiceFilter.invoiceNumber === invoice.invoiceNumber : true) &&
                                               (invoiceFilter.invoiceSupplier ? invoiceFilter.invoiceSupplier === invoice.invoiceSupplier : true) &&
                                               (invoiceFilter.invoiceStatus ? invoiceFilter.invoiceStatus === invoice.invoiceStatus : true) &&
                                               (invoiceFilter.invoiceCategory ? invoiceFilter.invoiceCategory === invoice.invoiceCategory : true));
    }

    sortInvoices(invoiceList:Invoice[], fieldToSortBy: string): Invoice[] {
        return invoiceList.sort((inv1, inv2) => (inv1[fieldToSortBy] > inv2[fieldToSortBy] ? 1 : -1));
    }
}

Answer №3

If you want to efficiently filter data, I suggest utilizing a custom Pipe.

Define your custom Pipe as follows:

  1. Inspect incoming variables
  2. Filter out unwanted invoices
  3. Sort them if needed
@Pipe({
  name: 'filter'
})
export class FilterPipe implements PipeTransform {
  public transform(invoices: Invoice[], searchOptions: any): Invoice[] {
    if (!invoices || !invoices.length) return [];
    if (!searchOptions) return [];

    let filtered = invoices.filter(i => {
      return i &&
             (!searchOptions.number || i.invoiceNumber.toString().indexOf(searchOptions.number) >= 0) &&
             (!searchOptions.name || i.invoiceSupplier.toLowerCase().indexOf(searchOptions.name.toLowerCase()) >= 0) &&
             (!searchOptions.status || i.invoiceStatus === searchOptions.status) &&
             (!searchOptions.category || i.invoiceCategory === searchOptions.category);
    });

    let orderBy = searchOptions.orderBy;
    if (!orderBy) return filtered;

    /* kudos for sorting goes to: 'Ankit Singh' ! :) */
    return filtered.sort((a, b) => {

      switch (orderBy) {
        default: return 0;

        case 'invoiceNumber':
        case 'amount':
          return a[orderBy] - b[orderBy];
        case 'invoiceSupplier':
        case 'invoiceStatus':
        case 'invoiceCategory':
          return (a[orderBy] == [a[orderBy], b[orderBy]].sort()[1] ? 1 : -1);
        case 'date': {
          const x = new Date(a.date.split('/').reverse().join('/'));
          const y = new Date(b.date.split('/').reverse().join('/'));
          return x.getTime() - y.getTime();
        }
      }
   });
  }
}

To utilize this Pipe, follow this format:

<div class="row" *ngFor="let invoice of invoiceList | filter:searchOptions">
  <div>{{ invoice.invoiceNumber }}</div>
  <div>{{ invoice.invoiceSupplier }}</div>
  <div>{{ invoice.date }}</div>
  <div>{{ invoice.amount }}</div>
</div>

The searchOptions object is obtained from our form inputs:

<md-input-container>
  <input mdInput ngModel name="name" placeholder="Invoice supplier name">
</md-input-container>

<md-select placeholder="Invoice status" ngModel name="status">
  <md-option *ngFor="let status of statusArray" [value]="status.val">
    {{ status.name}}
  </md-option>
</md-select>

<md-select placeholder="Invoice category" ngModel name="category">
  <md-option *ngFor="let category of categoryArray" [value]="category.val">
    {{ category.name}}
  </md-option>
</md-select>

<md-select placeholder="order by" ngModel name="orderBy">
  <md-option *ngFor="let key of invoiceKeyArray" [value]="key.val">
    {{ key.name }}
  </md-option>
</md-select>

<button md-raised-button color="primary" (click)="searchOptions = f.value">search</button>
<button md-raised-button color="accent" (click)="resetForm(f);">reset</button>

Check out the live demonstration here: http://plnkr.co/edit/VPTnIOpieKt3YLqsEncr?p=preview

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Passing Selected Table Row Model Data to Backend in Angular 7

My goal is to send the selected data in a table row, which I select through a checkbox, to the server. However, I'm unsure about how to handle this via a service call. While I have the basic structure in place, I need assistance with sending the items ...

Sending a post request to an Express.js API from a different device

I currently have a server running on localhost:3000, with my app.js set up to utilize the angular router. When attempting to access localhost:3000 in my browser (for example: app.use('/', express.static(path.join(__dirname, '/dist/Client&apo ...

`Property cannot be redefined: __internal__deprecationWarning` detected in a Shopify Hydrogen development project

Recently, while working on my Shopify Hydrogen project using Remix and Typescript, I encountered a sudden error when running npm run dev. Everything was functioning perfectly just 5 hours ago, but after returning from dinner, the app refuses to launch. ╭ ...

Entering key-value pairs into a dictionary to show correlation

I've been struggling to find a solution for what seems like a simple issue. The problem lies in typing a dictionary with values of different types so that TypeScript can infer the type based on the key. Here is the scenario: type Id = string; inter ...

Error TS2694 is being caused by Electron Typescript Material-UI withStyles because the namespace "".../node_modules/csstype/index"" does not have an exported member called 'FontFace'

While I am experienced with Material-UI, I am relatively new to Electron and using React, TypeScript, and Material-UI together. Recently, I encountered an error while attempting to create an electron boilerplate code for future project initialization. Init ...

What is the method in AngularJS2 for using TypeScript to inject dependencies into components?

I have been encountering different methods of injecting dependencies into my component and not all of them seem to be working for me. I am curious about the advantages and disadvantages, what the recommended best practices are, and why some methods are not ...

Establishing the testing sequence for Angular 8

I've been encountering a frustrating issue where one of my tests fails at random intervals. To add some order to the debugging process, I attempted to set a seed number in the 'karma.conf.js' file and also tried setting 'random: false&a ...

All authentication logic in Angular encapsulated within the service

I am considering moving all the business logic into the auth service and simply calling the method on the component side. Since none of my functions return anything, I wonder if it's okay or if they will hang. COMPONENT credentials: Credentials = ...

Alerting Users Before Navigating Away from an Angular Page

I am looking to implement a feature in my app that will display a warning message when attempting to close the tab, exit the page, or reload it. However, I am facing an issue where the warning message is displayed but the page still exits before I can resp ...

Develop a new flow by generating string literals as types from existing types

I'm running into a situation where my code snippet works perfectly in TS, but encounters errors in flow. Is there a workaround to make it function correctly in flow as well? type field = 'me' | 'you' type fieldWithKeyword = `${fiel ...

Filtering observables to handle routing in Angular Material Autocomplete is a must-have skill to master

I am currently working with Angular Material version "^6.4.7" and Angular version "^6.1.9". I have set up routing to navigate through a menu filled with observables, using the id of these observables for navigation. Now, I need to implement an autocomplete ...

The type 'Observable<void | AuthError>' cannot be assigned to 'Observable<Action>'

I am encountering an error that reads: error TS2322: Type 'Observable<void | AuthError>' is not assignable to type 'Observable<Action>'. Type 'void | AuthError' is not assignable to type 'Action'. Type &a ...

Encountering issues with HttpClient POST function in Angular 6 after transitioning from AngularJS

I am currently in the process of upgrading my application from Angular 1.6 to Angular 6, and I seem to be encountering an issue on the login screen. Below is a snippet of the code I am struggling with - const body = new URLSearchParams(); body.set(' ...

Tips for customizing the event target appearance in Angular 2?

After following the steps outlined in this particular blog post (section 3, event binding), I successfully added an event listener to my component class. I can confirm that it responds when the mouse enters and exits. <p class="title" (mouseenter)="unf ...

Why styled-components namespace problem in React Rollup build?

I designed a "UI Library" to be utilized in various projects using ReactJS + TypeScript + styled-components and Rollup. However, I am currently encountering issues with conflicting classNames. I am aware that styled-components offers a plugin for creating ...

A guide on implementing directives in Angular 2

I am trying to load my navbar.html in my app.component.html by using directives and following the method below: Here is my navbar html: <p>Hi, I am a pen</p> This is my navbar.ts: import {Component, Directive, OnInit} from '@angular/c ...

Unable to assign to 'disabled' as it is not recognized as a valid attribute for 'app-button'

How to link the disabled property with my button component? I attempted to add 'disabled' to the HTML file where it should be recognized as an input in the button component (similar to how color and font color are recognized as inputs) ... but ...

When the form is submitted, the HTMLCollection becomes void

I am facing an issue with a form that always seems to be invalid. To troubleshoot, I tried running this command in my web browser to identify the invalid fields: document.getElementsByClassName('ng-invalid'); However, the HTMLCollection returned ...

Typescript library available as a private npm dependency

I have developed a Typescript library that I bundle as an npm module. During the development of my frontend application, I easily integrated this library using yarn link. As I set up GitLab CI for other developers to work on the frontend application, I am ...

The Dynamic Duo: Typescript and Knex

I'm new to TypeScript and currently working on a node application using Express, Postgresql, and Knex. I'm a bit confused about when to apply type definitions in my code. Looking at other projects for guidance has left me even more puzzled as to ...