diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-controller.md b/docs/site/tutorials/todo-list/todo-list-tutorial-controller.md index ef98e5b2570b..305baf661839 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-controller.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-controller.md @@ -30,6 +30,7 @@ Controller TodoList will be created in src/controllers/todo-list.controller.ts ? What is the name of your CRUD repository? TodoListRepository ? What is the name of ID property? id ? What is the type of your ID? number +? Is the id omitted when creating a new instance? Yes ? What is the base HTTP path name of the CRUD operations? /todo-lists create src/controllers/todo-list.controller.ts update src/controllers/index.ts @@ -193,119 +194,8 @@ export class TodoListTodoController { ``` Using our constraining factory as we did with the `POST` request, we'll define -the controller methods for the rest of the HTTP verbs for the route. The -completed controller should look as follows: - -{% include code-caption.html content="src/controllers/todo-list-todo.controller.ts" %} - -```ts -import { - Count, - CountSchema, - Filter, - repository, - Where, -} from '@loopback/repository'; -import { - del, - get, - getModelSchemaRef, - getWhereSchemaFor, - param, - patch, - post, - requestBody, -} from '@loopback/rest'; -import {Todo} from '../models'; -import {TodoListRepository} from '../repositories'; - -export class TodoListTodoController { - constructor( - @repository(TodoListRepository) protected todoListRepo: TodoListRepository, - ) {} - - @post('/todo-lists/{id}/todos', { - responses: { - '200': { - description: 'TodoList.Todo model instance', - content: {'application/json': {schema: getModelSchemaRef(Todo)}}, - }, - }, - }) - async create( - @param.path.number('id') id: number, - @requestBody({ - content: { - 'application/json': { - schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}), - }, - }, - }) - todo: Omit, - ): Promise { - return this.todoListRepo.todos(id).create(todo); - } - - @get('/todo-lists/{id}/todos', { - responses: { - '200': { - description: "Array of Todo's belonging to TodoList", - content: { - 'application/json': { - schema: {type: 'array', items: getModelSchemaRef(Todo)}, - }, - }, - }, - }, - }) - async find( - @param.path.number('id') id: number, - @param.query.object('filter') filter?: Filter, - ): Promise { - return this.todoListRepo.todos(id).find(filter); - } - - @patch('/todo-lists/{id}/todos', { - responses: { - '200': { - description: 'TodoList.Todo PATCH success count', - content: {'application/json': {schema: CountSchema}}, - }, - }, - }) - async patch( - @param.path.number('id') id: number, - @requestBody({ - content: { - 'application/json': { - schema: getModelSchemaRef(Todo, {partial: true}), - }, - }, - }) - todo: Partial - @param.query.object('where', getWhereSchemaFor(Todo)) where?: Where, - ): Promise { - return this.todoListRepo.todos(id).patch(todo, where); - } - - @del('/todo-lists/{id}/todos', { - responses: { - '200': { - description: 'TodoList.Todo DELETE success count', - content: {'application/json': {schema: CountSchema}}, - }, - }, - }) - async delete( - @param.path.number('id') id: number, - @param.query.object('where', getWhereSchemaFor(Todo)) where?: Where, - ): Promise { - return this.todoListRepo.todos(id).delete(where); - } -} -``` - -Check out our `TodoList` example to see the full source code generated for the +the controller methods for the rest of the HTTP verbs for the route. Check out +our `TodoList` example to see the full source code generated for the `TodoListTodo` controller: [src/controllers/todo-list-todo.controller.ts](https://github.com/strongloop/loopback-next/blob/master/examples/todo-list/src/controllers/todo-list-todo.controller.ts) @@ -333,4 +223,4 @@ And there you have it! You now have the power to define APIs for related models! ### Navigation -Previous step: [Add TodoList repository](todo-list-tutorial-repository.md) +Previous step: [Add Model Relations](todo-list-tutorial-relations.md) diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-has-one-relation.md b/docs/site/tutorials/todo-list/todo-list-tutorial-has-one-relation.md new file mode 100644 index 000000000000..c3c99e741560 --- /dev/null +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-has-one-relation.md @@ -0,0 +1,207 @@ +--- +lang: en +title: 'Add TodoListImage Relation' +keywords: LoopBack 4.0, LoopBack 4 +sidebar: lb4_sidebar +permalink: /doc/en/lb4/todo-list-tutorial-has-one-relation.html +summary: LoopBack 4 TodoList Application Tutorial - Add TodoListImage Relation +--- + +We have that a `Todo` [`belongsTo`](../../BelongsTo-relation.md) a `TodoList` +and a `TodoList` [`hasMany`](../../HasMany-relation.md) `Todo`s. Another type of +relation we can add is [`hasOne`](../../hasOne-relation.md). To do so, let's add +`TodoListImage` such that each `TodoList` `hasOne` image. + +### Create the Model + +Similar to how we created the model for +[`TodoList`](todo-list-tutorial-model.md), using `lb4 model`: + +```sh +lb4 model +? Model class name: TodoListImage +? Please select the model base class Entity (A persisted model with an ID) +? Allow additional (free-form) properties? No +Model TodoListImage will be created in src/models/todo-list-image.model.ts + +Let's add a property to TodoListImage +Enter an empty property name when done + +? Enter the property name: id +? Property type: number +? Is id the ID property? Yes +? Is id generated automatically? No +? Is it required?: No +? Default value [leave blank for none]: + +Let's add another property to TodoListImage +Enter an empty property name when done + +? Enter the property name: value +? Property type: string +? Is it required?: Yes +? Default value [leave blank for none]: + +Let's add another property to TodoListImage +Enter an empty property name when done + +? Enter the property name: + create src/models/todo-list-image.model.ts + update src/models/index.ts + +Model TodoListImage was created in src/models/ +``` + +### Create the Repository + +Using `lb4 repository`, let's create the repository: + +```sh +lb4 repository +? Please select the datasource DbDatasource +? Select the model(s) you want to generate a repository TodoListImage +? Please select the repository base class DefaultCrudRepository (Legacy juggler bridge) + create src/repositories/todo-list-image.repository.ts + update src/repositories/index.ts + +Repository TodoListImageRepository was created in src/repositories/ +``` + +### Add the Relation + +First, let's add the relation to the model classes: + +{% include code-caption.html content="src/models/todo-list-image.model.ts" %} + +```ts +import {belongsTo} from '@loopback/repository'; +import {TodoList, TodoListWithRelations} from './todo-list.model'; + +@model() +export class TodoListImage extends Entity { + // ... other properties + + @belongsTo(() => TodoList) + todoListId: number; + + // ... +} + +export interface TodoListImageRelations { + todoList?: TodoListWithRelations; +} +``` + +{% include code-caption.html content="src/models/todo-list.model.ts" %} + +```ts +import {hasOne} from '@loopback/repository'; +import { + TodoListImage, + TodoListImageWithRelations, +} from './todo-list-image.model'; + +@model() +export class TodoList extends Entity { + // ... other properties + + @hasOne(() => TodoListImage) + image?: TodoListImage; + + // ... +} + +export interface TodoListRelations { + todos?: TodoWithRelations[]; + + // Add the following line + image?: TodoListImageWithRelations; +} +``` + +Next, let's add the relation to the repository classes: + +{% include code-caption.html content="src/repositories/todo-list.repository.ts" %} + +```ts +// Add the following imports +import {HasOneRepositoryFactory} from '@loopback/repository'; +import {TodoListImage} from '../models'; +import {TodoListImageRepository} from './todo-list-image.repository'; + +export class TodoListRepository extends DefaultCrudRepository< + TodoList, + typeof TodoList.prototype.id, + TodoListRelations +> { + // other code + + // Add the following + public readonly image: HasOneRepositoryFactory< + TodoListImage, + typeof TodoList.prototype.id + >; + + constructor( + // other code + + // Add the following + @repository.getter('TodoListImageRepository') + protected todoListImageRepositoryGetter: Getter, + ) { + // other code + + // Add the following + this.image = this.createHasOneRepositoryFactoryFor( + 'image', + todoListImageRepositoryGetter, + ); + + this.registerInclusionResolver('image', this.image.inclusionResolver); + } +} +``` + +```ts +import {BelongsToAccessor} from '@loopback/repository'; +import {TodoList} from '../models'; +import {TodoListRepository} from './todo-list.repository'; + +export class TodoListImageRepository extends DefaultCrudRepository< + TodoListImage, + typeof TodoListImage.prototype.id, + TodoListImageRelations +> { + // Add the following + public readonly todoList: BelongsToAccessor< + TodoList, + typeof TodoListImage.prototype.id + >; + constructor( + // other code + + // Add the following line + protected todoListRepositoryGetter: Getter, + ) { + // other code + + // Add the following + this.todoList = this.createBelongsToAccessorFor( + 'todoList', + todoListRepositoryGetter, + ); + + this.registerInclusionResolver('todoList', this.todoList.inclusionResolver); + } +} +``` + +{% include note.html content=" +We are working on adding `hasOne` to the CLI command `lb4 relation`. See [issue #2980](https://github.com/strongloop/loopback-next/issues/2980). +" %} + +### Navigation + +Previous step: [Add Model Relations](todo-list-tutorial-relations.md) + +Last step: [Add TodoList Controller](todo-list-tutorial-controller.md) diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-model.md b/docs/site/tutorials/todo-list/todo-list-tutorial-model.md index 939de4a1763f..a2a709920d26 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-model.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-model.md @@ -31,7 +31,7 @@ for us as follows: ```sh $ lb4 model ? Model class name: TodoList -? Please select the model base class Entity +? Please select the model base class Entity (A persisted model with an ID) ? Allow additional (free-form) properties? No Model TodoList will be created in src/models/todo-list.model.ts @@ -41,14 +41,14 @@ Enter an empty property name when done ? Enter the property name: id ? Property type: number ? Is id the ID property? Yes -? Is id generated automatically? Yes +? Is id generated automatically? No Let's add another property to TodoList Enter an empty property name when done ? Enter the property name: title ? Property type: string -? Required?: Yes +? Is it required?: Yes ? Default value [leave blank for none]: Let's add another property to TodoList @@ -56,7 +56,7 @@ Enter an empty property name when done ? Enter the property name: color ? Property type: string -? Required?: No +? Is it required?: No ? Default value [leave blank for none]: Let's add another property to TodoList diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-relations.md b/docs/site/tutorials/todo-list/todo-list-tutorial-relations.md index 4525d5b4a464..cb9c3f1652da 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-relations.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-relations.md @@ -48,7 +48,10 @@ https://loopback.io/doc/en/lb4/HasMany-relation.html#relation-metadata and other relations as well. " %} -### Behind the scene +To practice adding another relation, see the +[Adding a `HasOne` relation](todo-list-tutorial-has-one-relation.md) page. + +### Behind the scenes If you want to understand the code changes introduced from the relation generator command, read on the details in this section; otherwise, you are ready @@ -87,11 +90,57 @@ export class Todo extends Entity { #### Inclusion of Related Models -When we ran the `lb4 relation` command, we accepted the default of `Yes` to the -prompt: +You'll notice there's a `TodoRelations` interface in the `Todo` model class file +as well as a `TodoWithRelations` type defined. + +{% include code-caption.html content="src/models/todo.model.ts" %} + +```ts +export interface TodoRelations { + // describe navigational properties here +} + +export type TodoWithRelations = Todo & TodoRelations; +``` + +In the `TodoRelations` interface, we want to describe a `todoList` as a +navigational property. We can do that as follows: + +{% include code-caption.html content="src/models/todo.model.ts" %} + +```ts +// add TodoListWithRelations to the following import +import {TodoList, TodoListWithRelations} from './todo-list.model'; +``` + +```ts +export interface TodoRelations { + // add the following line + todoList?: TodoListWithRelations; +} +``` + +Let's add a `todo` navigational property to the `TodoList` model as well: + +{% include code-caption.html content="src/models/todo-list.model.ts" %} + +```ts +// add TodoWithRelations to the following import +import {Todo, TodoWithRelations} from './todo.model'; +``` + +```ts +export interface TodoRelations { + // add the following line + todos?: TodoWithRelations[]; +} +``` + +Further, when we ran the `lb4 relation` command, we accepted the default of +`Yes` to the prompt: ```sh -? Allow Order queries to include data from related Customer instances? (Y/n) +? Allow Todo queries to include data from related TodoList instances? (Y/n) ``` This registers the `inclusionResolver` for the relation(s) you were working with diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md index 42ea9f477fb5..ba438b69d79d 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md @@ -29,10 +29,10 @@ datasources. lb4 repository ? Please select the datasource DbDatasource ? Select the model(s) you want to generate a repository TodoList - create src/repositories/todo-list.repository.ts - update src/repositories/index.ts ? Please select the repository base class DefaultCrudRepository (Legacy juggler bridge) + create src/repositories/todo-list.repository.ts + update src/repositories/index.ts Repository TodoListRepository was created in src/repositories/ ``` diff --git a/docs/site/tutorials/todo/todo-tutorial-datasource.md b/docs/site/tutorials/todo/todo-tutorial-datasource.md index 7cb57247cc8c..fa5a70172225 100644 --- a/docs/site/tutorials/todo/todo-tutorial-datasource.md +++ b/docs/site/tutorials/todo/todo-tutorial-datasource.md @@ -69,7 +69,7 @@ Create a `data` folder in the applications root and add a new file called {% include note.html content="If you are using a relational database as the datasource, don't forget to create the corresponding table or follow the -[Database migration instruction](https://loopback.io/doc/en/lb4/Database-migrations.html) to get it created programmatically. +[Database migration instructions](https://loopback.io/doc/en/lb4/Database-migrations.html) to get it created programmatically. " %} Once you're ready, we'll move onto adding a diff --git a/docs/site/tutorials/todo/todo-tutorial-geocoding-service.md b/docs/site/tutorials/todo/todo-tutorial-geocoding-service.md index f2f48d71012f..0e4d3c26c815 100644 --- a/docs/site/tutorials/todo/todo-tutorial-geocoding-service.md +++ b/docs/site/tutorials/todo/todo-tutorial-geocoding-service.md @@ -92,20 +92,31 @@ docs here: [REST connector](/doc/en/lb3/REST-connector.html). ### Implement a service provider -Create a new directory `src/services` and add the following two new files: +Use the `lb4 service` command and the following inputs to create a calculator +service: + +```sh +lb4 service +? Service type: Remote service proxy backed by a data source +? Please select the datasource GeocoderDataSource +? Service name: geocoder + create src/services/geocoder.service.ts + update src/services/index.ts + +Service Geocoder was created in src/services/ +``` -- `src/services/geocoder.service.ts` defining TypeScript interfaces for Geocoder - service and implementing a service proxy provider. -- `src/services/index.ts` providing a conventient access to all services via a - single `import` statement. +In the `src/services/geocoder.service.ts`, we'll add a `GeoPoint` interface and +a `geocode` function to the `GeocoderService` interface as follows: {% include code-caption.html content="src/services/geocoder.service.ts" %} ```ts -import {getService, juggler} from '@loopback/service-proxy'; +import {getService} from '@loopback/service-proxy'; import {inject, Provider} from '@loopback/core'; import {GeocoderDataSource} from '../datasources/geocoder.datasource'; +// Add the following interface export interface GeoPoint { /** * latitude @@ -119,13 +130,14 @@ export interface GeoPoint { } export interface GeocoderService { + // Add the following property geocode(address: string): Promise; } export class GeocoderServiceProvider implements Provider { constructor( @inject('datasources.geocoder') - protected dataSource: juggler.DataSource = new GeocoderDataSource(), + protected dataSource: GeocoderDataSource = new GeocoderDataSource(), ) {} value(): Promise { @@ -134,12 +146,6 @@ export class GeocoderServiceProvider implements Provider { } ``` -{% include code-caption.html content="src/services/index.ts" %} - -```ts -export * from './geocoder.service'; -``` - ### Enhance Todo model with location data Add two new properties to our Todo model: `remindAtAddress` and `remindAtGeo`. @@ -179,7 +185,8 @@ import {GeocoderService} from '../services'; export class TodoController { constructor( - @repository(TodoRepository) protected todoRepo: TodoRepository, + @repository(TodoRepository) + public todoRepository: TodoRepository, @inject('services.GeocoderService') protected geoService: GeocoderService, ) {} @@ -187,7 +194,7 @@ export class TodoController { } ``` -Modify `create` method to look up the address provided in `remindAtAddress` +Modify the `create` method to look up the address provided in `remindAtAddress` property and convert it to GPS coordinates stored in `remindAtGeo`. {% include code-caption.html content="src/controllers/todo.controller.ts" %} diff --git a/docs/site/tutorials/todo/todo-tutorial-model.md b/docs/site/tutorials/todo/todo-tutorial-model.md index 083171295ea1..45a78145f1ee 100644 --- a/docs/site/tutorials/todo/todo-tutorial-model.md +++ b/docs/site/tutorials/todo/todo-tutorial-model.md @@ -48,7 +48,7 @@ these steps: ```sh lb4 model ? Model class name: todo -? Please select the model base class: Entity +? Please select the model base class Entity (A persisted model with an ID) ? Allow additional (free-form) properties? No Model Todo will be created in src/models/todo.model.ts @@ -58,8 +58,8 @@ Enter an empty property name when done ? Enter the property name: id ? Property type: number ? Is id the ID property? Yes -? Is it required?: No ? Is id generated automatically? No +? Is it required?: No ? Default value [leave blank for none]: Let's add another property to Todo diff --git a/docs/site/tutorials/todo/todo-tutorial-putting-it-together.md b/docs/site/tutorials/todo/todo-tutorial-putting-it-together.md index 73470e1f56c0..6b10d80de840 100644 --- a/docs/site/tutorials/todo/todo-tutorial-putting-it-together.md +++ b/docs/site/tutorials/todo/todo-tutorial-putting-it-together.md @@ -52,6 +52,8 @@ Here are some requests you can try: That's it! You've just created your first LoopBack 4 application! +_Note: Use **CTRL+C** to stop the application_ + ### Where to go from here There are still a ton of features you can use to build on top of the diff --git a/docs/site/tutorials/todo/todo-tutorial-repository.md b/docs/site/tutorials/todo/todo-tutorial-repository.md index 214eb3d89989..77bf8e376555 100644 --- a/docs/site/tutorials/todo/todo-tutorial-repository.md +++ b/docs/site/tutorials/todo/todo-tutorial-repository.md @@ -33,11 +33,12 @@ list of available datasources. lb4 repository ? Please select the datasource DbDatasource ? Select the model(s) you want to generate a repository Todo - create src/repositories/todo.repository.ts - update src/repositories/index.ts ? Please select the repository base class DefaultCrudRepository (Legacy juggler bridge) + create src/repositories/todo.repository.ts + update src/repositories/index.ts + Repository TodoRepository was created in src/repositories/ ``` diff --git a/docs/site/tutorials/todo/todo-tutorial-scaffolding.md b/docs/site/tutorials/todo/todo-tutorial-scaffolding.md index 43b6c08af9e7..e0953d2fc2d6 100644 --- a/docs/site/tutorials/todo/todo-tutorial-scaffolding.md +++ b/docs/site/tutorials/todo/todo-tutorial-scaffolding.md @@ -81,6 +81,8 @@ tsconfig.json .mocharc.json ``` +Note that there might be extra files not listed here. + | File | Purpose | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `index.ts` | Allows importing contents of the `src` folder (for use elsewhere) |