| ``` | ``` | ||||
| ``` | ``` | ||||
| npm install --save-dev ts-node nodemon rimraf uuid @types/uuid | |||||
| npm install --save-dev ts-node nodemon rimraf uuid @types/uuid apollo-link-ws | |||||
| ``` | ``` | ||||
| "mutationType": { | "mutationType": { | ||||
| "name": "Mutation" | "name": "Mutation" | ||||
| }, | }, | ||||
| "subscriptionType": null, | |||||
| "subscriptionType": { | |||||
| "name": "Subscription" | |||||
| }, | |||||
| "types": [ | "types": [ | ||||
| { | { | ||||
| "kind": "OBJECT", | "kind": "OBJECT", | ||||
| "enumValues": null, | "enumValues": null, | ||||
| "possibleTypes": null | "possibleTypes": null | ||||
| }, | }, | ||||
| { | |||||
| "kind": "OBJECT", | |||||
| "name": "Subscription", | |||||
| "description": null, | |||||
| "fields": [ | |||||
| { | |||||
| "name": "pizzasChanged", | |||||
| "description": null, | |||||
| "args": [], | |||||
| "type": { | |||||
| "kind": "NON_NULL", | |||||
| "name": null, | |||||
| "ofType": { | |||||
| "kind": "LIST", | |||||
| "name": null, | |||||
| "ofType": { | |||||
| "kind": "OBJECT", | |||||
| "name": "Pizza", | |||||
| "ofType": null | |||||
| } | |||||
| } | |||||
| }, | |||||
| "isDeprecated": false, | |||||
| "deprecationReason": null | |||||
| }, | |||||
| { | |||||
| "name": "toppingsChanged", | |||||
| "description": null, | |||||
| "args": [], | |||||
| "type": { | |||||
| "kind": "NON_NULL", | |||||
| "name": null, | |||||
| "ofType": { | |||||
| "kind": "LIST", | |||||
| "name": null, | |||||
| "ofType": { | |||||
| "kind": "OBJECT", | |||||
| "name": "Topping", | |||||
| "ofType": null | |||||
| } | |||||
| } | |||||
| }, | |||||
| "isDeprecated": false, | |||||
| "deprecationReason": null | |||||
| } | |||||
| ], | |||||
| "inputFields": null, | |||||
| "interfaces": [], | |||||
| "enumValues": null, | |||||
| "possibleTypes": null | |||||
| }, | |||||
| { | { | ||||
| "kind": "OBJECT", | "kind": "OBJECT", | ||||
| "name": "__Schema", | "name": "__Schema", |
| updatedToppingDto: ChangeToppingDto; | updatedToppingDto: ChangeToppingDto; | ||||
| }; | }; | ||||
| export type Subscription = { | |||||
| __typename?: 'Subscription'; | |||||
| pizzasChanged: Array<Maybe<Pizza>>; | |||||
| toppingsChanged: Array<Maybe<Topping>>; | |||||
| }; | |||||
| export type ResolverTypeWrapper<T> = Promise<T> | T; | export type ResolverTypeWrapper<T> = Promise<T> | T; | ||||
| ChangePizzaDto: ChangePizzaDto; | ChangePizzaDto: ChangePizzaDto; | ||||
| ChangeToppingDto: ChangeToppingDto; | ChangeToppingDto: ChangeToppingDto; | ||||
| Mutation: ResolverTypeWrapper<{}>; | Mutation: ResolverTypeWrapper<{}>; | ||||
| Subscription: ResolverTypeWrapper<{}>; | |||||
| }; | }; | ||||
| /** Mapping between all available schema types and the resolvers parents */ | /** Mapping between all available schema types and the resolvers parents */ | ||||
| ChangePizzaDto: ChangePizzaDto; | ChangePizzaDto: ChangePizzaDto; | ||||
| ChangeToppingDto: ChangeToppingDto; | ChangeToppingDto: ChangeToppingDto; | ||||
| Mutation: {}; | Mutation: {}; | ||||
| Subscription: {}; | |||||
| }; | }; | ||||
| export type PizzaResolvers<ContextType = any, ParentType extends ResolversParentTypes['Pizza'] = ResolversParentTypes['Pizza']> = { | export type PizzaResolvers<ContextType = any, ParentType extends ResolversParentTypes['Pizza'] = ResolversParentTypes['Pizza']> = { | ||||
| updateTopping?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<MutationUpdateToppingArgs, 'toppingId' | 'updatedToppingDto'>>; | updateTopping?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<MutationUpdateToppingArgs, 'toppingId' | 'updatedToppingDto'>>; | ||||
| }; | }; | ||||
| export type SubscriptionResolvers<ContextType = any, ParentType extends ResolversParentTypes['Subscription'] = ResolversParentTypes['Subscription']> = { | |||||
| pizzasChanged?: SubscriptionResolver<Array<Maybe<ResolversTypes['Pizza']>>, "pizzasChanged", ParentType, ContextType>; | |||||
| toppingsChanged?: SubscriptionResolver<Array<Maybe<ResolversTypes['Topping']>>, "toppingsChanged", ParentType, ContextType>; | |||||
| }; | |||||
| export type Resolvers<ContextType = any> = { | export type Resolvers<ContextType = any> = { | ||||
| Pizza?: PizzaResolvers<ContextType>; | Pizza?: PizzaResolvers<ContextType>; | ||||
| Topping?: ToppingResolvers<ContextType>; | Topping?: ToppingResolvers<ContextType>; | ||||
| Query?: QueryResolvers<ContextType>; | Query?: QueryResolvers<ContextType>; | ||||
| Mutation?: MutationResolvers<ContextType>; | Mutation?: MutationResolvers<ContextType>; | ||||
| Subscription?: SubscriptionResolvers<ContextType>; | |||||
| }; | }; | ||||
| import { ApolloServer } from "apollo-server/dist"; | |||||
| import { ApolloServer, PubSub } from "apollo-server/dist"; | |||||
| import { Resolvers, ChangePizzaDto, ChangeToppingDto } from "./generated/graphql"; | import { Resolvers, ChangePizzaDto, ChangeToppingDto } from "./generated/graphql"; | ||||
| import { pizzaSchema } from "./schema/pizza"; | import { pizzaSchema } from "./schema/pizza"; | ||||
| import { PizzaResolver } from "./resolver/pizza-resolver"; | import { PizzaResolver } from "./resolver/pizza-resolver"; | ||||
| import { ToppingResolver } from "./resolver/topping-resolver"; | import { ToppingResolver } from "./resolver/topping-resolver"; | ||||
| const resolvers: Resolvers = { | const resolvers: Resolvers = { | ||||
| Topping: { | Topping: { | ||||
| id: (root, args, context) => { | id: (root, args, context) => { | ||||
| updateTopping: (root, args, context) => { | updateTopping: (root, args, context) => { | ||||
| return ToppingResolver.update(args.toppingId, args.updatedToppingDto as ChangeToppingDto); | return ToppingResolver.update(args.toppingId, args.updatedToppingDto as ChangeToppingDto); | ||||
| }, | }, | ||||
| }, | |||||
| Subscription: { | |||||
| pizzasChanged: { | |||||
| subscribe: () => PizzaResolver.pizzaEvent.asyncIterator(PizzaResolver.events.PIZZA_CHANGED) | |||||
| }, | |||||
| toppingsChanged: { | |||||
| subscribe: () => ToppingResolver.toppingEvent.asyncIterator(ToppingResolver.events.TOPPING_CHANGED) | |||||
| }, | |||||
| } | } | ||||
| } | } | ||||
| const server = new ApolloServer({ | const server = new ApolloServer({ | ||||
| typeDefs: pizzaSchema, | typeDefs: pizzaSchema, | ||||
| resolvers: resolvers as any | |||||
| }); | |||||
| resolvers: resolvers as any, | |||||
| } | |||||
| ); | |||||
| server.listen().then(({ url }) => { | |||||
| server.listen().then(({ url, subscriptionsUrl }) => { | |||||
| console.log(`🚀 Server ready at ${url}`) | console.log(`🚀 Server ready at ${url}`) | ||||
| console.log(`🚀 Subscriptions ready at ${subscriptionsUrl}`); | |||||
| }); | }); |
| import { PizzaList } from "../data/pizza-list"; | import { PizzaList } from "../data/pizza-list"; | ||||
| import { v4 as uuidv4 } from 'uuid'; | import { v4 as uuidv4 } from 'uuid'; | ||||
| import { ToppingResolver } from "./topping-resolver"; | import { ToppingResolver } from "./topping-resolver"; | ||||
| import { PubSub } from "apollo-server"; | |||||
| export class PizzaResolver { | export class PizzaResolver { | ||||
| static pizzaEvent = new PubSub(); | |||||
| static events = { | |||||
| PIZZA_CHANGED: "pizzaChanged", | |||||
| } | |||||
| static getById = (id: string): Pizza => { | static getById = (id: string): Pizza => { | ||||
| return PizzaList.filter(pizza => pizza.id === id)[0]; | return PizzaList.filter(pizza => pizza.id === id)[0]; | ||||
| }; | }; | ||||
| }; | }; | ||||
| static create = (pizzaCreateDto: ChangePizzaDto): Pizza => { | static create = (pizzaCreateDto: ChangePizzaDto): Pizza => { | ||||
| // Get Topping Objects by Ids | |||||
| const toppings: Topping[] = pizzaCreateDto.toppingIds | const toppings: Topping[] = pizzaCreateDto.toppingIds | ||||
| .map<string>((toppingId) => toppingId as string) | .map<string>((toppingId) => toppingId as string) | ||||
| .map<Topping>((toppingId) => ToppingResolver.getById(toppingId)); | .map<Topping>((toppingId) => ToppingResolver.getById(toppingId)); | ||||
| // Init new Pizza Object | |||||
| const pizza: Pizza = { | const pizza: Pizza = { | ||||
| id: uuidv4(), | id: uuidv4(), | ||||
| name: pizzaCreateDto.name, | name: pizzaCreateDto.name, | ||||
| toppings: toppings | toppings: toppings | ||||
| } | } | ||||
| // Add Pizza to 'Database' | |||||
| PizzaList.push(pizza); | |||||
| PizzaResolver.pizzaEvent.publish(PizzaResolver.events.PIZZA_CHANGED, { | |||||
| pizzasChanged: PizzaList | |||||
| }); | |||||
| console.log(`Create Pizza ...`, pizza) | console.log(`Create Pizza ...`, pizza) | ||||
| return pizza; | return pizza; | ||||
| }; | }; | ||||
| .map<Topping>((toppingId: string) => ToppingResolver.getById(toppingId)); | .map<Topping>((toppingId: string) => ToppingResolver.getById(toppingId)); | ||||
| pizza.name = pizzaUpdateDto.name; | pizza.name = pizzaUpdateDto.name; | ||||
| pizza.toppings = toppings; | pizza.toppings = toppings; | ||||
| const pizzaIndex = PizzaList.indexOf(pizza); | |||||
| PizzaList[pizzaIndex] = pizza; | |||||
| console.log(`Update Pizza ...`, pizza) | console.log(`Update Pizza ...`, pizza) | ||||
| PizzaResolver.pizzaEvent.publish(PizzaResolver.events.PIZZA_CHANGED, { | |||||
| pizzasChanged: PizzaList | |||||
| }); | |||||
| return pizza; | return pizza; | ||||
| }; | }; | ||||
| import { ToppingList } from "../data/topping-list"; | import { ToppingList } from "../data/topping-list"; | ||||
| import { v4 as uuidv4 } from 'uuid'; | import { v4 as uuidv4 } from 'uuid'; | ||||
| import { EEXIST } from "constants"; | import { EEXIST } from "constants"; | ||||
| import { PubSub } from "apollo-server"; | |||||
| export class ToppingResolver { | export class ToppingResolver { | ||||
| static toppingEvent = new PubSub(); | |||||
| static events = { | |||||
| TOPPING_CHANGED: "toppingChanged", | |||||
| } | |||||
| static getById = (id: string): Topping => { | static getById = (id: string): Topping => { | ||||
| return ToppingList.filter(topping => topping.id === id)[0]; | return ToppingList.filter(topping => topping.id === id)[0]; | ||||
| }; | }; | ||||
| id: uuidv4(), | id: uuidv4(), | ||||
| name: toppingCreateDto.name, | name: toppingCreateDto.name, | ||||
| } | } | ||||
| console.log(`Create Topping ...`, topping) | |||||
| ToppingList.push(topping); | |||||
| console.log(`Create Topping ...`, topping); | |||||
| ToppingResolver.toppingEvent.publish(ToppingResolver.events.TOPPING_CHANGED, { | |||||
| toppingsChanged: ToppingList | |||||
| }); | |||||
| return topping; | return topping; | ||||
| }; | }; | ||||
| `No Topping found with id ${toppingId}` | `No Topping found with id ${toppingId}` | ||||
| ) | ) | ||||
| } | } | ||||
| topping.name = toppingUpdateDto.name; | |||||
| topping.name = toppingUpdateDto.name; | |||||
| const toppingIndex = ToppingList.indexOf(topping); | |||||
| ToppingList[toppingIndex] = topping; | |||||
| ToppingResolver.toppingEvent.publish(ToppingResolver.events.TOPPING_CHANGED, { | |||||
| toppingsChanged: ToppingList | |||||
| }); | |||||
| console.log(`Update Topping ...`, topping) | console.log(`Update Topping ...`, topping) | ||||
| return topping; | return topping; | ||||
| }; | }; |
| createTopping(createToppingDto: ChangeToppingDto): Topping! | createTopping(createToppingDto: ChangeToppingDto): Topping! | ||||
| updateTopping(toppingId: ID!, updatedToppingDto: ChangeToppingDto!): Topping! | updateTopping(toppingId: ID!, updatedToppingDto: ChangeToppingDto!): Topping! | ||||
| } | } | ||||
| type Subscription { | |||||
| pizzasChanged: [Pizza]! | |||||
| toppingsChanged: [Topping]! | |||||
| } | |||||
| `; | `; |
| ] /* Specify library files to be included in the compilation. */, | ] /* Specify library files to be included in the compilation. */, | ||||
| "allowJs": true /* Allow javascript files to be compiled. */, | "allowJs": true /* Allow javascript files to be compiled. */, | ||||
| "outDir": "build" /* Redirect output structure to the directory. */, | "outDir": "build" /* Redirect output structure to the directory. */, | ||||
| "rootDir": "../srceal.js/assets/src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |||||
| "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |||||
| "strict": true /* Enable all strict type-checking options. */, | "strict": true /* Enable all strict type-checking options. */, | ||||
| "skipLibCheck": true, | "skipLibCheck": true, | ||||
| "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, |
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "apollo-link-ws": { | |||||
| "version": "1.0.20", | |||||
| "resolved": "https://registry.npmjs.org/apollo-link-ws/-/apollo-link-ws-1.0.20.tgz", | |||||
| "integrity": "sha512-mjSFPlQxmoLArpHBeUb2Xj+2HDYeTaJqFGOqQ+I8NVJxgL9lJe84PDWcPah/yMLv3rB7QgBDSuZ0xoRFBPlySw==", | |||||
| "requires": { | |||||
| "apollo-link": "^1.2.14", | |||||
| "tslib": "^1.9.3" | |||||
| }, | |||||
| "dependencies": { | |||||
| "tslib": { | |||||
| "version": "1.13.0", | |||||
| "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", | |||||
| "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "apollo-utilities": { | "apollo-utilities": { | ||||
| "version": "1.3.4", | "version": "1.3.4", | ||||
| "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz", | "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz", | ||||
| "async-limiter": { | "async-limiter": { | ||||
| "version": "1.0.1", | "version": "1.0.1", | ||||
| "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", | ||||
| "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", | |||||
| "dev": true | |||||
| "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" | |||||
| }, | }, | ||||
| "asynckit": { | "asynckit": { | ||||
| "version": "0.4.0", | "version": "0.4.0", | ||||
| "backo2": { | "backo2": { | ||||
| "version": "1.0.2", | "version": "1.0.2", | ||||
| "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", | ||||
| "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", | |||||
| "dev": true | |||||
| "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" | |||||
| }, | }, | ||||
| "balanced-match": { | "balanced-match": { | ||||
| "version": "1.0.0", | "version": "1.0.0", | ||||
| "iterall": { | "iterall": { | ||||
| "version": "1.3.0", | "version": "1.3.0", | ||||
| "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", | "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", | ||||
| "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", | |||||
| "dev": true | |||||
| "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" | |||||
| }, | }, | ||||
| "jasmine": { | "jasmine": { | ||||
| "version": "2.8.0", | "version": "2.8.0", | ||||
| "version": "0.9.17", | "version": "0.9.17", | ||||
| "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.17.tgz", | "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.17.tgz", | ||||
| "integrity": "sha512-hNHi2N80PBz4T0V0QhnnsMGvG3XDFDS9mS6BhZ3R12T6EBywC8d/uJscsga0cVO4DKtXCkCRrWm2sOYrbOdhEA==", | "integrity": "sha512-hNHi2N80PBz4T0V0QhnnsMGvG3XDFDS9mS6BhZ3R12T6EBywC8d/uJscsga0cVO4DKtXCkCRrWm2sOYrbOdhEA==", | ||||
| "dev": true, | |||||
| "requires": { | "requires": { | ||||
| "backo2": "^1.0.2", | "backo2": "^1.0.2", | ||||
| "eventemitter3": "^3.1.0", | "eventemitter3": "^3.1.0", | ||||
| "eventemitter3": { | "eventemitter3": { | ||||
| "version": "3.1.2", | "version": "3.1.2", | ||||
| "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", | ||||
| "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", | |||||
| "dev": true | |||||
| "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" | |||||
| }, | }, | ||||
| "ws": { | "ws": { | ||||
| "version": "5.2.2", | "version": "5.2.2", | ||||
| "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", | "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", | ||||
| "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", | "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", | ||||
| "dev": true, | |||||
| "requires": { | "requires": { | ||||
| "async-limiter": "~1.0.0" | "async-limiter": "~1.0.0" | ||||
| } | } |
| "apollo-cache-inmemory": "^1.6.0", | "apollo-cache-inmemory": "^1.6.0", | ||||
| "apollo-client": "^2.6.0", | "apollo-client": "^2.6.0", | ||||
| "apollo-link": "^1.2.11", | "apollo-link": "^1.2.11", | ||||
| "apollo-link-ws": "^1.0.20", | |||||
| "graphql": "^15.3.0", | "graphql": "^15.3.0", | ||||
| "graphql-tag": "^2.10.0", | "graphql-tag": "^2.10.0", | ||||
| "rxjs": "~6.5.5", | "rxjs": "~6.5.5", | ||||
| "subscriptions-transport-ws": "^0.9.17", | |||||
| "tslib": "^2.0.0", | "tslib": "^2.0.0", | ||||
| "zone.js": "~0.10.3" | "zone.js": "~0.10.3" | ||||
| }, | }, |
| <mat-sidenav-content> | <mat-sidenav-content> | ||||
| <app-pizza-list></app-pizza-list> | <app-pizza-list></app-pizza-list> | ||||
| <app-pizza-list-with-topping></app-pizza-list-with-topping> | <app-pizza-list-with-topping></app-pizza-list-with-topping> | ||||
| <div class=row> | |||||
| <app-pizza-create class="col-6"></app-pizza-create> | |||||
| <app-topping-create class="col-6"></app-topping-create> | |||||
| </div> | |||||
| <mat-toolbar class="footer"></mat-toolbar> | <mat-toolbar class="footer"></mat-toolbar> | ||||
| </mat-sidenav-content> | </mat-sidenav-content> | ||||
| </mat-sidenav-container> | </mat-sidenav-container> |
| import { BrowserModule } from '@angular/platform-browser'; | |||||
| import { CommonModule } from '@angular/common'; | |||||
| import { HttpClientModule } from '@angular/common/http'; | |||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||
| import { ReactiveFormsModule } from '@angular/forms'; | |||||
| import { MatButtonModule } from '@angular/material/button'; | |||||
| import { MatCardModule } from '@angular/material/card'; | import { MatCardModule } from '@angular/material/card'; | ||||
| import { MatCheckboxModule } from '@angular/material/checkbox'; | import { MatCheckboxModule } from '@angular/material/checkbox'; | ||||
| import { MatFormFieldModule } from '@angular/material/form-field'; | |||||
| import { MatIconModule } from '@angular/material/icon'; | import { MatIconModule } from '@angular/material/icon'; | ||||
| import { MatInputModule } from '@angular/material/input'; | import { MatInputModule } from '@angular/material/input'; | ||||
| import { MatListModule } from '@angular/material/list'; | import { MatListModule } from '@angular/material/list'; | ||||
| import { MatMenuModule } from '@angular/material/menu'; | import { MatMenuModule } from '@angular/material/menu'; | ||||
| import { MatSelectModule } from '@angular/material/select'; | |||||
| import { MatSidenavModule } from '@angular/material/sidenav'; | import { MatSidenavModule } from '@angular/material/sidenav'; | ||||
| import { MatTableModule } from '@angular/material/table'; | import { MatTableModule } from '@angular/material/table'; | ||||
| import { MatTabsModule } from '@angular/material/tabs'; | import { MatTabsModule } from '@angular/material/tabs'; | ||||
| import { MatToolbarModule } from '@angular/material/toolbar'; | import { MatToolbarModule } from '@angular/material/toolbar'; | ||||
| import { MatButtonModule } from '@angular/material/button'; | |||||
| import { AppComponent } from './app.component'; | |||||
| import { GraphQLModule } from '../graphql.module'; | |||||
| import { HttpClientModule } from '@angular/common/http'; | |||||
| import { MatSnackBarModule } from '@angular/material/snack-bar'; | |||||
| import { BrowserModule } from '@angular/platform-browser'; | |||||
| import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||||
| import { PizzaListComponent } from './pizza-list/pizza-list.component'; | |||||
| import { Apollo } from 'apollo-angular'; | |||||
| import { HttpLink } from 'apollo-angular-link-http'; | |||||
| import { GraphQLModule } from '../graphql.module'; | |||||
| import { AppComponent } from './app.component'; | |||||
| import { PizzaCreateComponent } from './pizza-create/pizza-create.component'; | |||||
| import { PizzaListWithToppingComponent } from './pizza-list-with-topping/pizza-list-with-topping.component'; | import { PizzaListWithToppingComponent } from './pizza-list-with-topping/pizza-list-with-topping.component'; | ||||
| import { CommonModule } from '@angular/common'; | |||||
| import { FormsModule } from '@angular/forms'; | |||||
| import { PizzaListComponent } from './pizza-list/pizza-list.component'; | |||||
| import { ToppingCreateComponent } from './topping-create/topping-create.component'; | |||||
| @NgModule({ | @NgModule({ | ||||
| declarations: [ | declarations: [ | ||||
| AppComponent, | AppComponent, | ||||
| PizzaListComponent, | PizzaListComponent, | ||||
| PizzaListWithToppingComponent, | PizzaListWithToppingComponent, | ||||
| PizzaCreateComponent, | |||||
| ToppingCreateComponent, | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| MatSnackBarModule, | |||||
| MatSelectModule, | |||||
| MatFormFieldModule, | |||||
| MatCheckboxModule, | MatCheckboxModule, | ||||
| MatCardModule, | MatCardModule, | ||||
| MatInputModule, | MatInputModule, | ||||
| BrowserModule, | BrowserModule, | ||||
| HttpClientModule, | HttpClientModule, | ||||
| CommonModule, | CommonModule, | ||||
| FormsModule, | |||||
| ReactiveFormsModule, | |||||
| GraphQLModule, | GraphQLModule, | ||||
| BrowserModule, | BrowserModule, | ||||
| GraphQLModule, | GraphQLModule, | ||||
| providers: [], | providers: [], | ||||
| bootstrap: [AppComponent] | bootstrap: [AppComponent] | ||||
| }) | }) | ||||
| export class AppModule { } | |||||
| export class AppModule { | |||||
| constructor( | |||||
| apollo: Apollo, | |||||
| httpLink: HttpLink | |||||
| ) { | |||||
| // // Create an http link: | |||||
| // const http = httpLink.create({ | |||||
| // uri: 'http://localhost:4000' | |||||
| // }); | |||||
| // // Create a WebSocket link: | |||||
| // const ws = new WebSocketLink({ | |||||
| // uri: `ws://localhost:4000/`, | |||||
| // options: { | |||||
| // reconnect: true | |||||
| // } | |||||
| // }); | |||||
| // // using the ability to split links, you can send data to each link | |||||
| // // depending on what kind of operation is being sent | |||||
| // const link = split( | |||||
| // // split based on operation type | |||||
| // ({ query }) => { | |||||
| // const { kind } = getMainDefinition(query); | |||||
| // return kind === 'OperationDefinition'; | |||||
| // }, | |||||
| // ws, | |||||
| // http, | |||||
| // ); | |||||
| // apollo.create({ | |||||
| // link, | |||||
| // cache: null | |||||
| // }); | |||||
| } | |||||
| } |
| subscription ToppingChanged { | |||||
| toppingsChanged { | |||||
| id | |||||
| name | |||||
| } | |||||
| } |
| query ListToppingsForPizzaCreate { | |||||
| listTopping { | |||||
| id | |||||
| name | |||||
| } | |||||
| } |
| <div class="container"> | |||||
| <form [formGroup]="pizzaCreateForm" (ngSubmit)="create()" class="form"> | |||||
| <h1>Create Pizza</h1> | |||||
| <mat-form-field class="form-element"> | |||||
| <mat-label> | |||||
| Name | |||||
| </mat-label> | |||||
| <input matInput type="text" formControlName="pizzaName" /> | |||||
| </mat-form-field> | |||||
| <mat-form-field appearance="fill"> | |||||
| <mat-label>Toppings</mat-label> | |||||
| <mat-select | |||||
| name="selectedToppings" | |||||
| [formControl]="pizzaToppings" | |||||
| multiple | |||||
| > | |||||
| <mat-select-trigger> | |||||
| {{ pizzaToppings.value ? pizzaToppings.value[0]?.name : "" }} | |||||
| <span | |||||
| *ngIf="pizzaToppings.value?.length > 1" | |||||
| class="example-additional-selection" | |||||
| > | |||||
| (+{{ pizzaToppings.value?.length - 1 }} | |||||
| {{ pizzaToppings.value?.length === 2 ? "other" : "others" }}) | |||||
| </span> | |||||
| </mat-select-trigger> | |||||
| <mat-option *ngFor="let topping of toppings" [value]="topping">{{ | |||||
| topping.name | |||||
| }}</mat-option> | |||||
| </mat-select> | |||||
| </mat-form-field> | |||||
| <br> | |||||
| <button mat-button type="submit">Submit</button> | |||||
| </form> | |||||
| </div> |
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { PizzaCreateComponent } from './pizza-create.component'; | |||||
| describe('PizzaCreateComponent', () => { | |||||
| let component: PizzaCreateComponent; | |||||
| let fixture: ComponentFixture<PizzaCreateComponent>; | |||||
| beforeEach(async(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [ PizzaCreateComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| })); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(PizzaCreateComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import { Component, OnInit } from '@angular/core'; | |||||
| import { FormControl, FormGroup } from '@angular/forms'; | |||||
| import { map } from 'rxjs/operators'; | |||||
| import { ChangePizzaDto, CreatePizzaGQL, ListToppingsForPizzaCreateGQL, Topping, ToppingChangedGQL } from 'src/generated/graphql'; | |||||
| import { MatSnackBar } from '@angular/material/snack-bar'; | |||||
| @Component({ | |||||
| selector: 'app-pizza-create', | |||||
| templateUrl: './pizza-create.component.html', | |||||
| styleUrls: ['./pizza-create.component.scss'] | |||||
| }) | |||||
| export class PizzaCreateComponent implements OnInit { | |||||
| pizzaName = new FormControl(''); | |||||
| pizzaToppings = new FormControl([]); | |||||
| pizzaCreateForm = new FormGroup({ | |||||
| pizzaName: this.pizzaName, | |||||
| pizzaToppings: this.pizzaToppings, | |||||
| }); | |||||
| toppings: Topping[]; | |||||
| create(): void { | |||||
| if (!this.pizzaCreateForm.valid) { | |||||
| return; | |||||
| } | |||||
| const createPizzaDto: ChangePizzaDto = { | |||||
| name: this.pizzaName.value, | |||||
| toppingIds: this.pizzaToppings.value.map((topping: Topping) => topping.id) | |||||
| }; | |||||
| this.createPizza | |||||
| .mutate({ createPizzaDto }) | |||||
| .subscribe((res) => { | |||||
| this.snackBar.open( | |||||
| `created Pizza ${res.data.createPizza.name}`, | |||||
| 'ok', | |||||
| { | |||||
| duration: 1000 | |||||
| } | |||||
| ); | |||||
| }); | |||||
| this.pizzaName.setValue(null); | |||||
| this.pizzaToppings.setValue([]); | |||||
| } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| constructor( | |||||
| listToppings: ListToppingsForPizzaCreateGQL, | |||||
| protected createPizza: CreatePizzaGQL, | |||||
| toppingChanged: ToppingChangedGQL, | |||||
| private snackBar: MatSnackBar | |||||
| ) { | |||||
| listToppings | |||||
| .watch() | |||||
| .valueChanges | |||||
| .pipe(map(result => result.data.listTopping as Topping[])) | |||||
| .subscribe(toppings => this.toppings = toppings); | |||||
| toppingChanged | |||||
| .subscribe() | |||||
| .pipe(map(event => event.data.toppingsChanged as Topping[])) | |||||
| .subscribe(toppings => this.toppings = toppings); | |||||
| } | |||||
| } |
| mutation CreatePizza($createPizzaDto: ChangePizzaDto) { | |||||
| createPizza (createPizzaDto: $createPizzaDto) { | |||||
| name | |||||
| } | |||||
| } |
| subscription PizzaWithToppingChanged { | |||||
| pizzasChanged { | |||||
| id, | |||||
| name | |||||
| toppings { | |||||
| name | |||||
| } | |||||
| } | |||||
| } |
| <mat-toolbar> | |||||
| <mat-toolbar-row> | |||||
| <h1>Pizzas With topping</h1> | |||||
| </mat-toolbar-row> | |||||
| </mat-toolbar> | |||||
| <div class="container"> | |||||
| <mat-toolbar> | |||||
| <mat-toolbar-row> | |||||
| <h1>Pizzas With topping</h1> | |||||
| </mat-toolbar-row> | |||||
| </mat-toolbar> | |||||
| <table mat-table [dataSource]="pizzas"> | |||||
| <ng-container matColumnDef="id"> | |||||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.id }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <table mat-table [dataSource]="pizzas"> | |||||
| <ng-container matColumnDef="id"> | |||||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.id }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="name"> | |||||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.name }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="name"> | |||||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.name }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="toppings"> | |||||
| <th class="toppings" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="toppings" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ getToppingListString(pizza.toppings) }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="toppings"> | |||||
| <th class="toppings" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="toppings" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ getToppingListString(pizza.toppings) }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||||
| </table> | |||||
| <ng-template #nopizzaSelected> | |||||
| no pizza selected, please select a pizza to see events! | |||||
| </ng-template> | |||||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||||
| </table> | |||||
| <ng-template #nopizzaSelected> | |||||
| no pizza selected, please select a pizza to see events! | |||||
| </ng-template> | |||||
| </div> |
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { Pizza, ListPizzaGQL, ListPizzaWithToppingGQL, Topping } from 'src/generated/graphql'; | |||||
| import { Observable } from 'rxjs'; | |||||
| import { Subject } from 'rxjs'; | |||||
| import { map } from 'rxjs/operators'; | import { map } from 'rxjs/operators'; | ||||
| import { ListPizzaWithToppingGQL, Pizza, PizzaWithToppingChangedGQL, Topping } from 'src/generated/graphql'; | |||||
| @Component({ | @Component({ | ||||
| 'toppings' | 'toppings' | ||||
| ]; | ]; | ||||
| pizzas: Observable<Pizza[]>; | |||||
| pizzas = new Subject<Pizza[]>(); | |||||
| constructor(listPizzaWithToppings: ListPizzaWithToppingGQL) { | |||||
| this.pizzas = listPizzaWithToppings | |||||
| constructor(listPizzaWithToppings: ListPizzaWithToppingGQL, pizzaChanged: PizzaWithToppingChangedGQL) { | |||||
| listPizzaWithToppings | |||||
| .watch() | .watch() | ||||
| .valueChanges | .valueChanges | ||||
| .pipe(map(result => result.data.listPizza as Pizza[])); | |||||
| .pipe(map(result => result.data.listPizza as Pizza[])) | |||||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||||
| pizzaChanged.subscribe().pipe(map(event => event.data.pizzasChanged as Pizza[])) | |||||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||||
| } | } | ||||
| ngOnInit(): void { | ngOnInit(): void { |
| subscription PizzaChanged { | |||||
| pizzasChanged { | |||||
| id | |||||
| name | |||||
| } | |||||
| } |
| <mat-toolbar> | |||||
| <mat-toolbar-row> | |||||
| <h1>Pizza</h1> | |||||
| </mat-toolbar-row> | |||||
| </mat-toolbar> | |||||
| <div class="container"> | |||||
| <mat-toolbar> | |||||
| <mat-toolbar-row> | |||||
| <h1>Pizza</h1> | |||||
| </mat-toolbar-row> | |||||
| </mat-toolbar> | |||||
| <table mat-table [dataSource]="pizzas"> | |||||
| <ng-container matColumnDef="id"> | |||||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.id }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <table mat-table [dataSource]="pizzas"> | |||||
| <ng-container matColumnDef="id"> | |||||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.id }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="name"> | |||||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.name }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="name"> | |||||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||||
| {{ pizza.name }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||||
| </table> | |||||
| <ng-template #nopizzaSelected> | |||||
| no pizza selected, please select a pizza to see events! | |||||
| </ng-template> | |||||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||||
| </table> | |||||
| <ng-template #nopizzaSelected> | |||||
| no pizza selected, please select a pizza to see events! | |||||
| </ng-template> | |||||
| </div> |
| import { Component, OnInit } from '@angular/core'; | |||||
| import { Pizza, ListPizzaGQL } from 'src/generated/graphql'; | |||||
| import { Observable } from 'rxjs'; | |||||
| import { Component, OnInit, DebugElement } from '@angular/core'; | |||||
| import { Pizza, ListPizzaGQL, PizzaChangedGQL } from 'src/generated/graphql'; | |||||
| import { Observable, race, combineLatest, concat, Subject } from 'rxjs'; | |||||
| import { map } from 'rxjs/operators'; | import { map } from 'rxjs/operators'; | ||||
| 'name', | 'name', | ||||
| ]; | ]; | ||||
| pizzas: Observable<Pizza[]>; | |||||
| pizzas = new Subject<Pizza[]>(); | |||||
| constructor(listPizza: ListPizzaGQL) { | |||||
| this.pizzas = listPizza | |||||
| .watch() | |||||
| .valueChanges | |||||
| .pipe(map(result => result.data.listPizza as Pizza[])); | |||||
| constructor(listPizza: ListPizzaGQL, pizzaChanged: PizzaChangedGQL) { | |||||
| listPizza | |||||
| .watch() | |||||
| .valueChanges | |||||
| .pipe(map(result => result.data.listPizza as Pizza[])) | |||||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||||
| pizzaChanged.subscribe().pipe(map(event => event.data.pizzasChanged as Pizza[])) | |||||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||||
| } | } | ||||
| ngOnInit(): void { | ngOnInit(): void { |
| <div class="container"> | |||||
| <form [formGroup]="toppingCreateForm" (ngSubmit)="create()" class="form"> | |||||
| <h1>Create Topping</h1> | |||||
| <mat-form-field class="form-element"> | |||||
| <mat-label> | |||||
| Name | |||||
| </mat-label> | |||||
| <input matInput type="text" formControlName="toppingName" /> | |||||
| </mat-form-field> | |||||
| <br> | |||||
| <button mat-button type="submit">Submit</button> | |||||
| </form> | |||||
| </div> |
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { ToppingCreateComponent } from './topping-create.component'; | |||||
| describe('ToppingCreateComponent', () => { | |||||
| let component: ToppingCreateComponent; | |||||
| let fixture: ComponentFixture<ToppingCreateComponent>; | |||||
| beforeEach(async(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [ ToppingCreateComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| })); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(ToppingCreateComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import { Component, OnInit } from '@angular/core'; | |||||
| import { FormControl, FormGroup } from '@angular/forms'; | |||||
| import { ChangeToppingDto, CreateToppingGQL } from 'src/generated/graphql'; | |||||
| import { MatSnackBar } from '@angular/material/snack-bar'; | |||||
| @Component({ | |||||
| selector: 'app-topping-create', | |||||
| templateUrl: './topping-create.component.html', | |||||
| styleUrls: ['./topping-create.component.scss'] | |||||
| }) | |||||
| export class ToppingCreateComponent implements OnInit { | |||||
| toppingName = new FormControl(''); | |||||
| toppingCreateForm = new FormGroup({ | |||||
| toppingName: this.toppingName, | |||||
| }); | |||||
| create(): void { | |||||
| if (!this.toppingCreateForm.valid) { | |||||
| return; | |||||
| } | |||||
| const createToppingDto: ChangeToppingDto = { | |||||
| name: this.toppingName.value, | |||||
| }; | |||||
| this.createToping | |||||
| .mutate({ createToppingDto }) | |||||
| .subscribe((res) => { | |||||
| this.snackBar.open( | |||||
| `created Topping ${res.data.createTopping.name}`, | |||||
| 'ok', | |||||
| { | |||||
| duration: 1000 | |||||
| } | |||||
| ); | |||||
| }); | |||||
| this.toppingName.setValue(null); | |||||
| } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| constructor( | |||||
| protected createToping: CreateToppingGQL, | |||||
| private snackBar: MatSnackBar | |||||
| ) { | |||||
| } | |||||
| } |
| mutation CreateTopping($createToppingDto: ChangeToppingDto) { | |||||
| createTopping (createToppingDto: $createToppingDto) { | |||||
| name | |||||
| } | |||||
| } |
| toppingName: Scalars['ID']; | toppingName: Scalars['ID']; | ||||
| }; | }; | ||||
| export type ChangePizzaDto = { | |||||
| name: Scalars['String']; | |||||
| toppingIds: Array<Maybe<Scalars['ID']>>; | |||||
| }; | |||||
| export type ChangeToppingDto = { | |||||
| name: Scalars['String']; | |||||
| }; | |||||
| export type Mutation = { | |||||
| __typename?: 'Mutation'; | |||||
| createPizza: Pizza; | |||||
| updatePizza: Pizza; | |||||
| createTopping: Topping; | |||||
| updateTopping: Topping; | |||||
| }; | |||||
| export type MutationCreatePizzaArgs = { | |||||
| createPizzaDto?: Maybe<ChangePizzaDto>; | |||||
| }; | |||||
| export type MutationUpdatePizzaArgs = { | |||||
| pizzaId: Scalars['ID']; | |||||
| updatedPizzaDto: ChangePizzaDto; | |||||
| }; | |||||
| export type MutationCreateToppingArgs = { | |||||
| createToppingDto?: Maybe<ChangeToppingDto>; | |||||
| }; | |||||
| export type MutationUpdateToppingArgs = { | |||||
| toppingId: Scalars['ID']; | |||||
| updatedToppingDto: ChangeToppingDto; | |||||
| }; | |||||
| export type Subscription = { | |||||
| __typename?: 'Subscription'; | |||||
| pizzasChanged: Array<Maybe<Pizza>>; | |||||
| toppingsChanged: Array<Maybe<Topping>>; | |||||
| }; | |||||
| export enum CacheControlScope { | export enum CacheControlScope { | ||||
| Public = 'PUBLIC', | Public = 'PUBLIC', | ||||
| Private = 'PRIVATE' | Private = 'PRIVATE' | ||||
| } | } | ||||
| export type ToppingChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||||
| export type ToppingChangedSubscription = ( | |||||
| { __typename?: 'Subscription' } | |||||
| & { toppingsChanged: Array<Maybe<( | |||||
| { __typename?: 'Topping' } | |||||
| & Pick<Topping, 'id' | 'name'> | |||||
| )>> } | |||||
| ); | |||||
| export type ListToppingsForPizzaCreateQueryVariables = Exact<{ [key: string]: never; }>; | |||||
| export type ListToppingsForPizzaCreateQuery = ( | |||||
| { __typename?: 'Query' } | |||||
| & { listTopping: Array<Maybe<( | |||||
| { __typename?: 'Topping' } | |||||
| & Pick<Topping, 'id' | 'name'> | |||||
| )>> } | |||||
| ); | |||||
| export type CreatePizzaMutationVariables = Exact<{ | |||||
| createPizzaDto?: Maybe<ChangePizzaDto>; | |||||
| }>; | |||||
| export type CreatePizzaMutation = ( | |||||
| { __typename?: 'Mutation' } | |||||
| & { createPizza: ( | |||||
| { __typename?: 'Pizza' } | |||||
| & Pick<Pizza, 'name'> | |||||
| ) } | |||||
| ); | |||||
| export type PizzaWithToppingChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||||
| export type PizzaWithToppingChangedSubscription = ( | |||||
| { __typename?: 'Subscription' } | |||||
| & { pizzasChanged: Array<Maybe<( | |||||
| { __typename?: 'Pizza' } | |||||
| & Pick<Pizza, 'id' | 'name'> | |||||
| & { toppings: Array<( | |||||
| { __typename?: 'Topping' } | |||||
| & Pick<Topping, 'name'> | |||||
| )> } | |||||
| )>> } | |||||
| ); | |||||
| export type ListPizzaWithToppingQueryVariables = Exact<{ [key: string]: never; }>; | export type ListPizzaWithToppingQueryVariables = Exact<{ [key: string]: never; }>; | ||||
| )>> } | )>> } | ||||
| ); | ); | ||||
| export type PizzaChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||||
| export type PizzaChangedSubscription = ( | |||||
| { __typename?: 'Subscription' } | |||||
| & { pizzasChanged: Array<Maybe<( | |||||
| { __typename?: 'Pizza' } | |||||
| & Pick<Pizza, 'id' | 'name'> | |||||
| )>> } | |||||
| ); | |||||
| export type ListPizzaQueryVariables = Exact<{ [key: string]: never; }>; | export type ListPizzaQueryVariables = Exact<{ [key: string]: never; }>; | ||||
| )>> } | )>> } | ||||
| ); | ); | ||||
| export type CreateToppingMutationVariables = Exact<{ | |||||
| createToppingDto?: Maybe<ChangeToppingDto>; | |||||
| }>; | |||||
| export type CreateToppingMutation = ( | |||||
| { __typename?: 'Mutation' } | |||||
| & { createTopping: ( | |||||
| { __typename?: 'Topping' } | |||||
| & Pick<Topping, 'name'> | |||||
| ) } | |||||
| ); | |||||
| export const ToppingChangedDocument = gql` | |||||
| subscription ToppingChanged { | |||||
| toppingsChanged { | |||||
| id | |||||
| name | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class ToppingChangedGQL extends Apollo.Subscription<ToppingChangedSubscription, ToppingChangedSubscriptionVariables> { | |||||
| document = ToppingChangedDocument; | |||||
| } | |||||
| export const ListToppingsForPizzaCreateDocument = gql` | |||||
| query ListToppingsForPizzaCreate { | |||||
| listTopping { | |||||
| id | |||||
| name | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class ListToppingsForPizzaCreateGQL extends Apollo.Query<ListToppingsForPizzaCreateQuery, ListToppingsForPizzaCreateQueryVariables> { | |||||
| document = ListToppingsForPizzaCreateDocument; | |||||
| } | |||||
| export const CreatePizzaDocument = gql` | |||||
| mutation CreatePizza($createPizzaDto: ChangePizzaDto) { | |||||
| createPizza(createPizzaDto: $createPizzaDto) { | |||||
| name | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class CreatePizzaGQL extends Apollo.Mutation<CreatePizzaMutation, CreatePizzaMutationVariables> { | |||||
| document = CreatePizzaDocument; | |||||
| } | |||||
| export const PizzaWithToppingChangedDocument = gql` | |||||
| subscription PizzaWithToppingChanged { | |||||
| pizzasChanged { | |||||
| id | |||||
| name | |||||
| toppings { | |||||
| name | |||||
| } | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class PizzaWithToppingChangedGQL extends Apollo.Subscription<PizzaWithToppingChangedSubscription, PizzaWithToppingChangedSubscriptionVariables> { | |||||
| document = PizzaWithToppingChangedDocument; | |||||
| } | |||||
| export const ListPizzaWithToppingDocument = gql` | export const ListPizzaWithToppingDocument = gql` | ||||
| query ListPizzaWithTopping { | query ListPizzaWithTopping { | ||||
| listPizza { | listPizza { | ||||
| export class ListPizzaWithToppingGQL extends Apollo.Query<ListPizzaWithToppingQuery, ListPizzaWithToppingQueryVariables> { | export class ListPizzaWithToppingGQL extends Apollo.Query<ListPizzaWithToppingQuery, ListPizzaWithToppingQueryVariables> { | ||||
| document = ListPizzaWithToppingDocument; | document = ListPizzaWithToppingDocument; | ||||
| } | |||||
| export const PizzaChangedDocument = gql` | |||||
| subscription PizzaChanged { | |||||
| pizzasChanged { | |||||
| id | |||||
| name | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class PizzaChangedGQL extends Apollo.Subscription<PizzaChangedSubscription, PizzaChangedSubscriptionVariables> { | |||||
| document = PizzaChangedDocument; | |||||
| } | } | ||||
| export const ListPizzaDocument = gql` | export const ListPizzaDocument = gql` | ||||
| query ListPizza { | query ListPizza { | ||||
| export class ListPizzaGQL extends Apollo.Query<ListPizzaQuery, ListPizzaQueryVariables> { | export class ListPizzaGQL extends Apollo.Query<ListPizzaQuery, ListPizzaQueryVariables> { | ||||
| document = ListPizzaDocument; | document = ListPizzaDocument; | ||||
| } | |||||
| export const CreateToppingDocument = gql` | |||||
| mutation CreateTopping($createToppingDto: ChangeToppingDto) { | |||||
| createTopping(createToppingDto: $createToppingDto) { | |||||
| name | |||||
| } | |||||
| } | |||||
| `; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class CreateToppingGQL extends Apollo.Mutation<CreateToppingMutation, CreateToppingMutationVariables> { | |||||
| document = CreateToppingDocument; | |||||
| } | } |
| import {NgModule} from '@angular/core'; | |||||
| import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular'; | |||||
| import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http'; | |||||
| import {InMemoryCache} from 'apollo-cache-inmemory'; | |||||
| import { NgModule } from '@angular/core'; | |||||
| import { ApolloModule, APOLLO_OPTIONS, Apollo } from 'apollo-angular'; | |||||
| import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http'; | |||||
| import { InMemoryCache } from 'apollo-cache-inmemory'; | |||||
| import { WebSocketLink } from 'apollo-link-ws'; | |||||
| import { split } from 'apollo-link'; | |||||
| import { getMainDefinition } from 'apollo-utilities'; | |||||
| import { OperationDefinitionNode } from 'graphql'; | |||||
| const uri = 'http://localhost:4000'; // <-- add the URL of the GraphQL server here | const uri = 'http://localhost:4000'; // <-- add the URL of the GraphQL server here | ||||
| export function createApollo(httpLink: HttpLink) { | |||||
| const wsUri = 'ws://localhost:4000/graphql'; // <-- add the URL of the GraphQL server here | |||||
| export function createApollo( | |||||
| httpLink: HttpLink | |||||
| ) { | |||||
| const http = httpLink.create({ | |||||
| uri | |||||
| }); | |||||
| const ws = new WebSocketLink({ | |||||
| uri: wsUri, | |||||
| options: { | |||||
| reconnect: true, | |||||
| }, | |||||
| }); | |||||
| const link = split( | |||||
| // split based on operation type | |||||
| ({ query }) => { | |||||
| const { kind, operation } = getMainDefinition(query) as OperationDefinitionNode; | |||||
| return kind === 'OperationDefinition' && operation === 'subscription'; | |||||
| }, | |||||
| ws, | |||||
| http, | |||||
| ); | |||||
| return { | return { | ||||
| link: httpLink.create({uri}), | |||||
| link, | |||||
| cache: new InMemoryCache(), | cache: new InMemoryCache(), | ||||
| }; | }; | ||||
| } | } | ||||
| }, | }, | ||||
| ], | ], | ||||
| }) | }) | ||||
| export class GraphQLModule {} | |||||
| export class GraphQLModule { } |
| table { | table { | ||||
| width: 100%; | width: 100%; | ||||
| } | } | ||||
| .container { | |||||
| padding: 10px; | |||||
| } | |||||
| .form { | |||||
| min-width: 150px; | |||||
| max-width: 500px; | |||||
| width: 100%; | |||||
| } | |||||
| .form-element { | |||||
| padding: 5px 0px 25px 2px; | |||||
| width: 100%; | |||||
| } |
| </code> | </code> | ||||
| </pre> | </pre> | ||||
| </section> | </section> | ||||
| </section> | |||||
| <section> | |||||
| <section> | <section> | ||||
| <img | <img | ||||
| src="/assets/apollo-logo.png" | src="/assets/apollo-logo.png" |