A few days back I was trying to create an editable grid using ReactJs with Mvc.net and nodeJs but didn't find any good working examples on this topic. So I had to struggle a lot to make it work. Then this blog's idea came into my mind, in this blog I will try to provide the exact steps to create an editable grid with ReactJs using Mvc.net.
software Requirement/prerequisites: Visual studio, NodeJs
Steps:
1. Create a new project and select MVC as shown in the screenshot
Now click on the Create button and Now select MVC
Now with the above steps default MVC application is created.
2. Now Add DataBase in App_Data folder or skip this step if you already
have a DataBase.
Now add employee table or any table in the DB
3. Now generate Entity Framework entities using the above DB.
4. Now create 'react' name folder under scripts folder and open this react folder in window explorer and open power shell command
and run the below commands to install the ReactJs, WebPack and
React-Bootstrap-table-next (grid)
npm install --save-dev react react-dom
npm install --save-dev webpack-cli
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/polyfill webpack
npm install react-bootstrap-table-next --save
5. Now add the following files in React Folder
a) index.js
import React from 'react';
import ReactDOM from 'react-dom';
import EmployeeList from './employeeList'
class Root extends React.Component {
constructor(props) {
super(props);
this.state = { statuses: [] };
}
render() {
return <EmployeeList valueFromParent={this.state.statuses} />;
}
}
ReactDOM.render(<Root />, document.getElementById('root'));
b) employeeList.js
import React, { Component } from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
{
dataField: 'Name',
text: 'Name',
editable: false,
style: {
backgroundColor: '#D3D3D3'
}
}, {
dataField: 'Address',
text: 'Address',
editable: false,
style: {
backgroundColor: '#D3D3D3'
}
}, {
dataField: 'Age',
text: 'Age',
editable: false
}];
class EmployeeList extends Component {
constructor(props) {
super(props);
this.state = { appMasterList: [] };
}
loadDataFromServer() {
fetch('/api/Employee/').then(res => res.json()).then(
(result) => {
window.results = result;
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}
loadData = () => {
this.loadDataFromServer();
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <button className="btn btn-primary" onClick={this.loadData}>Load Data</button>;
} else {
return (
<div>
<button className="btn btn-primary" onClick={this.loadData}>Refresh</button>
<BootstrapTable keyField='Name' data={items} columns={columns} />
</div>
);
}
}
}
export default EmployeeList;
c) package.json
{
"name": "ReactWithMvc",
"description": "Edtitable grid using React",
"main": "index.js",
"scripts": {
"webpack": "webpack"
},
"author": "CodingDekho",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/polyfill": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.4",
"babel-loader": "^8.1.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
},
"dependencies": {
"react-bootstrap-table-next": "^4.0.3"
}
}
d) webpack.config.js
module.exports = {
mode: 'development',
context: __dirname,
entry: "./index.js",
output: {
path: __dirname + "/dist",
filename: "bundle.js"
},
watch: true,
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/env", "@babel/react"],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
}
}
]
}
}
e) add a dist folder
6. Now your folder and file structure will be something like
below
7. Now add EmployeeController (WebApi)
public class EmployeeController : ApiController
{
// GET: api/Employee
public IEnumerable<Employee> Get()
{
return new TestDbEntities().Employees.ToList();
}
}
8. Now add webapiConfig.cs file in App_Start folder
code:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
9. Now change global.asax.cs file as following:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
10. Now edit the _Layout.cshtml files and add the following lines
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/react/dist/bundle.js"></script>
11. Now go to index.cshtml file and replace the code with below code:
@{
ViewBag.Title = "Index";
}
<div id="root"></div>
12. Now in the command prompt run below command for web pack
npm run webpack
with this command, the js code will be complied and will generate a bundle file under dist folder..
13. Now run the project and click on the Load Data button and observe the data loaded in the grid...
Result:
now go to part 2, to see the editing code changes.
Please let us know if you have any questions or any suggestions.
Thanks
Coding Dekho :)
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
ReplyDelete{
FieldBuilder fieldBuilder = tb.DefineField(Convert.ToString("_") + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod(Convert.ToString("get_") + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr = tb.DefineMethod(Convert.ToString("set_") + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
public static Type CompileResultType(DataTable dt, string TypeName)
ReplyDelete{
TypeBuilder tb = GetTypeBuilder(TypeName);
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
foreach (DataColumn column in dt.Columns)
CreateProperty(tb, column.ColumnName, column.DataType);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder(string TypeName)
{
var typeSignature = TypeName;
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, null);
return tb;
}
public static void FillProperties(object SenderObject, DataRow Row)
ReplyDelete{
foreach (DataColumn Column in Row.Table.Columns)
{
PropertyInfo ObjectProperty = SenderObject.GetType().GetProperty(Column.ColumnName);
if (IsDBNothing(Row(Column.ColumnName)) && Column.DataType == typeof(string))
ObjectProperty.SetValue(SenderObject, IsDBNothing(Row(Column.ColumnName), Column.ColumnName + "- N/A"), null/* TODO Change to default(_) if this is not a reference type */);
else
ObjectProperty.SetValue(SenderObject, IsDBNothing(Row(Column.ColumnName), null/* TODO Change to default(_) if this is not a reference type */), null/* TODO Change to default(_) if this is not a reference type */);
}
}
public static bool IsDBNothing(object ValueToTest)
{
return Information.IsNothing(ValueToTest) || Information.IsDBNull(ValueToTest);
}
public static object IsDBNothing(object ValueToTest, object RetValIfIsDBNothing)
{
if (IsDBNothing(ValueToTest))
return RetValIfIsDBNothing;
else
return ValueToTest;
}