Adding an Angular component
We need to display the current weather information, where <p>current weather</p> is located. In order to achieve this, you need to build a component that will be responsible for displaying the weather data.
The reason behind creating a separate component is an architectural best practice that is codified in the Model-View-ViewModel (MVVM) design pattern. You may have heard of the Model-View-Controller (MVC) pattern before. Vast majority of web-based code written circa 2005-2015 has been written following the MVC pattern. MVVM differs, in important ways, from the MVC pattern. As I have explained in my 2013 article on DevPro:
[An effective implementation of MVVM] inherently enforces proper separation of concerns. Business logic is clearly separated from presentation logic. So when a View is developed, it stays developed, because fixing a bug in one View's functionality doesn't impact other views. On the flip side, if [you use] visual inheritance effectively and [create] reusable user controls, fixing a bug in one place can fix issues throughout the application.
Angular provides an effective implementation of MVVM.
ViewModels neatly encapsulate any presentation logic and allow for simpler View code by acting as a specialized version of the model. The relationship between a View and ViewModel is straightforward, allowing for more natural ways to wrap UI behavior in reusable user controls.
You can read further about the architectural nuance, with illustrations, at http://bit.ly/MVVMvsMVC.
Next, you will create your very first Angular component, which will include the View and the ViewModel, using Angular CLI's ng generate command:
- In the terminal, execute npx ng generate component current-weather
Ensure that you are executing ng commands under the local-weather-app folder, and not under the root project folder. In addition, note that npx ng generate component current-weather can be rewritten as ng g c current-weather. Going forward, this book will utilize the shorthand format and expect you to prepend npx, if necessary.
- Observe the new files created in your app folder:
src/app
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── current-weather
├── current-weather.component.css
├── current-weather.component.html
├── current-weather.component.spec.ts
└── current-weather.component.ts
A generated component has four parts:
- current-weather.component.css contains any CSS that is specific to the component and is an optional file
- current-weather.component.html contains the HTML template that defines the look of the component and rendering of the bindings, and can be considered the View, in combination with any CSS styles used
- current-weather.component.spec.ts contains Jasmine-based unit tests that you can extend to test your component functionality
- current-weather.component.ts contains the @Component decorator above the class definition and is the glue that ties together the CSS, HTML, and JavaScript code together. The class itself can be considered the ViewModel, pulling data from services and performing any necessary transformations to expose sensible bindings for the View, as shown as follows:
src/app/current-weather/current-weather.component.ts
import { Component, OnInit } from '@angular/core'
@Component({
selector: 'app-current-weather',
templateUrl: './current-weather.component.html',
styleUrls: ['./current-weather.component.css'],
})
export class CurrentWeatherComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
If the component you're planning to write is a simple one, you can rewrite it using inline styles and an inline template, to simplify the structure of your code.
- Update CurrentWeatherComponent with an inline template and styles:
src/app/current-weather/current-weather.component.ts
import { Component, OnInit } from '@angular/core'
@Component({
selector: 'app-current-weather',
template: `
<p>
current-weather works!
</p>
`,
styles: ['']
})
export class CurrentWeatherComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
When you executed the generate command, in addition to creating the component, the command also added the new module you created to app.module.ts, avoiding an otherwise tedious task of wiring up components together:
src/app/app.module.ts
...
import { CurrentWeatherComponent } from './current-weather/current-weather.component'
...
@NgModule({
declarations: [AppComponent, CurrentWeatherComponent],
...
The bootstrap process of Angular is, admittedly, a bit convoluted. This is the chief reason Angular CLI exists. index.html contains an element named <app-root>. When Angular begins execution, it first loads main.ts, which configures the framework for browser use and loads the app module. App module then loads all its dependencies and renders within the aforementioned <app-root> element. In Chapter 7, Create a Router-First Line-of-Business App, when we build a line-of-business app, we will create our own feature modules to take advantage of the scalability features of Angular.
Now, we need to display our new component on the initial AppComponent template, so it is visible to the end user:
- Add the CurrentWeatherComponent to AppComponent by replacing <p>current weather</p> with <app-current-weather></app-current-weather>:
src/app/app.component.html
<p style="text-align:center">
<h1>
LocalCast Weather
</h1>
<p>Your city, your forecast, right now!</p>
<h2>Current Weather</h2>
<app-current-weather></app-current-weather>
</p>
- If everything worked correctly, you should see this:
Initial render of your local weather app
Note the icon and name in the tab of the browser window. As a web development norm, in the index.html file, update the <title> tag and the favicon.ico file with the name and icon of your application to customize the browser tab information. If your favicon doesn't update, append the href attribute with a unique version number, such as href="favicon.ico?v=2". As a result, your app will start looking like a real web app, instead of a CLI-generated starter project.