<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>Apollo Angular 2 example</title>
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.23?main=browser"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="https://wzrd.in/standalone/frontpage-app-dependencies@1.0.1"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<app-root>
loading...
</app-root>
</body>
</html>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
var paths = {
'npm:': 'https://unpkg.com/'
};
var map = {
'app': 'src',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'rxjs': 'npm:rxjs@5.0.0-beta.12',
'ts': 'npm:plugin-typescript@4.0.10/lib/plugin.js',
'typescript': 'npm:typescript@2.0.0/lib/typescript.js',
'apollo-client': 'npm:apollo-client',
'apollo-client-rxjs': 'npm:apollo-client-rxjs',
'angular2-apollo': 'npm:angular2-apollo',
'lodash': 'npm:lodash',
'graphql': 'npm:graphql',
'whatwg-fetch': 'npm:whatwg-fetch',
'graphql-tag': 'npm:graphql-tag',
'symbol-observable': 'npm:symbol-observable', // XXX waiting for apollo-client@0.4.15
'redux': 'npm:redux/dist/redux.min.js'
};
//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: './main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js', main: 'index.js' },
'symbol-observable': { defaultExtension: 'js', main: 'index.js' },
'lodash': { defaultExtension: 'js', main: 'index.js' },
'apollo-client': { main: './index.js', defaultExtension: 'js' },
'apollo-client-rxjs': { main: './build/src/index.js', defaultExtension: 'js' },
'angular2-apollo': { main: './build/src/index.js', defaultExtension: 'js' },
'whatwg-fetch': { main: './fetch.js', defaultExtension: 'js' },
'redux': { format: 'cjs', defaultExtension: 'js' }
};
[
'assign',
'countby',
'clonedeep',
'flatten',
'forin',
'forown',
'has',
'identity',
'includes',
'isarray',
'isequal',
'isnull',
'isnumber',
'isobject',
'isstring',
'isfunction',
'isundefined',
'isplainobject',
'keys',
'keysin',
'mapvalues',
'omit',
'pick',
'rest',
'_baseclone',
'_baseeach',
'_baseflatten',
'_basefor',
'_baseiteratee',
'_basetostring',
'_stringtopath',
'_root'
].forEach((name) => {
var pkg = `lodash.${name}`;
map[pkg] = `npm:${pkg}`;
packages[pkg] = {
defaultExtension: 'js',
main: 'index.js'
};
});
System.config({
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
'exports': 'ts'
}
},
paths: paths,
//map tells the System loader where to look for things
map: map,
//packages defines our app package
packages: packages
});
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styles: [`
app-root {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
`],
template: `
<app-post-list></app-post-list>
`
})
export class AppComponent {}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ApolloModule } from 'angular2-apollo';
import { AppComponent } from './app.component';
import { PostListComponent } from './post/post-list.component';
import { PostUpvoterComponent } from './post/post-upvoter.component';
import { client } from './client';
@NgModule({
declarations: [
AppComponent,
PostListComponent,
PostUpvoterComponent
],
imports: [
BrowserModule,
ApolloModule.withClient(client)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
import { Component, OnInit } from '@angular/core';
import { Angular2Apollo, ApolloQueryObservable } from 'angular2-apollo';
const gql = frontpageAppDependencies['graphql-tag'].default
@Component({
selector: 'app-post-list',
template: `
<ul>
<li *ngFor="let post of posts | async | select: 'posts'">
{{post.title}} by {{post.author.firstName}} {{post.author.lastName}}
({{post.votes}} votes)
<app-post-upvoter [postId]="post.id"></app-post-upvoter>
</li>
</ul>
`
})
export class PostListComponent implements OnInit {
posts: ApolloQueryObservable<any>;
constructor(private apollo: Angular2Apollo) {}
ngOnInit() {
this.posts = this.apollo.watchQuery({
query: gql`
query allPosts {
posts {
id
title
votes
author {
id
firstName
lastName
}
}
}
`,
});
}
}
import { Component, Input } from '@angular/core';
import { Angular2Apollo } from 'angular2-apollo';
const gql = frontpageAppDependencies['graphql-tag'].default;
@Component({
selector: 'app-post-upvoter',
template: `
<button (click)="upvote()">
Upvote
</button>
`
})
export class PostUpvoterComponent {
@Input() postId: number;
constructor(private apollo: Angular2Apollo) {}
upvote() {
this.apollo.mutate({
mutation: gql`
mutation upvotePost($postId: Int!) {
upvotePost(postId: $postId) {
id
votes
}
}
`,
variables: {
postId: this.postId,
},
});
}
}
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { find, filter } from 'lodash';
// src/schema/schema.js
const { makeExecutableSchema } = frontpageAppDependencies['graphql-tools'];
const typeDefinitions = `
type Author {
id: Int! # the ! means that every author object _must_ have an id
firstName: String
lastName: String
posts: [Post] # the list of Posts by this author
}
type Post {
id: Int!
title: String
author: Author
votes: Int
}
# the schema allows the following query:
type Query {
posts: [Post]
}
# this schema allows the following mutation:
type Mutation {
upvotePost (
postId: Int!
): Post
}
# we need to tell the server which types represent the root query
# and root mutation types. We call them RootQuery and RootMutation by convention.
schema {
query: Query
mutation: Mutation
}
`;
// src/schema/resolvers.js
const authors = [
{ id: 1, firstName: 'Tom', lastName: 'Coleman' },
{ id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
];
const posts = [
{ id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },
{ id: 2, authorId: 2, title: 'GraphQL Rocks', votes: 3 },
{ id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },
];
const resolveFunctions = {
Query: {
posts() {
return posts;
},
},
Mutation: {
upvotePost(_, { postId }) {
const post = find(posts, { id: postId });
if (!post) {
throw new Error(`Couldn't find post with id ${postId}`);
}
post.votes += 1;
return post;
}
},
Author: {
posts(author) {
return filter(posts, { authorId: author.id });
},
},
Post: {
author(post) {
return find(authors, { id: post.authorId });
},
}
}
// src/schema/index.js
const schema = makeExecutableSchema({
typeDefs: typeDefinitions,
resolvers: resolveFunctions,
});
// in-browser-network-interface.js
const { execute } = frontpageAppDependencies.graphql;
class InBrowserNetworkInterface {
constructor({ schema }) {
this.schema = schema;
}
query(request) {
return execute(
this.schema,
request.query,
{},
{},
request.variables,
request.operationName);
}
}
const networkInterface = new InBrowserNetworkInterface({ schema });
export const client = new ApolloClient({
networkInterface,
dataIdFromObject: r => r.id,
});
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}