Skip to content

Commit 564da6f

Browse files
authored
Add React project with basic contact list (#19)
1 parent dfeb915 commit 564da6f

35 files changed

+65353
-1
lines changed

ASP.NET Core Basics/ASP.NET Core Basics.sln

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26228.4
4+
VisualStudioVersion = 15.0.26730.16
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6446DEF2-D423-47E2-85CC-691A52915EC2}"
77
EndProject
@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aurelia", "src\Aurelia\Aure
1313
EndProject
1414
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Angular", "src\Angular\Angular.csproj", "{F27EF346-050A-4AC6-BCAA-D2446246240C}"
1515
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "React", "React\React.csproj", "{309AB54C-C4ED-4E7E-870C-9032A970E3CF}"
17+
EndProject
1618
Global
1719
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1820
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,10 @@ Global
3133
{F27EF346-050A-4AC6-BCAA-D2446246240C}.Debug|Any CPU.Build.0 = Debug|Any CPU
3234
{F27EF346-050A-4AC6-BCAA-D2446246240C}.Release|Any CPU.ActiveCfg = Release|Any CPU
3335
{F27EF346-050A-4AC6-BCAA-D2446246240C}.Release|Any CPU.Build.0 = Release|Any CPU
36+
{309AB54C-C4ED-4E7E-870C-9032A970E3CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{309AB54C-C4ED-4E7E-870C-9032A970E3CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{309AB54C-C4ED-4E7E-870C-9032A970E3CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{309AB54C-C4ED-4E7E-870C-9032A970E3CF}.Release|Any CPU.Build.0 = Release|Any CPU
3440
EndGlobalSection
3541
GlobalSection(SolutionProperties) = preSolution
3642
HideSolutionNode = FALSE
@@ -39,5 +45,9 @@ Global
3945
{A1208011-947A-475C-9011-E2657FAB5A31} = {6446DEF2-D423-47E2-85CC-691A52915EC2}
4046
{EB45A937-E3EF-4B0F-A674-92B44BEA3FDA} = {6446DEF2-D423-47E2-85CC-691A52915EC2}
4147
{F27EF346-050A-4AC6-BCAA-D2446246240C} = {6446DEF2-D423-47E2-85CC-691A52915EC2}
48+
{309AB54C-C4ED-4E7E-870C-9032A970E3CF} = {6446DEF2-D423-47E2-85CC-691A52915EC2}
49+
EndGlobalSection
50+
GlobalSection(ExtensibilityGlobals) = postSolution
51+
SolutionGuid = {810FF428-282C-444B-9C2A-660B9AAE9D8F}
4252
EndGlobalSection
4353
EndGlobal
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import './css/site.css';
2+
import 'bootstrap';
3+
import * as React from 'react';
4+
import * as ReactDOM from 'react-dom';
5+
import { AppContainer } from 'react-hot-loader';
6+
import { BrowserRouter } from 'react-router-dom';
7+
import * as RoutesModule from './routes';
8+
let routes = RoutesModule.routes;
9+
10+
function renderApp() {
11+
// This code starts up the React app when it runs in a browser. It sets up the routing
12+
// configuration and injects the app into a DOM element.
13+
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
14+
ReactDOM.render(
15+
<AppContainer>
16+
<BrowserRouter children={ routes } basename={ baseUrl } />
17+
</AppContainer>,
18+
document.getElementById('react-app')
19+
);
20+
}
21+
22+
renderApp();
23+
24+
// Allow Hot Module Replacement
25+
if (module.hot) {
26+
module.hot.accept('./routes', () => {
27+
routes = require<typeof RoutesModule>('./routes').routes;
28+
renderApp();
29+
});
30+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as React from 'react';
2+
import { RouteComponentProps } from 'react-router';
3+
import 'isomorphic-fetch';
4+
5+
interface ContactListState {
6+
contacts: Contact[];
7+
loading: boolean;
8+
}
9+
10+
export class ContactList extends React.Component<RouteComponentProps<{}>, ContactListState> {
11+
constructor() {
12+
super();
13+
this.state = { contacts: [], loading: true };
14+
15+
fetch('http://localhost:13322/api/contactsApi/')
16+
.then(response => response.json() as Promise<Contact[]>)
17+
.then(data => {
18+
this.setState({ contacts: data, loading: false });
19+
});
20+
}
21+
22+
public render() {
23+
let contents = this.state.loading
24+
? <p><em>Loading...</em></p>
25+
: ContactList.renderContactsTable(this.state.contacts);
26+
27+
return <div>
28+
<h1>Contact List</h1>
29+
{contents}
30+
</div>;
31+
}
32+
33+
private static renderContactsTable(contacts: Contact[]) {
34+
return <table className='table'>
35+
<thead>
36+
<tr>
37+
<th>ID</th>
38+
<th>Name</th>
39+
</tr>
40+
</thead>
41+
<tbody>
42+
{contacts.map(contact =>
43+
<tr key={contact.id}>
44+
<td>{contact.id}</td>
45+
<td>{contact.name}</td>
46+
</tr>
47+
)}
48+
</tbody>
49+
</table>;
50+
}
51+
}
52+
53+
interface Contact {
54+
id: number;
55+
name: string;
56+
address: string;
57+
city: string;
58+
state: string;
59+
postalCode: string;
60+
phone: string;
61+
email: string;
62+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as React from 'react';
2+
import { RouteComponentProps } from 'react-router';
3+
4+
interface CounterState {
5+
currentCount: number;
6+
}
7+
8+
export class Counter extends React.Component<RouteComponentProps<{}>, CounterState> {
9+
constructor() {
10+
super();
11+
this.state = { currentCount: 0 };
12+
}
13+
14+
public render() {
15+
return <div>
16+
<h1>Counter</h1>
17+
18+
<p>This is a simple example of a React component.</p>
19+
20+
<p>Current count: <strong>{ this.state.currentCount }</strong></p>
21+
22+
<button onClick={ () => { this.incrementCounter() } }>Increment</button>
23+
</div>;
24+
}
25+
26+
incrementCounter() {
27+
this.setState({
28+
currentCount: this.state.currentCount + 1
29+
});
30+
}
31+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import * as React from 'react';
2+
import { RouteComponentProps } from 'react-router';
3+
import 'isomorphic-fetch';
4+
5+
interface FetchDataExampleState {
6+
forecasts: WeatherForecast[];
7+
loading: boolean;
8+
}
9+
10+
export class FetchData extends React.Component<RouteComponentProps<{}>, FetchDataExampleState> {
11+
constructor() {
12+
super();
13+
this.state = { forecasts: [], loading: true };
14+
15+
fetch('api/SampleData/WeatherForecasts')
16+
.then(response => response.json() as Promise<WeatherForecast[]>)
17+
.then(data => {
18+
this.setState({ forecasts: data, loading: false });
19+
});
20+
}
21+
22+
public render() {
23+
let contents = this.state.loading
24+
? <p><em>Loading...</em></p>
25+
: FetchData.renderForecastsTable(this.state.forecasts);
26+
27+
return <div>
28+
<h1>Weather forecast</h1>
29+
<p>This component demonstrates fetching data from the server.</p>
30+
{ contents }
31+
</div>;
32+
}
33+
34+
private static renderForecastsTable(forecasts: WeatherForecast[]) {
35+
return <table className='table'>
36+
<thead>
37+
<tr>
38+
<th>Date</th>
39+
<th>Temp. (C)</th>
40+
<th>Temp. (F)</th>
41+
<th>Summary</th>
42+
</tr>
43+
</thead>
44+
<tbody>
45+
{forecasts.map(forecast =>
46+
<tr key={ forecast.dateFormatted }>
47+
<td>{ forecast.dateFormatted }</td>
48+
<td>{ forecast.temperatureC }</td>
49+
<td>{ forecast.temperatureF }</td>
50+
<td>{ forecast.summary }</td>
51+
</tr>
52+
)}
53+
</tbody>
54+
</table>;
55+
}
56+
}
57+
58+
interface WeatherForecast {
59+
dateFormatted: string;
60+
temperatureC: number;
61+
temperatureF: number;
62+
summary: string;
63+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from 'react';
2+
import { RouteComponentProps } from 'react-router';
3+
4+
export class Home extends React.Component<RouteComponentProps<{}>, {}> {
5+
public render() {
6+
return <div>
7+
<h1>Hello, world!</h1>
8+
<p>Welcome to your new single-page application, built with:</p>
9+
<ul>
10+
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
11+
<li><a href='https://facebook.github.io/react/'>React</a> and <a href='http://www.typescriptlang.org/'>TypeScript</a> for client-side code</li>
12+
<li><a href='https://webpack.github.io/'>Webpack</a> for building and bundling client-side resources</li>
13+
<li><a href='http://getbootstrap.com/'>Bootstrap</a> for layout and styling</li>
14+
</ul>
15+
<p>To help you get started, we've also set up:</p>
16+
<ul>
17+
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
18+
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
19+
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, rebuilt React components will be injected directly into your running application, preserving its live state.</li>
20+
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>
21+
</ul>
22+
<h4>Going further</h4>
23+
<p>
24+
For larger applications, or for server-side prerendering (i.e., for <em>isomorphic</em> or <em>universal</em> applications), you should consider using a Flux/Redux-like architecture.
25+
You can generate an ASP.NET Core application with React and Redux using <code>dotnet new reactredux</code> instead of using this template.
26+
</p>
27+
</div>;
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as React from 'react';
2+
import { NavMenu } from './NavMenu';
3+
4+
export interface LayoutProps {
5+
children?: React.ReactNode;
6+
}
7+
8+
export class Layout extends React.Component<LayoutProps, {}> {
9+
public render() {
10+
return <div className='container-fluid'>
11+
<div className='row'>
12+
<div className='col-sm-3'>
13+
<NavMenu />
14+
</div>
15+
<div className='col-sm-9'>
16+
{ this.props.children }
17+
</div>
18+
</div>
19+
</div>;
20+
}
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as React from 'react';
2+
import { Link, NavLink } from 'react-router-dom';
3+
4+
export class NavMenu extends React.Component<{}, {}> {
5+
public render() {
6+
return <div className='main-nav'>
7+
<div className='navbar navbar-inverse'>
8+
<div className='navbar-header'>
9+
<button type='button' className='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
10+
<span className='sr-only'>Toggle navigation</span>
11+
<span className='icon-bar'></span>
12+
<span className='icon-bar'></span>
13+
<span className='icon-bar'></span>
14+
</button>
15+
<Link className='navbar-brand' to={ '/' }>React</Link>
16+
</div>
17+
<div className='clearfix'></div>
18+
<div className='navbar-collapse collapse'>
19+
<ul className='nav navbar-nav'>
20+
<li>
21+
<NavLink to={ '/' } exact activeClassName='active'>
22+
<span className='glyphicon glyphicon-home'></span> Home
23+
</NavLink>
24+
</li>
25+
<li>
26+
<NavLink to={ '/counter' } activeClassName='active'>
27+
<span className='glyphicon glyphicon-education'></span> Counter
28+
</NavLink>
29+
</li>
30+
<li>
31+
<NavLink to={ '/fetchdata' } activeClassName='active'>
32+
<span className='glyphicon glyphicon-th-list'></span> Fetch data
33+
</NavLink>
34+
</li>
35+
<li>
36+
<NavLink to={'/contactlist'} activeClassName='active'>
37+
<span className='glyphicon glyphicon-th-list-alt'></span> Contact List
38+
</NavLink>
39+
</li>
40+
</ul>
41+
</div>
42+
</div>
43+
</div>;
44+
}
45+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
.main-nav li .glyphicon {
2+
margin-right: 10px;
3+
}
4+
5+
/* Highlighting rules for nav menu items */
6+
.main-nav li a.active,
7+
.main-nav li a.active:hover,
8+
.main-nav li a.active:focus {
9+
background-color: #4189C7;
10+
color: white;
11+
}
12+
13+
/* Keep the nav menu independent of scrolling and on top of other items */
14+
.main-nav {
15+
position: fixed;
16+
top: 0;
17+
left: 0;
18+
right: 0;
19+
z-index: 1;
20+
}
21+
22+
@media (max-width: 767px) {
23+
/* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
24+
body {
25+
padding-top: 50px;
26+
}
27+
}
28+
29+
@media (min-width: 768px) {
30+
/* On small screens, convert the nav menu to a vertical sidebar */
31+
.main-nav {
32+
height: 100%;
33+
width: calc(25% - 20px);
34+
}
35+
.main-nav .navbar {
36+
border-radius: 0px;
37+
border-width: 0px;
38+
height: 100%;
39+
}
40+
.main-nav .navbar-header {
41+
float: none;
42+
}
43+
.main-nav .navbar-collapse {
44+
border-top: 1px solid #444;
45+
padding: 0px;
46+
}
47+
.main-nav .navbar ul {
48+
float: none;
49+
}
50+
.main-nav .navbar li {
51+
float: none;
52+
font-size: 15px;
53+
margin: 6px;
54+
}
55+
.main-nav .navbar li a {
56+
padding: 10px 16px;
57+
border-radius: 4px;
58+
}
59+
.main-nav .navbar a {
60+
/* If a menu item's text is too long, truncate it */
61+
width: 100%;
62+
white-space: nowrap;
63+
overflow: hidden;
64+
text-overflow: ellipsis;
65+
}
66+
}

0 commit comments

Comments
 (0)