1

I have a html table containing input tags and I want a mechanism to allow me to insert the same value into multiple table cells at the same time without having to manually type it in every cell.

for example a table like this :

id | attr1 | attr2 | attr3
1     "x"    "x"      "x"
2     "x"    "z"      "y"
3     "a"    "w"      "x"

x is the value i want to insert into those specific cells, i don't want to manually type it.

EDIT : this is how my table looks like :

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    
  <!--- Note that these columns can be defined in any order.
            The actual rendered columns are set as a property on the row definition" -->
    
  <!-- Position Column -->
  <ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef> No. </th>
    <td mat-cell *matCellDef="let element"> <input type"text" value="{{element.position}}"></td>
  </ng-container>
    
  <!-- Name Column -->
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let element"> <input type"text" value="{{element.name}}"> </td>
  </ng-container>
    
  <!-- Weight Column -->
  <ng-container matColumnDef="weight">
    <th mat-header-cell *matHeaderCellDef> Weight </th>
    <td mat-cell *matCellDef="let element"> <input type"text" value="{{element.weight}}"> </td>
  </ng-container>
    
  <!-- Symbol Column -->
  <ng-container matColumnDef="symbol">
    <th mat-header-cell *matHeaderCellDef> Symbol </th>
    <td mat-cell *matCellDef="let element"> <input type"text" value="{{element.symbol}}"> </td>
  </ng-container>
    
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}
    
const ELEMENT_DATA: PeriodicElement[] = [
  {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
  {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
  {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
  {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
  {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
  {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
  {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
  {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
  {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
  {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
];
    
/**
 * @title Basic use of `<table mat-table>`
 */
@Component({
  selector: 'table-basic-example',
  styleUrls: ['table-basic-example.css'],
  templateUrl: 'table-basic-example.html',
})
export class TableBasicExample {
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = ELEMENT_DATA;
}
7
  • Do you want to convert a text to an html table in a code editor? Commented May 15, 2022 at 12:22
  • @Shahriar i have a table containing input tags in its table cells. instead of typing the values manually for every input, i want a mechanism to select multiple inputs ( cells ) and type the text and that text is shared among all the selected cells. It's the exact mechanism present in Microsoft EXCEL that i want to achieve Commented May 15, 2022 at 12:25
  • Got it, so please add the HTML code and provide what you've tried so far. Commented May 15, 2022 at 12:28
  • This helps solving your problem: stackoverflow.com/questions/42950192/… Commented May 15, 2022 at 12:31
  • Some help to get you started: First you want to create an array. Then use 'fromEvent()' to listen for holding a key, you might want it to clear the array on keydown. Then you want a second observable, taking the cells that are clicked. Then you combine those observables and store something that identifies the cells you selected in the array. You can render some css using ngClass if a cell is in the array. And then you want each input to have a onchanges event bound to a function that also sets all the other inputs that have their identifier in the array. I'll gladly help if you get stuck. Commented May 15, 2022 at 12:39

1 Answer 1

3

Step by Step Guide

I hope you managed to find out some things on your own by now, but I decided I wanted to give it a try too. So here is a step by step guide for you on how I managed to get there.

Step 1

First create an array to hold the selected fields.

selected: string[] = [];

Step 2

Then use 'fromEvent()' to listen for holding a key, you might want it to clear the array on keydown. In this case you might want to listen to both keyup and keydown events.

keyDowns$ = fromEvent(document, 'keydown');
keyUps$ = fromEvent(document, 'keyup');

ctrlPresses$ = merge(this.keyDowns$, this.keyUps$).pipe(
  filter((e: KeyboardEvent) => e.keyCode === CTRL), // <-- Keycode for CTRL = 17
  debounceTime(100),
  tap((x: KeyboardEvent) => {
    if (x.type === 'keydown') {
      this.selected = [];
      this.ctrlPressed = true;
      console.log('CTRL is down');
    }
  }),
  tap((x: KeyboardEvent) => {
    if (x.type === 'keyup') {
      this.ctrlPressed = false;
      console.log('CTRL is up');
    }
  })
);

Step 3

Then create a second observable, taking the cells that are clicked. Now combine those observables and store something that identifies the cells you selected in the array. To keep it simple I just flipped a boolean in the first observable, but you actually want to avoid side-effects like that using higher-order observables. The second observable we get from pushing click events to our own subject.

selectionSubject = new Subject();

get selection$() {
  return this.selectionSubject.asObservable().pipe(
    tap((x: string) => {
      if (this.ctrlPressed && this.selected.indexOf(x) === -1) {
        this.selected.push(x);
      }
    })
  );
}

onClicked(event: MouseEvent) {
  const target = event.currentTarget as HTMLInputElement;
  this.selectionSubject.next(target.id);
}

Step 4

Render some css using ngClass if a cell is in the array.

isSelected(id: string) {
  if (this.selected.indexOf(id) !== -1) {
    return 'selected';
  }
}

Add some basic styling:

input {
  margin: 2px;

  &:focus {
    outline: none;
  }
}

.selected {
  border: none;
  box-shadow: 0 0 3pt 2pt cornflowerblue;
}

Step 5

Bind a change event for each input to a function that also sets all the other inputs that have their identifier in the array. Binding the keyup event worked best for me.

onChange(e) {
  this.selected.forEach((id) => {
    this[id].nativeElement.value = e;
  });
}

Finally

The inputs in html should look something like this:

<input
  #tf1
  [ngClass]="isSelected('tf1')"
  id="tf1"
  (click)="onClicked($event)"
  (keyup)="onChange($event.target['value'])"
  type="text"
/>

Here is the example code in a Stackblitz.

The code can probably still be optimized a little. I also added clearing of the selection on mouse clicks, and some styling magic to be able to have multiple (fake) carets in the Stackblitz project. It clutters the basic logic a little, so I didn't include it here.

Sign up to request clarification or add additional context in comments.

4 Comments

man I just can't seem to make it work and i'm starting to panick because I need to have this working soon... your code is triggering a typpescript error and my table is way more complex than that and now I'm panicking
Always stay calm, there's always a way to please typescript. What does the error say? Or even better can you reproduce it in a simple project. I'll have a look at it.
I also made the carets blink in sync on the Stackblitz btw, that was bothering me. XD If you can manage to get me some code, I can look into it right away if you want, I'm done working for today.
thank you so much for your help, i managed to make it work after a very slow, step by step debugging and I couldn't have done it without your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.