Sarcastic Text Generator pt. 2 - Modifying the Text

sarcastic-text-generator Jan 24, 2021

This post is a continuation of the code written in Sarcastic Text Generator pt. 1

So, where we last left off we had a basic app done, but it wasn't terribly exciting, all it could do was copy whatever text we typed. Lets now go and add some more functionality.

Building the Rest of the App

Transforming the Text

We've got a box to type into, and we can see the text copied into the output field as we type it, so far so good. Now lets get to the meat of the problem, transforming that output text. To do this, we'll use a pipe. Pipes let you transform data that is displayed, for example changing how a date is formatted.

We're not going to do anything near as useful as that, however, all we want is to alternate the capitalisation of the text a user types in. To get started, lets make our pipe class.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sarcasm'
})
export class SarcasmPipe implements PipeTransform {

  transform(value: string): string {
    return string;
  }

}
sarcasm.pipe.ts

The code snippet shows what's necessary for making a pipe, namely the @Pipe annotation, and the use of the PipeTransform interface. The transform function is where we'll make the magic happen. When the pipe is inv0ked, we'll manipulate the variable value and return a modified string. To use the pipe, we need to reference it in the core app file, and invoke it on the data we want to be transformed in the HTML file.

import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { SarcasticTextComponent } from './sarcastic-text/sarcastic-text.component';
import { SarcasmPipe } from './sarcasm.pipe';


