Your First Component

In the last section, I showed you how to use some command line tools to set up a React application and an Angular application. Both tools scaffolded an application for you, including a first component. Now, we will add some more functionality to this first component.

The first component can be found in a single file, src/App.tsx. Edit this file to add the "hello world" functionality. Add new TypeScript functions to add more compoments.

The first angular component contains of two files: src/app/app.component.ts and app.component.html in the same directory. Edit both files to add the "hello world" functionality. Use the Angular CLI to add more components.

The Scaffolded Component

Both your React application and your Angular application show some sort of a "welcome screen" when you start them: They confirm that the application was created correctly and is up-and-running. In both cases, this welcome screen is implemented by the only component that the command line tool created.

The Main Component of the React App

In the most simple case, a react component is just a function that returns a ReactElement. And that's exactly what the command line tool created for you: A function that returns... something that looks like HTML, not like TypeScript code at all?!

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      ...
    </div>
  );
}

export default App;

This syntax inside the return statement in src/App.tsx, that looks like HTML, is actually JSX. JSX is a syntax extension to JavaScript, and even though it looks like a template language, it is actually closer to source code: The JSX code in your components will be compiled down to React.createElement() calls.

Let's do a first, quick edit of this component. Remove the inner JSX code and replace it with a single headline. You can also remove the logo.

import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>Hello World</h1>
      </header>
    </div>
  );
}

export default App;

If your application was already running in the development server, your browser should immediately refresh and now show:

Screenshot of the running react application

The Main Component of the Angular App

An Angular component contains of at least a TypeScript class. But oftentimes, you will create more files that together make up the component. The main component in the scaffolded application consists of a template (/src/app/app.component.html) and the component class (/src/app/app.component.ts).

The class AppComponent is an Angular component because it has the @Component TypeScript decorator.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'mood-ng';
}

The component class itself does not yet contain any meaningful amount of code, but in its decorator, it references the HTML template and a CSS file that contains the component styles. In the @Component decorator, the code also defines the selector app-root. This means that other components can use <app-root></app-root> in their HTML template to reference this component.

The HTML template of the component (/src/app/app.component.html) contains a lot of code. Let's remove all that code and add a single headline:

<h1>Hello World</h1>

If you already had your application running in the development server, your browser will refresh immediately and show:

Screenshot of the running angular application

Writing a New Component

You didn't just come here to change an existing component so it displays some static text, did you? Let's take a look at how to write your own component:

Creating a New React Component

Create a new file, src/Hello.tsx, and add a function that returns a React element:

import React from 'react';

export function Hello() {
  return <h1>Hello World</h1>
}

Now you can use this function as a React component inside src/App.tsx. To do that, you must do two things:

  • Import the function in your TypeScript file (import { Hello } from './Hello';)
  • Use the function name in your JSX like an HTML tag (<Hello />)
import React from 'react';
import { Hello } from './Hello';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Hello />
      </header>
    </div>
  );
}

export default App;

What is displayed in your browser remains unchanged since you only moved the headline to a new component. Let's add some extra functionality: We want the component to accept a default greeting as a parameter so that we can write in src/App.tsx:

function App() {
  return (
    ...
        <Hello defaultGreeting="React" />
    ...
  );
}

