docs(nx): add an intro tutorial
This commit is contained in:
parent
3eede56891
commit
a063168a82
@ -25,6 +25,60 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Tutorial",
|
||||
"id": "tutorial",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "1 - Create Application",
|
||||
"id": "01-create-application"
|
||||
},
|
||||
{
|
||||
"name": "2 - Add E2E Test",
|
||||
"id": "02-add-e2e-test"
|
||||
},
|
||||
{
|
||||
"name": "3 - Display Todos",
|
||||
"id": "03-display-todos"
|
||||
},
|
||||
{
|
||||
"name": "4 - Connect to API",
|
||||
"id": "04-connect-to-api"
|
||||
},
|
||||
{
|
||||
"name": "5 - Add Node Application",
|
||||
"id": "05-add-node-app"
|
||||
},
|
||||
{
|
||||
"name": "6 - Add Node Application",
|
||||
"id": "06-proxy"
|
||||
},
|
||||
{
|
||||
"name": "7 - Share Code",
|
||||
"id": "07-share-code"
|
||||
},
|
||||
{
|
||||
"name": "8 - Create Libraries",
|
||||
"id": "08-create-libs"
|
||||
},
|
||||
{
|
||||
"name": "9 - Dep Graph",
|
||||
"id": "09-dep-graph"
|
||||
},
|
||||
{
|
||||
"name": "10 - Test Affected Projects",
|
||||
"id": "10-test-affected-projects"
|
||||
},
|
||||
{
|
||||
"name": "11 - Build Affected Projects",
|
||||
"id": "11-build-affected-projects"
|
||||
},
|
||||
{
|
||||
"name": "12 - Summary",
|
||||
"id": "12-summary"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Fundamentals",
|
||||
"id": "fundamentals",
|
||||
|
||||
105
docs/tutorial/01-create-application.md
Normal file
105
docs/tutorial/01-create-application.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Step 1: Create Application
|
||||
|
||||
In this tutorial you will use Nx to build a full-stack application out of common libraries using modern technologies like Cypress and Nest.
|
||||
|
||||
## Create a New Workspace
|
||||
|
||||
**Start by creating a new workspace.**
|
||||
|
||||
```bash
|
||||
npx -p @nrwl/schematics create-nx-workspace myorg
|
||||
```
|
||||
|
||||
When asked about 'preset', select `empty`.
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
├── libs/
|
||||
├── tools/
|
||||
├── nx.json
|
||||
├── angular.json
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tslint.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
This is an empty Nx workspace without any applications or libraries: nothing to run and nothing to test.
|
||||
|
||||
## Create an Angular Application
|
||||
|
||||
**Create you first Angular application.**
|
||||
|
||||
```bash
|
||||
ng g app todos
|
||||
```
|
||||
|
||||
Nx will ask you a few questions about the application you are trying to create: the directory it will be placed it, the tags used for linting, etc.. As your workspace grows, those things become really important. For now the default answers are good enough.
|
||||
|
||||
After Nx generated the code and ran `npm install`, you should see something like this:
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── todos/
|
||||
│ │ ├── browserslist
|
||||
│ │ ├── jest.conf.js
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── app/
|
||||
│ │ │ ├── assets/
|
||||
│ │ │ ├── environments/
|
||||
│ │ │ ├── favicon.ico
|
||||
│ │ │ ├── index.html
|
||||
│ │ │ ├── main.ts
|
||||
│ │ │ ├── polyfills.ts
|
||||
│ │ │ ├── styles.scss
|
||||
│ │ │ └── test.ts
|
||||
│ │ ├── tsconfig.app.json
|
||||
│ │ ├── tsconfig.json
|
||||
│ │ ├── tsconfig.spec.json
|
||||
│ │ └── tslint.json
|
||||
│ └── todos-e2e/
|
||||
│ ├── cypress.json
|
||||
│ ├── src/
|
||||
│ │ ├── fixtures/
|
||||
│ │ │ └── example.json
|
||||
│ │ ├── integration/
|
||||
│ │ │ └── app.spec.ts
|
||||
│ │ ├── plugins/
|
||||
│ │ │ └── index.ts
|
||||
│ │ └── support/
|
||||
│ │ ├── app.po.ts
|
||||
│ │ ├── commands.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── tsconfig.e2e.json
|
||||
│ ├── tsconfig.json
|
||||
│ └── tslint.json
|
||||
├── libs/
|
||||
├── tools/
|
||||
├── angular.json
|
||||
├── nx.json
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tslint.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
The generate command added two projects to our workspace:
|
||||
|
||||
- An Angular application
|
||||
- E2E tests for the Angular application
|
||||
|
||||
**Serve the newly created application.**
|
||||
|
||||
```bash
|
||||
ng serve todos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open http://localhost:4200 in the browser. What do you see?
|
||||
|
||||
Page saying "This project was generated with Angular CLI using Nrwl Nx"
|
||||
Page saying "This project was created using Angular CLI"
|
||||
404
|
||||
40
docs/tutorial/02-add-e2e-test.md
Normal file
40
docs/tutorial/02-add-e2e-test.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Step 2: Add E2E Test
|
||||
|
||||
By default, Nx uses Cypress to run E2E tests.
|
||||
|
||||
**Open `apps/todos-e2e/src/support/app.po.ts`.** It's a page object file that contains helpers for querying the page.
|
||||
|
||||
**Add the following two helpers:**
|
||||
|
||||
```typescript
|
||||
export const getTodos = () => cy.get('li.todo');
|
||||
export const getAddTodoButton = () => cy.get('button#add-todo');
|
||||
```
|
||||
|
||||
**Next, update `apps/todos-e2e/src/integration/app.spec.ts`.**
|
||||
|
||||
```typescript
|
||||
import { getAddTodoButton, getTodos } from '../support/app.po';
|
||||
|
||||
describe('TodoApps', () => {
|
||||
beforeEach(() => cy.visit('/'));
|
||||
|
||||
it('should display todos', () => {
|
||||
getTodos().should(t => expect(t.length).equal(2));
|
||||
getAddTodoButton().click();
|
||||
getTodos().should(t => expect(t.length).equal(3));
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
This is, obviously, not a great example of an E2E test, but the purposes of this tutorial it will do.
|
||||
|
||||
**If you haven't done it already, stop the `ng serve` command and run `ng e2e todos-e2e --watch`.**
|
||||
|
||||
!!!!!
|
||||
What assertion fails?
|
||||
!!!!!
|
||||
Expect 0 to equal 2
|
||||
Nothing fails. Everything works.
|
||||
Cannot find any elements matching 'li.todo'
|
||||
Cannot find any elements matching 'button#add-todo'
|
||||
86
docs/tutorial/03-display-todos.md
Normal file
86
docs/tutorial/03-display-todos.md
Normal file
@ -0,0 +1,86 @@
|
||||
# Step 3: Display Todos
|
||||
|
||||
Great! You have a failing E2E test. Let's make it pass!
|
||||
|
||||
The best way to work with Cypress is to keep the failing E2E test running while working on the app. This helps you see the progress you are making.
|
||||
|
||||
## Show Todos
|
||||
|
||||
**Open `apps/todos`.** If you have used Angular CLI, this should look very familiar: same layout, same module and component files. The only difference is that Nx uses Jest instead of Karma.
|
||||
|
||||
**To make the first assertion of the e2e test pass, update `apps/todos/src/app/app.component.ts`:**
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
interface Todo {
|
||||
title: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'myorg-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
todos: Todo[] = [{ title: 'Todo 1' }, { title: 'Todo 2' }];
|
||||
}
|
||||
```
|
||||
|
||||
and `apps/todos/src/app/app.component.html`:
|
||||
|
||||
```html
|
||||
<h1>Todos</h1>
|
||||
|
||||
<ul>
|
||||
<li *ngFor="let t of todos" class="todo">{{ t.title }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Refresh the e2e test.** Now the test will fail while trying to find the add todo button.
|
||||
|
||||
## Add Todos
|
||||
|
||||
**Add the `add-todo` button with the corresponding click handler.**
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
interface Todo {
|
||||
title: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'myorg-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
todos: Todo[] = [{ title: 'Todo 1' }, { title: 'Todo 2' }];
|
||||
|
||||
addTodo() {
|
||||
this.todos.push({
|
||||
title: `New todo ${Math.floor(Math.random() * 1000)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<h1>Todos</h1>
|
||||
|
||||
<ul>
|
||||
<li *ngFor="let t of todos" class="todo">{{ t.title }}</li>
|
||||
</ul>
|
||||
|
||||
<button id="add-todo" (click)="addTodo()">Add Todo</button>
|
||||
```
|
||||
|
||||
The tests should pass now.
|
||||
|
||||
---
|
||||
|
||||
## What will you see if you run `ng e2e todos-e2e --headless`
|
||||
|
||||
Cypress will run in the headless mode, and the test will pass.
|
||||
Cypress will run in the headless mode, and the test will fail.
|
||||
63
docs/tutorial/04-connect-to-api.md
Normal file
63
docs/tutorial/04-connect-to-api.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Step 4: Connect to API
|
||||
|
||||
Real-world applications don’t live in isolation — they need APIs to talk to. Let's sketch something out!
|
||||
|
||||
**Open `apps/todos/src/app/app.module.ts` to import `HttpClientModule`.**
|
||||
|
||||
```typescript
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule, HttpClientModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
**Now, use `HttpClient` in the component to get the data from the api.**
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
interface Todo {
|
||||
title: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'myorg-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
todos: Todo[] = [];
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
this.http.get<Todo[]>('/api/todos').subscribe(t => (this.todos = t));
|
||||
}
|
||||
|
||||
addTodo() {
|
||||
this.http.post('/api/addTodo', {}).subscribe(() => {
|
||||
this.fetch();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Run `ng serve todos` and open localhost:4200. What do you see?
|
||||
|
||||
"the server responded with a status of 404 (Not Found)" in Console.
|
||||
Blank screen.
|
||||
Exception rendered on the screen.
|
||||
129
docs/tutorial/05-add-node-app.md
Normal file
129
docs/tutorial/05-add-node-app.md
Normal file
@ -0,0 +1,129 @@
|
||||
# Step 5: Add Node Application Implementing API
|
||||
|
||||
Using Nx you can develop node applications next to your Angular applications. You can use same commands to run and test them. You can share code between the backend and the frontend. Let's use this capability to implement the API service.
|
||||
|
||||
**Run the following to generate a new Node application:**
|
||||
|
||||
```bash
|
||||
ng g node-app api --frontendProject=todos
|
||||
```
|
||||
|
||||
Nx will ask you a few questions, and, as with the Angular application, the defaults will work well here.
|
||||
|
||||
After Nx is done installing the required dependencies, you should see something like this:
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── todos/
|
||||
│ ├── todos-e2e/
|
||||
│ └── api/
|
||||
│ ├── jest.conf.js
|
||||
│ ├── proxy.conf.json
|
||||
│ ├── src/
|
||||
│ │ ├── app/
|
||||
│ │ │ ├── app.controller.ts
|
||||
│ │ │ ├── app.controller.spec.ts
|
||||
│ │ │ ├── app.module.ts
|
||||
│ │ │ ├── app.service.ts
|
||||
│ │ │ └── app.service.spec.ts
|
||||
│ │ ├── assets/
|
||||
│ │ ├── environments/
|
||||
│ │ │ ├── environment.ts
|
||||
│ │ │ └── environment.prod.ts
|
||||
│ │ └── main.ts
|
||||
│ ├── tsconfig.app.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tsconfig.spec.json
|
||||
│ └── tslint.json
|
||||
├── libs/
|
||||
├── nx.json
|
||||
├── package.json
|
||||
├── tools/
|
||||
├── tsconfig.json
|
||||
└── tslint.json
|
||||
```
|
||||
|
||||
The `apps` directory is where Nx places anything you can run: frontend applications, backend applications, e2e test suites. That's why the `api` application appeared there.
|
||||
|
||||
You can run:
|
||||
|
||||
- `ng serve api` to serve the application
|
||||
- `ng build api` to build the application
|
||||
- `ng test api` to test the application
|
||||
|
||||
**Open `apps/api/src/app/app.module.ts`.**
|
||||
|
||||
```typescript
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [AppService]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
By default, Nx uses the Nest framework when creating node applications. Nest is heavily inspired by Angular, so the configuration for your backend and your frontend will look similar. Also, a lot of best practices used in Angular can be used in Nest as well.
|
||||
|
||||
In this case you have an application that registers a service and a controller. Services in Nest are responsible for the business logic, and controllers are responsible for implementing Http endpoints.
|
||||
|
||||
**Update `AppService`:**
|
||||
|
||||
```typescript
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
interface Todo {
|
||||
title: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
todos: Todo[] = [{ title: 'Todo 1' }, { title: 'Todo 2' }];
|
||||
|
||||
getData(): Todo[] {
|
||||
return this.todos;
|
||||
}
|
||||
|
||||
addTodo() {
|
||||
this.todos.push({
|
||||
title: `New todo ${Math.floor(Math.random() * 1000)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Next, update the controller to invoke the service:**
|
||||
|
||||
```typescript
|
||||
import { Controller, Get, Post } from '@nestjs/common';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get('todos')
|
||||
getData() {
|
||||
return this.appService.getData();
|
||||
}
|
||||
|
||||
@Post('addTodo')
|
||||
addTodo() {
|
||||
return this.appService.addTodo();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Run `ng serve api` and open https://localhost:3333/api/todos. What do you see?
|
||||
|
||||
`[{"title":"Todo 1"},{"title":"Todo 2"}]`
|
||||
Blank screen
|
||||
404
|
||||
47
docs/tutorial/06-proxy.md
Normal file
47
docs/tutorial/06-proxy.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Step 6: Proxy
|
||||
|
||||
You passed `--frontendProject=todos` when creating the node application. What did that argument do?
|
||||
|
||||
It created a proxy configuration that allows the Angular application to talk to the API.
|
||||
|
||||
**To see how it works, open `angular.json` and find the `serve` target of the todos app.**
|
||||
|
||||
```json
|
||||
{
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "todos:build",
|
||||
"proxyConfig": "apps/todos/proxy.conf.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "todos:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note the `proxyConfig` property.**
|
||||
|
||||
**Now open `proxy.conf.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3333",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration tells `ng serve` to forward all requests starting with `/api` to the process listening on port 3333.
|
||||
|
||||
---
|
||||
|
||||
## Now run both `ng serve todos` and `ng serve api`, open localhost:4200. What do you see?
|
||||
|
||||
Todos application working!
|
||||
404 in the console
|
||||
Todos are displayed but the Add Todo button doesn't work
|
||||
109
docs/tutorial/07-share-code.md
Normal file
109
docs/tutorial/07-share-code.md
Normal file
@ -0,0 +1,109 @@
|
||||
# Step 7: Share Code
|
||||
|
||||
There is a problem. Both the backend and the frontend define the `Todo` interface. The interface is in sync now, but in a real application, over time, it will diverge, and, as a result, runtime errors will creep in. You need to share this interface between the backend and the frontend. In Nx, you can do it by creating a library.
|
||||
|
||||
**Run the following generator to create a library:**
|
||||
|
||||
```bash
|
||||
ng g lib data
|
||||
```
|
||||
|
||||
**When asked "What framework should this library use?", select `TypeScript`.** The result should look like this:
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── frontend/
|
||||
│ ├── frontend-e2e/
|
||||
│ └── api/
|
||||
├── libs/
|
||||
│ └── data/
|
||||
│ ├── jest.conf.js
|
||||
│ ├── src/
|
||||
│ │ ├── lib/
|
||||
│ │ └── index.ts
|
||||
│ ├── tsconfig.app.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tsconfig.spec.json
|
||||
│ └── tslint.json
|
||||
├── nx.json
|
||||
├── package.json
|
||||
├── tools/
|
||||
├── tsconfig.json
|
||||
└── tslint.json
|
||||
```
|
||||
|
||||
**Copy the interface into the library's index file.**
|
||||
|
||||
```typescript
|
||||
interface Todo {
|
||||
title: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Update Backend
|
||||
|
||||
**Now update `app.service.ts` to import the interface:**
|
||||
|
||||
```typescript
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Todo } from '@myorg/data';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
todos: Todo[] = [{ title: 'Todo 1' }, { title: 'Todo 2' }];
|
||||
|
||||
getData(): Todo[] {
|
||||
return this.todos;
|
||||
}
|
||||
|
||||
addTodo() {
|
||||
this.todos.push({
|
||||
title: `New todo ${Math.floor(Math.random() * 1000)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Update Frontend
|
||||
|
||||
**Next import the interface on the frontend:**
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Todo } from '@myorg/data';
|
||||
|
||||
@Component({
|
||||
selector: 'myorg-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
todos: Todo[] = [];
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
this.http.get<Todo[]>('/api/todos').subscribe(t => (this.todos = t));
|
||||
}
|
||||
|
||||
addTodo() {
|
||||
this.http.post('/api/addTodo', {}).subscribe(() => {
|
||||
this.fetch();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Every time you add a new library, you have to restart `ng serve`. **So restart both `ng serve api` and `ng serve todos` and you should see the application running.**
|
||||
|
||||
---
|
||||
|
||||
## Nx allows you to share code...
|
||||
|
||||
Between frontend and backend apps
|
||||
Between different frontend apps
|
||||
Between different node apps
|
||||
158
docs/tutorial/08-create-libs.md
Normal file
158
docs/tutorial/08-create-libs.md
Normal file
@ -0,0 +1,158 @@
|
||||
# Step 8: Create Libs
|
||||
|
||||
Libraries aren't just a way to share code in Nx. They are also useful for factoring out code into small units with well-defined public API.
|
||||
|
||||
## Public API
|
||||
|
||||
Every library has an `index.ts` file, which defines its public API. Other applications and libraries can only access what the `index.ts` exports. Everything else defined in the library is private.
|
||||
|
||||
## UI Libraries
|
||||
|
||||
To illustrate how useful libraries can be, create a library of Angular components.
|
||||
|
||||
**Run `ng g lib ui`, and select Angular as the library framework.**
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── frontend/
|
||||
│ ├── frontend-e2e/
|
||||
│ └── api/
|
||||
├── libs/
|
||||
│ ├── data/
|
||||
│ └── ui/
|
||||
│ ├── jest.conf.js
|
||||
│ ├── src/
|
||||
│ │ ├── lib/
|
||||
│ │ │ ├── ui.module.spec.ts
|
||||
│ │ │ └── ui.module.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── tsconfig.app.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tsconfig.spec.json
|
||||
│ └── tslint.json
|
||||
├── nx.json
|
||||
├── package.json
|
||||
├── tools/
|
||||
├── tsconfig.json
|
||||
└── tslint.json
|
||||
```
|
||||
|
||||
The `ui.module.ts` file looks like this:
|
||||
|
||||
```typescript
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule]
|
||||
})
|
||||
export class UiModule {}
|
||||
```
|
||||
|
||||
## Add Component
|
||||
|
||||
**Add a component to the newly created ui library by running:**
|
||||
|
||||
```bash
|
||||
ng g component todos --project=ui --export
|
||||
```
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── frontend/
|
||||
│ ├── frontend-e2e/
|
||||
│ └── api/
|
||||
├── libs/
|
||||
│ ├── data/
|
||||
│ └── ui/
|
||||
│ ├── jest.conf.js
|
||||
│ ├── src/
|
||||
│ │ ├── lib/
|
||||
│ │ │ ├── todos/
|
||||
│ │ │ │ ├── todos.component.css
|
||||
│ │ │ │ ├── todos.component.html
|
||||
│ │ │ │ ├── todos.component.spec.ts
|
||||
│ │ │ │ └── todos.component.ts
|
||||
│ │ │ ├── ui.module.spec.ts
|
||||
│ │ │ └── ui.module.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── tsconfig.app.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tsconfig.spec.json
|
||||
│ └── tslint.json
|
||||
├── nx.json
|
||||
├── package.json
|
||||
├── tools/
|
||||
├── tsconfig.json
|
||||
└── tslint.json
|
||||
```
|
||||
|
||||
**Add the `todos` input to the `TodosComponent`.**
|
||||
|
||||
```typescript
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Todo } from '@myorg/data';
|
||||
|
||||
@Component({
|
||||
selector: 'myorg-todos',
|
||||
templateUrl: './todos.component.html',
|
||||
styleUrls: ['./todos.component.css']
|
||||
})
|
||||
export class TodosComponent implements OnInit {
|
||||
@Input() todos: Todo[];
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
||||
```
|
||||
|
||||
**And update `todos.component.html` to display the given todos:**
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li *ngFor="let t of todos">{{ t.title }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## Use Ui Library
|
||||
|
||||
**Now import `UiModule` into `AppModule`.**
|
||||
|
||||
```typescript
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { UiModule } from '@myorg/ui';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule, HttpClientModule, UiModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
**And update `app.component.html`:**
|
||||
|
||||
```html
|
||||
<h1>Todos</h1>
|
||||
|
||||
<myorg-todos [todos]="todos"></myorg-todos>
|
||||
|
||||
<button (click)="addTodo()">Add Todo</button>
|
||||
```
|
||||
|
||||
**Restart both `ng serve api` and `ng serve todos` and you should see the application running.**
|
||||
|
||||
---
|
||||
|
||||
## Libraries' public API is defined in...
|
||||
|
||||
index.ts files
|
||||
angular.json and tsconfig.json files
|
||||
15
docs/tutorial/09-dep-graph.md
Normal file
15
docs/tutorial/09-dep-graph.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Step 9: Dep Graph
|
||||
|
||||
An Nx workspace can contain dozens (or hundreds) of applications and libraries. It can be difficult to understand how they depend upon each other, and what are the implications of making a particular change.
|
||||
|
||||
Previously, some senior architect would create an ad-hoc dependency diagram and upload it to a corporate wiki. The diagram isn’t correct even on Day 1, and gets more and more out of sync with every passing day.
|
||||
|
||||
With Nx, you can do better than that.
|
||||
|
||||
---
|
||||
|
||||
## Run `npm run dep-graph`. What do you see?
|
||||
|
||||
A dependency diagram in the browser
|
||||
A dep-graph.html file created at the root of the workspace
|
||||
A json document printed out in the terminal
|
||||
54
docs/tutorial/10-test-affected-projects.md
Normal file
54
docs/tutorial/10-test-affected-projects.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Step 10: Test Affected Projects
|
||||
|
||||
Because Nx understands the dependency graph of your workspace, Nx can be efficient at retesting and rebuilding your projects.
|
||||
|
||||
**Commit all the changes in the repo**:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -am 'init'
|
||||
```
|
||||
|
||||
**Open `todos.component.html` and change the template:**
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li *ngFor="let t of todos">{{ t.title }}!</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Run `npm run affected:apps -- --base=master`**, and you should see `todos` printed out. The `affected:apps` looks at what you have changed compared to `base` (in this case `master`) and uses the dependency graph to figure out which apps can be affected by this change.
|
||||
|
||||
**Run `npm run affected:libs -- --base=master`**, and you should see `ui` printed out. This command works similarly, but instead of printing the affected apps, it prints the affected libs.
|
||||
|
||||
## Test Affected Projects
|
||||
|
||||
Printing the affected projects can be handy, but usually you want to do something with them. For instance, you may want to test everything that has been affected.
|
||||
|
||||
**Run `npm run affected:test -- --base=master` to retest all the affected projects.**
|
||||
|
||||
You will see the following:
|
||||
|
||||
```
|
||||
Running test for affected projects failed.
|
||||
Failed projects: todos
|
||||
You can isolate the above projects by passing --only-failed
|
||||
```
|
||||
|
||||
One of the projects failed. Instead of retesting every single project on every change, pass `--only-failed` to only retest the failed ones.
|
||||
|
||||
**Run `npm run affected:test -- --base=master --only-failed` to retest the failed projects.**
|
||||
|
||||
## Testing in Parallel
|
||||
|
||||
Some changes affect every single project in the repository. To speed up the testing of this change, pass `--parallel`.
|
||||
|
||||
**Run `npm run affected:test -- --base=master --parallel` to test all projects in parallel**
|
||||
|
||||
---
|
||||
|
||||
## Check in the changes into master and run `npm run affected:test -- --base=master`. What do you see?
|
||||
|
||||
No tests ran
|
||||
The `todos` project failed as before
|
||||
`Cannot run tests against master` error
|
||||
32
docs/tutorial/11-build-affected-projects.md
Normal file
32
docs/tutorial/11-build-affected-projects.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Step 11: Build Affected Projects
|
||||
|
||||
## Build Affected Apps
|
||||
|
||||
**Once again make a change to `todos.component.html`:**
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li *ngFor="let t of todos">{{ t.title }}!!!!!</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Run `npm run affected:build -- --base=master`**
|
||||
|
||||
Nx will rebuild `todos` app. Why didn't it rebuild `ui`?
|
||||
|
||||
By default, Nx build libraries in the context of some application. You can change it if you mark a library as `publishable`.
|
||||
|
||||
## Affected:\*
|
||||
|
||||
You can run any target against the affected projects in the graph like this:
|
||||
|
||||
```bash
|
||||
npm run affected -- --target=build --base=master
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Run `npm run affected -- --target=invalid --base=master`. What do you see?
|
||||
|
||||
No projects to run invalid
|
||||
An error message saying that the "invalid" target is invalid
|
||||
20
docs/tutorial/12-summary.md
Normal file
20
docs/tutorial/12-summary.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Step 12: Summary
|
||||
|
||||
In this tutorial you:
|
||||
|
||||
- Built a full stack application using Angular and Nest
|
||||
- Shared code between the frontend and the backend
|
||||
- Created a UI library
|
||||
- Used Nx dep graph capabilities to only retest and rebuild what is affected
|
||||
|
||||
## Learn More
|
||||
|
||||
Watch the video showing how to use Nx and Angular Console to build full-stack applications.
|
||||
|
||||
<iframe width="560" height="380" src="https://www.youtube.com/embed/XZpp52IqD2A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
### Read Fundamentals
|
||||
|
||||
- [Using Modern Tools](/fundamentals/use-modern-tools)
|
||||
- [Building Full-Stack Applications](/fundamentals/build-full-stack-applications)
|
||||
- [Developing Like Google: Monorepos and Automation](/fundamentals/develop-like-google)
|
||||
Loading…
x
Reference in New Issue
Block a user