@NgModule({
  declarations: [
    AppComponent,
    SarcasticTextComponent,
    SarcasmPipe
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
Adding a reference to the sarcasm pipe to the core app file

Using the pipe is quite straightforward, we just pipe (I see what you did there, Angular developers) our bound data into the pipe, using the name declared in the pipe annotation.

<h2>Sarcastic Text Generator</h2>
<div>
  <label>Input:
    <input [(ngModel)]="sarcastic_text.inputText" placeholder="name"/>
  </label>
  <label>Output: {{sarcastic_text.inputText | sarcasm}}
  </label>
</div> 
Transforming text using the pipe

The HTML is still nicely readable using this pipe, and it's a good argument for naming pipes appropriately so you know what they do when referencing them.

Let's now go back to our sarcasm.pipe.ts file, and add some logic. This step is more of a basic algorithm problem than it really is an Angular issue. We have a string, and we want to make sure that every alternate character in that string is upper case, and all other characters are lower case. There's a few ways of going about doing this, and it's a fun exercise to try to find the most compact way the problem can be solved.

After a bit of back and forth, I arrived at the algorithm shown in the following file. Is it the greatest, most compact way to do what we want? Possibly not, but it's pretty darn good all the same.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sarcasm'
})
export class SarcasmPipe implements PipeTransform {

  // Takes a string as input, returns the string with alternate capitalizations
  // Example input: This is a string
  // Example output: tHiS iS a StRiNg
  transform(value: string): string {

    //Strings are immutable, a lot of new String objects are going to be made in this function
    if(value == null || value.length == 0){
      return value
    }

    //Make two variations of input, which the code will pull from when it needs a lower/uppercase character
    var lowercase = value.toLowerCase();
    var uppercase = value.toUpperCase();


    // where i is even, take from lowercase, where i is odd, take from uppercase string
    var outputString = "";
    for (var i = 0; i < value.length; i++){
      if(i % 2 == 0){ //Hello modulus operator, my old friend
        //even, lowercase
        outputString += lowercase.charAt(i);
      } else{
        //odd, uppercase
        outputString += uppercase.charAt(i);
      }
    }

    return outputString;
  }

}
Sarcasm pipe implementation

The code makes two versions of the input, one lowercase, one uppercase, and then builds a new string, alternating pulling a character from the lower or upper-case string.

Once this is implemented, we should see that whatever text we type into the input box gets output with alternating capitalisation, nice! So we're done, right?

Image of sarcastic text generator taking user input and outputting the same text with alternate capitalization
Didn't think I would ever write code that could hurt my feelings....

Prettifying the Whole Thing

Of course we're not done! While our app now does what we set out to do, it could look a bit, well, nicer. Might as well use this opportunity to get our hands dirty with Material, design components for Angular which lets you make things lot a bit more professional.

We'll add Material to our app by running the following command, which will add all the dependencies and magical "this is a great framework but I have no clue how it really works" stuff.

ng add @angular/material

There'll be a few questions asked in the CLI, and we can go with the default answers to them all.

So how do we use this fancy new Material stuff? By importing what we need into the app.module.ts file (remember this is the core app file), and using different tags in our HTML file. There was a bit of trial and error to get this all fully functional, and it required importing the specific components we want to use.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { SarcasticTextComponent } from './sarcastic-text/sarcastic-text.component';
import { FormsModule } from '@angular/forms';
import { SarcasmPipe } from './sarcasm.pipe';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';


@NgModule({
  declarations: [
    AppComponent,
    SarcasticTextComponent,
    SarcasmPipe
  ],
  imports: [
    BrowserModule,
    FormsModule,
    BrowserAnimationsModule,
    MatFormFieldModule,
    MatInputModule
  ],
  providers: [
    { provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'fill' } },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
app.module.ts with necessary Material dependencies added

Let's now completely re-do the beautiful HTML we've written to make use of the shiny Material components. While we're at it, lets add some placeholder text, and some labels to the forms, make the UI a bit more self-explanatory.

<head></head>
<body class="mat-app-background ">
<h2>Sarcastic Text Generator</h2>
<form class="form">
  <mat-form-field class="full-width">
    <mat-label>Type the text you want sarcasm-ified</mat-label>
    <textarea matInput placeholder="This is a good implementation" [(ngModel)]="sarcastic_text.inputText" name="inputfield"></textarea>
  </mat-form-field>

  <mat-form-field class="full-width">
    <mat-label>Your sarcasm-ified text</mat-label>
    <textarea matInput value="{{sarcastic_text.inputText | sarcasm}}" readonly="true"></textarea>
  </mat-form-field>
</form>
</body>
HTML file using material
The app using Material design components
The app using Material

Looking good! One thing to note is that Material lets us choose from a wide range of pre-defined CSS styles, so there's lots of options to simply change the highlighting colours used.

Copy to Clipboard

Are we done? Almost! Lets push the boat out a bit and add a "Copy to Clipboard" button, because once our text is transformed, we'll want an easy way to send it to others in chat messages. This is where we'll see another advantage of using material, because the button is going to look nice and shiny without us having to do much. In fact, there's a wide range of pre-defined button styles to choose from, if you don't want to have to think about stylings at all. To use Material buttons, and clipboard functionality, we'll add two more imports to the core app file.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { SarcasticTextComponent } from './sarcastic-text/sarcastic-text.component';
import { FormsModule } from '@angular/forms';
import { SarcasmPipe } from './sarcasm.pipe';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
import { MatInputModule} from '@angular/material/input';
import {MatButtonModule} from '@angular/material/button';
import { ClipboardModule } from '@angular/cdk/clipboard';

@NgModule({
  declarations: [
    AppComponent,
    SarcasticTextComponent,
    SarcasmPipe
  ],
  imports: [
    BrowserModule,
    FormsModule,
    BrowserAnimationsModule,
    MatFormFieldModule,
    MatInputModule,
    ClipboardModule,
    MatButtonModule 
  ],
  providers: [
    { provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'fill' } },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
app.module.ts with button and clipboard imports

To add the button to the HTML, we'll simply add this snippet inside the form tags.

<button mat-raised-button color="primary" [cdkCopyToClipboard]="sarcastic_text.inputText | sarcasm">Copy to clipboard</button>
HTML for Copy to Clipboard button

Ok, so what about the actual, you know, code needed for copying the text to the clipboard? It's already done! We're using a pre-made material component, so we don't even have to pretend to know it works. Hooray for frameworks!

Image of the completed app, with input box, output box, and copy to clipboard button
The completed app

So that's the tool done! We can make every single message we send to others sarcastic, forever.  Now all we need to do is deploy it, which we'll cover in part 3.

Tags