``` | ``` | ||||
``` | ``` | ||||
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" |