lukazohi
lukazohi

Reputation: 53

Angular: Bind text input value to substring inside textarea

Let's say I have a textarea, inside which are some substrings that match pattern. Is there a way I can edit that substring through text input.

Example: I have a textArea that contains next string:

${1:scan1}
${2:scan2}
${3:scan3}

I am subscribing to valueChanges of it and calling regex to get all repetitions of that string and converting them to FormControl, than pushing it to FormArray, so value pushed through FormControl is only text inside ( ${scan1} -> scan1 )

<textarea cols="30" rows="10" [formControl]="textArea"></textarea>
export class AppComponent implements OnInit {
  textArea: FormControl = new FormControl("");
  detectedStrings: FormArray = new FormArray([]);

  constructor() {}

  ngOnInit() {
    this.textArea.valueChanges.subscribe((textBodyText: string) => {
      const foundStrings =
        textBodyText.match(/[$][{][1-9][0-9]*[:][a-zA-Z0-9 ]{0,}[}]/g) || [];
      this.detectedStrings.clear();
      foundStrings.map(_string => {
        this.detectedStrings.push(
          new FormControl(
            _string.substring(_string.indexOf(":") + 1, _string.indexOf("}"))
          )
        );
      });
    });
  }
}

Then I'm displaying all found scans in list as input:

<div [formGroup]="detectedStrings">
  <div *ngFor="let string of detectedStrings.controls; let i = index">
    <input type="text" [formControlName]="i" />
  </div>
</div>

Now what I want is that as I would edit text in input, text in textarea would also change dynamically. ( scan1 -> scan100 )

Note: This is just a recreation

Upvotes: 1

Views: 1715

Answers (2)

Plochie
Plochie

Reputation: 4117

I have followed following steps,

  1. Find all matched tags with their respective indices.
  2. Create control inputs.
  3. After keyup event, get the respective start index of tag from step 1
  4. Replace latest value from FormArray control to existing value of TextArea
  5. Update TextArea.

Demo Stackblitz

Typescript

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "Angular";

  textArea: FormControl = new FormControl("");

  formArray: FormArray;

  tags: any;

  // array to hold prev changes, which is used to avoid stack size exceeded error
  // as this is cyclic behaviour
  formArrayChanges: string[] = [];

  constructor() {
    this.formArray = new FormArray([]);
  }

  ngOnInit() {
    this.textArea.valueChanges.subscribe((textBodyText: string) => {
      const regex = /[$][{][1-9][0-9]*[:][a-zA-Z0-9 ]{0,}[}]/g;
      const foundStrings = textBodyText.match(regex) || [];

      this.tags = new Object();
      let match;

      while ((match = regex.exec(textBodyText)) != null) {
        this.tags[match.index] = match[0];
      }
      // clear saved array
      this.formArray.clear();

      foundStrings.map(str => {
        // get id and value of control
        let id = str.substring(2, str.indexOf(":"));
        let value = str.substring(str.indexOf(":") + 1, str.indexOf("}"));

        this.formArray.push(new FormControl(value));
      });
    });

    this.textArea.setValue("${1:scan1}\n${1:scan1}\n${2:scan2}\n${3:scan3}");
  }

  onControlKey(index: number) {

    let currTextAreaValue = this.textArea.value;

    const tagStartIndex = Object.keys(this.tags)[index];
    console.log(tagStartIndex, this.tags[tagStartIndex]);

    const valueStartIndex = currTextAreaValue.indexOf(":", tagStartIndex) + 1; // +1 to remove :
    let valueEndIndex = currTextAreaValue.indexOf("}", valueStartIndex);

    currTextAreaValue =
          currTextAreaValue.substring(0, valueStartIndex) +
          this.formArray.at(index).value +
          currTextAreaValue.substring(valueEndIndex);

    this.textArea.setValue(currTextAreaValue);
  }
}

Template

<textarea cols="30" rows="10" [formControl]="textArea"></textarea>

<hr>

<div [formGroup]="formArray">
  <div *ngFor="let string of formArray.controls; let i = index">
    <input type="text" [formControlName]="i" (keyup)="onControlKey(i)"/>
  </div>
</div>

Upvotes: 1

jornathan
jornathan

Reputation: 856

I hope my code helps to you.

Step 1. Add event(keyup) to your template.

<textarea cols="30" rows="10" [formControl]="textArea"></textarea>
<div [formGroup]="detectedStrings">
    <div *ngFor="let string of detectedStrings.controls; let i = index">
        <input #box type="text" [formControlName]="i" (keyup)="onKey($event, i)" />
  </div>
</div>

Step 2. Handle event and update textarea.

onKey(event: any, index: any) { 
    console.log(index + '  changed: ' + event.target.value);    
    this.textArea.setValue('${1:scan1}\n${2:scan2}\n${3:scan33}'); //This is one of sample.
}

I think it is hard part to update original text at textArea.
But you may know what is requirement, so you can make.

Here is my sample code.

Upvotes: 0

Related Questions