To be able to do that, you must give the Hello function a parameter called props, which is short for "properties" (I usually don't like using abbreviations in my code, but in React, props is so common that I use it too). The props parameter is an object, and every field of that object can be passed to the component from JSX, like the defaultGreeting above.

type HelloProps = {
  defaultGreeting: string,
}

export function Hello(props: HelloProps) {
  return <h1>Hello {props.defaultGreeting}</h1>
}

Because you are using TypeScript, you should create a type for your props. And since defaultGreeting is mandatory in that type (and because TypeScript is configured to use strict mode), you would also get a type error if you did not pass the defaultGreeting parameter in src/App.tsx.

In the JSX code, you can add arbitrary JavaScript (or TypeScript, in our case) between curly brackets, and the result will appear in the created components. So, when you now look at the rendered result in your browser, it will say Hello React instead of Hello World.

You can even simplify the code of src/Hello.tsx a little bit more: You can destructure the props argument instead of using the object directly.

export function Hello({ defaultGreeting }: HelloProps) {
  return <h1>Hello {defaultGreeting}</h1>
}

Creating a New Angular Component

To create a new component in Angular, use the Angular CLI:

npm run ng generate component hello

The output of the command tells you exactly what it did under the hood: It created the four files that belong to the component (the template HTML file, the component class, a CSS file and a test) and it registered the component in the application module src/app/app.module.ts:

...

@NgModule({
  declarations: [
    AppComponent,
    HelloComponent
  ],
  ...
})
export class AppModule { }

You can now move the headline that says "Hello World" from the template of your application component to src/app/hello/hello.component.html. And in your src/app/app.component.html, you can use the selector of the new component instead:

<app-hello></app-hello>

Your browser should refresh automatically, but the application looks exactly like before: You just moved the template code to a new component. Let's add an input parameter too so that in the application component, you can write this again:

<app-hello defaultGreeting="Angular"></app-hello>

To achieve that, add a field to your component class in src/app/hello/hello.component.ts and decorate it with @Input():

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

@Component({
  selector: 'app-hello',
  templateUrl: './hello.component.html',
  styleUrls: ['./hello.component.css']
})
export class HelloComponent implements OnInit {
  @Input() defaultGreeting: string = 'World';

  constructor() { }

  ngOnInit(): void {
  }
}

Then you can access the field value in your template. This looks somewhat similar to the React version—you just use double curly braces instead of single—but under the hood, it works differently.


<h1>Hello {{defaultGreeting}}</h1>

After you save all files, your browser refreshes, and the headline now says Hello Angular instead of Hello World.

Processing User Input

As a last step in creating the first component, I now want to add some interactivity. I want you to add a text field to set the greeting that the component shows. That text field should show the default greeting at first. And whenever the user edits the field, the headline showing the greeting should change.

Processing Input in React

You can add an input field directly to the JSX code. But a React component must return a single element, so you need to group the input and the headline. You could use a generic HTML container element, like a div. But in this case, it is better to use <>...</>: This will create a grouping element that will not show up in the final HTML output.

export function Hello({ defaultGreeting }: HelloProps) {
  return <>
    <input type="text" value={defaultGreeting} />
    <h1>Hello {defaultGreeting}</h1>
  </>
}

Your browser refreshes, and you see that the app now shows an input field. But when you try to type into that field, nothing happens. This is by design. React will always render the input field with the value you provided in the code of the component, and in your case, that's the value of defaultGreeting.

What you are experiencing here is an example of React's one-way databinding. It binds a value from the component's state or props to the view works automatically, but then, the view will always render that value. To get some changed value back into the model, you will have to implement some code.

When you open the browser's development tools, you will see that React even warns you about that "problem" (that isn't really a problem after all):

Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange`
handler. This will render a read-only field. If the field should be mutable use `defaultValue`. 
Otherwise, set either `onChange` or `readOnly`.

So, you need to supply an onChange handler and do something with the changed value in order to make this warning go away and to make your application work. Let's store the changed value in some internal state (that we create with useState) and then also use that internal state to render the headline.

import React, { useState, } from 'react';

...

export function Hello({ defaultGreeting }: HelloProps) {
  const [greeting, setGreeting] = useState(defaultGreeting);
  return <>
    <input type="text" value={greeting} onChange={e => setGreeting(e.target.value)} />
    <h1>Hello {greeting}</h1>
  </>
}

So, on every change of the input field, the change handler passes the new value to setGreeting. Doing this will set the internal greeting state and cause a re-render of the component. Wait for your browser to refresh, and then you will see an interactive application where you can type in a new greeting, and the headline will always show that greeting.

Screenshot of the interactive react application, accepting user input

Processing Input in Angular

In Angular, you can add the input to the HTML template src/app/hello/hello.component.html too. And in this case, you do not even need a grouping element. But the Angular application should also use some internal state for the greeting.

<input type="text" [(ngModel)]="greeting" />


<h1>Hello {{greeting}}</h1>

We bind the internal greeting state to the ngModel of the input field using [()], which is basically two-way binding (but with a twist, so I will write more about bindings in one of the future sections). To remember the order of the parentheses, think [()] = "banana in a box". Create the internal state in the component code of src/app/hello/hello.component.ts:

export class HelloComponent implements OnInit {
  @Input() defaultGreeting: string = 'World';
  greeting: string='';

  ...
}

When you save both files... It doesn't work. The angular compiler will tell you:

Can't bind to 'ngModel' since it isn't a known property of 'input'.

What you created here is a template-driven form—which is one of the two ways to create forms in Angular. And to make template-driven forms work, you must register the FormsModule in src/app/app.module.ts:

import { FormsModule } from '@angular/forms';

...

@NgModule({
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
  ],
  ...
})
export class AppModule { }

One more thing is missing to make the application behave like it should: You must set the greeting to be the default greeting when the component is being initialized.

export class HelloComponent implements OnInit {
  ...
  ngOnInit(): void {
    this.greeting = this.defaultGreeting
  }
}

Wait for your browser to refresh (which might take a few seconds), and you will see the working, interactive application:

Screenshot of the interactive angular application, accepting user input

To Recap...

To create a new component in React, you create a function that returns a React element. This function can accept props and track some internal state via useState. You should usually create a new .tsx file for each component—but sometimes, components that are "internal" to other components (i.e., live in the same file) also make sense.

To create a new component in Angular, it's best to use the Angular CLI since you will create multiple files and you must register your component with the application's module.

To accept user input in React, add an input element to your components and add a value to the component. React is based on one-way data binding. This binds a value to a view that will always render the component with that value. To bring a changed value back into the model (internal state) of your component, you must implement an event listener, like onChange in our example.

One way to accept user input in Angular is to create a template-driven form. Then, you can bind an internal state to the ngModel of an HTML input element. And this binding works both ways, so when the input element changes, the internal state of the component will also change.

There is a lot more to say about components and processing user input. I will write more about that in later sections of this guide.