Search code examples
angularstringtypescriptconcatenation

Replace concat part of a string in typescript


I have a variable string, because it is inside a textarea. A user can write anything. After that there's a date picker. When I select the date, the value will be written in the textarea after the first part of the string. I have this function on change of the picker

concatValue(){
    let dataMsg = "";
    let date: string = moment(dateTime).format('D MMM YYYY, h:mm');
    dataMsg = ". " + date;
    this.message += dataMsg;
  }

The problem here, is that if I change date, the string keep concat and don't reset the before value. But I can't reset the entire this.message because it include other text. How can I do something like this? For example. First time

"Hello there at Thursday 15 October 2020, 19:00"

then i wanna change date:

"Hello there at Friday 16 October 2020, 15:00"

That's what I need instead of

"Hello there at Thursday 15 October 2020, 19:00"
"Hello there at Thursday 15 October 2020, 19:00 Friday 16 October 2020, 15:00"

Solution

  • I'm going to make my own minimal reproducible example so if you need to tweak it to apply to your use case, hopefully you can. Imagine I have this Foo class that takes a message and concats dates or times or something onto it:

    class Foo {
        message: string;
        constructor(message: string) {
            this.message = message;
        }
        concatDate() {
            this.message += " @ " + new Date().toLocaleTimeString();
        }
    }
    
    let f = new Foo("hello there");
    console.log(f.message); // "hello there"
    f.concatDate();
    console.log(f.message); // "hello there @ 12:56:10 PM"
    await new Promise(resolve => setTimeout(resolve, 2000));
    f.concatDate();
    console.log(f.message); // "hello there @ 12:56:10 PM @ 12:56:12 PM"
    

    Oops, every time I call concatDate() it adds to the end of message. How can I fix it? Well, one idea is that you can try to look at message and strip off any date string if one is there. Like this:

    class BadFixFoo {
        message: string;
        constructor(message: string) {
            this.message = message;
        }
        concatDate() {
            this.message = this.message.replace(/ @[^@]*$/, "") + 
              " @ " + new Date().toLocaleTimeString();
        }
    }
    

    It kind of works:

    f = new BadFixFoo("hello there");
    console.log(f.message); // "hello there"
    f.concatDate();
    console.log(f.message); // "hello there @ 12:56:12 PM"
    await new Promise(resolve => setTimeout(resolve, 2000));
    f.concatDate();
    console.log(f.message); // "hello there @ 12:56:14 PM"
    

    Until it doesn't work:

    f = new BadFixFoo("what if I use an @-sign in the message");
    console.log(f.message); // "what if I use an @-sign in the message"
    f.concatDate();
    console.log(f.message); // "what if I use an @ 12:56:14 PM"
    await new Promise(resolve => setTimeout(resolve, 2000));
    f.concatDate();
    console.log(f.message); // "what if I use an @ 12:56:16 PM"
    

    See, the method I used to strip off the date just looked for the last @ sign (after a space) in the message and removed it and everything after it. But if the original message has an @ sign in it, then the stripping will mess it up. Oops. Maybe we can write an even more clever way of identifying a date string, but if the user can truly write anything for the original message, there's nothing we can do to stop them from writing an actual date string. Do we want to strip that off?


    If not, you need to refactor so that you're not trying to forensically determine what the original message was. Instead, store it:

    class GoodFoo {
        originalMessage: string;
        message: string;
        constructor(message: string) {
            this.originalMessage = message;
            this.message = message;
        }
        concatDate() {
            this.message = this.originalMessage + " @ " + new Date().toLocaleTimeString();
        }
    }
    

    This means that concatDate() never tries to modify the current message. Instead, it copies the originalMessage and appends to that:

    f = new GoodFoo("what if I use an @-sign in the message");
    console.log(f.message); // "what if I use an @-sign in the message"
    f.concatDate();
    console.log(f.message); // "what if I use an @-sign in the message @ 12:56:16 PM"
    await new Promise(resolve => setTimeout(resolve, 2000));
    f.concatDate();
    console.log(f.message); // "what if I use an @-sign in the mssage @ 12:56:18 PM"
    

    Playground link to code