Skip to content

Commit 39f7f16

Browse files
Add example of server and client validation for React
1 parent ef7e136 commit 39f7f16

File tree

8 files changed

+113
-3
lines changed

8 files changed

+113
-3
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Threading.Tasks;
3+
using Microsoft.AspNet.Mvc;
4+
5+
namespace ReactExample.Controllers
6+
{
7+
public class PeopleApiController : Controller
8+
{
9+
[HttpPut("api/people/{personId:int}")]
10+
public async Task<ActionResult> UpdatePerson([FromBody] PersonDto person)
11+
{
12+
if (!ModelState.IsValid) {
13+
return HttpBadRequest(ModelState);
14+
} else {
15+
return new HttpOkResult();
16+
}
17+
}
18+
}
19+
20+
public class PersonDto {
21+
public string name { get; set; }
22+
public string city { get; set; }
23+
public string state { get; set; }
24+
public string country { get; set; }
25+
public string company { get; set; }
26+
27+
[Range(1, 10)]
28+
public int favoriteNumber { get; set; }
29+
}
30+
}

samples/react/ReactGrid/ReactApp/components/PeopleGrid.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import Griddle from 'griddle-react';
33
import { CustomPager } from './CustomPager.jsx';
44
import { fakeData } from '../data/fakeData.js';
5-
import { columnMeta } from '../data/columnMeta.js';
5+
import { columnMeta } from '../data/columnMeta.jsx';
66
const resultsPerPage = 10;
77

88
export class PeopleGrid extends React.Component {
@@ -13,6 +13,7 @@ export class PeopleGrid extends React.Component {
1313
<h1>People</h1>
1414
<div id="table-area">
1515
<Griddle results={fakeData}
16+
columns={columnMeta.map(x => x.columnName)}
1617
columnMetadata={columnMeta}
1718
resultsPerPage={resultsPerPage}
1819
tableClassName="table"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from 'react';
2+
import Formsy from 'formsy-react';
3+
import { Input } from 'formsy-react-components';
4+
import { fakeData } from '../data/fakeData.js';
5+
6+
export class PersonEditor extends React.Component {
7+
constructor() {
8+
super();
9+
this.state = { savedChanges: false };
10+
}
11+
12+
onChange() {
13+
this.setState({ savedChanges: false });
14+
}
15+
16+
submit(model, reset, setErrors) {
17+
PersonEditor.sendJson('put', `/api/people/${ this.props.params.personId }`, model).then(response => {
18+
if (response.ok) {
19+
this.setState({ savedChanges: true });
20+
} else {
21+
// Parse server-side validation errors from the response and display them
22+
response.json().then(setErrors);
23+
}
24+
});
25+
}
26+
27+
render() {
28+
var personId = parseInt(this.props.params.personId);
29+
var person = fakeData.filter(p => p.id === personId)[0];
30+
var notificationBox = this.state.savedChanges
31+
&& <div className="alert alert-success"><b>Done!</b> Your changes were saved.</div>;
32+
33+
return <div className='row'>
34+
<div className='page-header'>
35+
<h1>Edit { person.name }</h1>
36+
</div>
37+
<Formsy.Form ref='form' className='form-horizontal' onChange={ this.onChange.bind(this) } onValidSubmit={ this.submit.bind(this) }>
38+
<Input name='name' label='Name' value={ person.name } required />
39+
<Input name='city' label='City' value={ person.city } required />
40+
<Input name='state' label='State' value={ person.state } required />
41+
<Input name='country' label='Country' value={ person.country } required />
42+
<Input name='company' label='Company' value={ person.company } required />
43+
<Input name='favoriteNumber' label='Favorite Number' value={ person.favoriteNumber } required validations="isInt" validationError="Must be a number" />
44+
{ notificationBox }
45+
<button type='submit' className='btn btn-primary'>Save</button>
46+
</Formsy.Form>
47+
</div>;
48+
}
49+
50+
static sendJson(method, url, object) {
51+
return fetch(url, {
52+
method: method,
53+
headers: { 'Content-Type': 'application/json' },
54+
body: JSON.stringify(object)
55+
});
56+
}
57+
}

samples/react/ReactGrid/ReactApp/components/ReactApp.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import React from 'react';
22
import { Router, Route } from 'react-router';
33
import { PeopleGrid } from './PeopleGrid.jsx';
4+
import { PersonEditor } from './PersonEditor.jsx';
45

56
export default class ReactApp extends React.Component {
67
render() {
78
return (
89
<Router history={this.props.history}>
910
<Route path="/" component={PeopleGrid} />
1011
<Route path="/:pageIndex" component={PeopleGrid} />
12+
<Route path="/edit/:personId" component={PersonEditor} />
1113
</Router>
1214
);
1315
}

samples/react/ReactGrid/ReactApp/data/columnMeta.js renamed to samples/react/ReactGrid/ReactApp/data/columnMeta.jsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import React from 'react';
2+
import { Link } from 'react-router';
3+
4+
class RowActionsComponent extends React.Component {
5+
render() {
6+
return <Link to={'/edit/' + this.props.rowData.id}>Edit</Link>;
7+
}
8+
}
9+
110
var columnMeta = [
211
{
312
"columnName": "id",
@@ -40,6 +49,13 @@ var columnMeta = [
4049
"order": 7,
4150
"locked": false,
4251
"visible": true
52+
},
53+
{
54+
"columnName": "actions",
55+
"order": 8,
56+
"locked": true,
57+
"visible": true,
58+
"customComponent": RowActionsComponent
4359
}
4460
];
4561

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div id="react-app" asp-react-prerender-module="ReactApp/components/ReactApp.jsx"></div>
22

33
@section scripts {
4-
<script src="bundle.js"></script>
4+
<script src="/bundle.js"></script>
55
}

samples/react/ReactGrid/Views/Shared/_Layout.cshtml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
<link rel="stylesheet" href="/main.css" />
77
</head>
88
<body>
9-
@RenderBody()
9+
<div class="container">
10+
@RenderBody()
11+
</div>
1012
@RenderSection("scripts", required: false)
1113
</body>
1214
</html>

samples/react/ReactGrid/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
"dependencies": {
55
"babel-core": "^5.8.29",
66
"bootstrap": "^3.3.5",
7+
"formsy-react": "^0.17.0",
8+
"formsy-react-components": "^0.6.3",
79
"griddle-react": "^0.2.14",
810
"history": "^1.12.6",
911
"react": "^0.14.0",

0 commit comments

Comments
 (0)