Replacing Backbone.js views with React.js views in a ASP.NET MVC 5 Application and using ASP.NET WEB API 2 as a RESTful service for Backbone.js
Overview
RESTful service: ASP.NET Web API can be used for building RESTful services on top of the .NET Framework.
Maintainable client side code with RESTful persistence: Backbone.js provides structure to client side code. Backbone.js makes client side code more maintainable in the long term. Backbone.js supports RESTful persistence through its fetch() and create() methods on Collections and fetch(), save(), and destroy() methods on Models.
Efficient DOM updates with component based structure: React.js library deals with the view layer of the application & provides a component based structure to the application. In React.js, each component decides how it should be rendered. React.js efficiently updates and render just the right components when data changes.
News Application using ASP.NET MVC 5, ASP.NET WEB API 2 (RESTful service), Backbone.js (RESTful persistence) & React.js (view layer)
The following code demonstrates how we can use Backbone.js models & collections along with React.js views. Also, in this application, ASP.NET WEB API 2 is used as a RESTful service for Backbone.js.
Step 1: Create a ASP.NET WEB API 2 service (RESTful service) which will be used by Backbone.js. This will be used to perform CRUD operations.
News Model:
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace SampleApplication.Models { public class News { public int Id { get; set; } public string Headline { get; set; } public string Detail { get; set; } } }
News Web API for performing CRUD operations:
using SampleApplication.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace SampleApplication.Controllers { public class NewsApiController : ApiController { private static List<News> newsList; static NewsApiController() { newsList = new List<News> { new News { Id= 1, Headline = "Nice terror attack LIVE", Detail = "The attack that took place..." }, new News { Id= 2, Headline = "Truck 'terror' in France's Nice", Detail = "A terrorist gunman killed..." }, new News { Id= 3, Headline = "Shocking footages from terror-struck Nice", Detail = "Nice: Who knew France`s national day celebrations..." }, new News { Id= 4, Headline = "Congress names Sheila Dikshit as chief ministerial candidate", Detail = "New Delhi: Ending speculation, the Congress announced..." }, new News { Id= 5, Headline = "Pokemon Go: This is why you should strictly avoid playing this game", Detail = "New Delhi: Pokémon Go, a mobile game has rocked..." } }; } // GET: api/NewsApi public IEnumerable<News> Get() { // Modifying first headline to include timestamp. newsList.First().Headline = $"React.js In Action: {DateTime.Now.ToString("yyyyMMddHHmmssfff")}"; return newsList; } // GET: api/NewsApi/5 public News Get(int id) { return newsList.Find(p => p.Id == id); } // POST: api/NewsApi public void Post(News news) { news.Id = newsList.Count + 1; newsList.Add(news); } // PUT: api/NewsApi/5 public void Put(int id, News news) { var newsToUpdate = newsList.FirstOrDefault(x => x.Id == id); if (newsToUpdate != null) { newsToUpdate.Headline = news.Headline; newsToUpdate.Detail = news.Detail; } } // DELETE: api/NewsApi/5 public void Delete(int id) { newsList.RemoveAll(x => x.Id == id); } } }
Step 2: Install following Nuget packages in ASP.NET MVC 5 Application:
Install Backbone.js, react.js, React.Web Nuget packages
Step 3: Create Backbone.js model & collection:
Folder structure
Backbone.js model – newsItem.js
var app = app || {}; $(function () { 'use strict'; app.NewsItemModel = Backbone.Model.extend({ urlRoot: '/api/NewsApi', idAttribute: "Id", defaults: { date: new Date().toDateString() }, parse: function (data) { return data; } }); });
Backbone.js collection – newsList.js
var app = app || {}; $(function () { 'use strict'; app.NewsCollection = Backbone.Collection.extend({ model: app.NewsItemModel, url: '/api/NewsApi', parse: function (data) { return data; } }); });
Step 4: Replacing Backbone.js views with React.js views:
React.js views – NewsListing.jsx
var app = app || {}; $(function () { 'use strict'; var DisplaySingleNews = React.createClass({ render: function () { return ( <div className="news" style={{height: 150}}> <h2>{this.props.data.Headline}</h2> <div>{this.props.data.Detail}</div> </div> ); } }); var DisplayNewsList = React.createClass({ render: function () { var newsList = this.props.data.map(function (news) { return ( <DisplaySingleNews key={news.Id} data={news}> </DisplaySingleNews> ); }); return ( <div className="newsList"> {newsList} </div> ); } }); var NewsForm = React.createClass({ getInitialState: function () { return { Headline: '', Detail: '' }; }, handleHeadlineChange: function (e) { this.setState({ Headline: e.target.value }); }, handleDetailChange: function (e) { this.setState({ Detail: e.target.value }); }, handleSubmit: function (e) { e.preventDefault(); var headline = this.state.Headline.trim(); var detail = this.state.Detail.trim(); if (!detail || !headline) { return; } this.props.onNewsSubmit({ Headline: headline, Detail: detail }); this.setState({ Headline: '', Detail: '' }); }, render: function () { return ( <div className="form-group"> <form onSubmit={this.handleSubmit}> <textarea value={this.state.Headline} onChange={this.handleHeadlineChange} className="form-control" rows="5" type="text" id="Headline" placeholder="Headline" ref="headline" /> <textarea value={this.state.Detail} onChange={this.handleDetailChange} className="form-control" rows="5" type="text" id="Detail" placeholder="Details..." ref="text" /> <input type="submit" className="btn btn-success addnew" value="Add News" /> </form> </div> ); } }); app.NewsControl = React.createClass({ getInitialState: function () { return { data: [] }; }, loadNewsListFromServer: function () { var that = this; var newsCollection = new app.NewsCollection(); newsCollection.fetch({ success: function (response) { that.setState({ data: response.toJSON() }); } }); }, handleNewsSubmit: function (news) { var that = this; var newsModel = new app.NewsItemModel(news); newsModel.save({}, { success: function (model, respose, options) { that.loadNewsListFromServer(); }, error: function (model, xhr, options) { console.log("Save Failed."); } }); }, componentDidMount: function () { window.setInterval(this.loadNewsListFromServer, this.props.pollInterval); this.setState({ data: this.props.collection }); }, render: function () { return ( <div> <DisplayNewsList data={this.state.data} /> <div className="panel panel-default"> <div className="panel-heading">Post News</div> <div id="newsListing" className="panel-body"> <NewsForm onNewsSubmit={this.handleNewsSubmit}/> </div> </div> </div> ); } }); app.LatestNewsUpdate = React.createClass({ getInitialState: function () { return { data: { Headline: '', Detail: '' } }; }, loadLatestNewsFromServer: function () { var that = this; var randomId = Math.floor((Math.random() * 5) + 1); var newsModel = new app.NewsItemModel({ Id: randomId }); newsModel.fetch({ success: function (response) { that.setState({ data: response.toJSON() }); } }); }, componentDidMount: function () { window.setInterval(this.loadLatestNewsFromServer, this.props.pollInterval); this.setState({ data: this.props.model }); }, render: function () { return ( <DisplaySingleNews data={this.state.data} /> ); } }); });
news-view.js
var app = app || {}; $(function () { 'use strict'; app.LatestNewsUpdateView = Backbone.View.extend({ el: '#latestNewsUpdate', initialize: function () { }, render: function (response) { this.reactView = React.createElement(app.LatestNewsUpdate, { model: response, pollInterval: 4000 }); ReactDOM.unmountComponentAtNode(this.el); ReactDOM.render(this.reactView, this.el); } }); app.NewsControlView = Backbone.View.extend({ el: '#newsListing', initialize: function () { }, render: function (response) { this.reactView = React.createElement(app.NewsControl, { collection: response, submitUrl: this.submitUrl, pollInterval: 2000 }); ReactDOM.unmountComponentAtNode(this.el); ReactDOM.render(this.reactView, this.el); } }); });
Step 5: app.js contents
var app = app || {}; $(function () { 'use strict'; var randomId = Math.floor((Math.random() * 5) + 1); var newsModel = new app.NewsItemModel({ Id: randomId }); newsModel.fetch({ success: function (response) { var latestNewsUpdateView = new app.LatestNewsUpdateView({ model: newsModel }); latestNewsUpdateView.render(response.toJSON()); } }); var newsCollection = new app.NewsCollection(); newsCollection.fetch({ success: function (response) { var newsControlView = new app.NewsControlView({ collection: newsCollection }); newsControlView.render(response.toJSON()); } }); });
Step 6: ASP.NET MVC 5 Application, setting up Controller & Views:
Home Controller:
using SampleApplication.ProcessUtility; using System; using System.Web.Mvc; namespace SampleApplication.Controllers { public class HomeController : Controller { public ActionResult News() { return View(); } } }
News.cshtml view under “Views/Home” directory:
@{ ViewBag.Title = "News Application"; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <link href="@Url.Content("~/Content/bootstrap.min.css")" rel="stylesheet" /> <title>News Application - TechCartNow.com</title> </head> <body> <section class="main-content" style="width:90%; align-content:center; margin-left:25px;"> <h1>News Application</h1> <div class="panel panel-default"> <div class="panel-heading">Latest Update</div> <div id="latestNewsUpdate" class="panel-body"></div> </div> <div class="panel panel-default"> <div class="panel-heading">News List</div> <div id="newsListing" class="panel-body"></div> </div> </section> @section Scripts{ <script src="@Url.Content("~/scripts/underscore.js")"></script> <script src="@Url.Content("~/scripts/backbone.js")"></script> <script src="@Url.Content("~/scripts/react/react.js")"></script> <script src="@Url.Content("~/scripts/react/react-dom.min.js")"></script> <script src="@Url.Content("~/scripts/news-app/backbone/models/newsItem.js")"></script> <script src="@Url.Content("~/scripts/news-app/backbone/collections/newsList.js")"></script> <script src="@Url.Content("~/scripts/news-app/react-components/NewsListing.jsx")"></script> <script src="@Url.Content("~/scripts/news-app/backbone/views/news-view.js")"></script> <script src="@Url.Content("~/scripts/news-app/backbone/app.js")"></script> } </body> </html>
Step 7: React in action in React Developer Tools
That’s All.
.NET Professional | Microsoft Certified Professional | DZone’s Most Valuable Blogger | Web Developer | Author | Blogger
Doctorate in Computer Science and Engineering
Microsoft Certified Professional (MCP) with over 12+ years of software industry experience including development, implementation & deployment of applications in the .NET framework
Experienced and skilled Agile Developer with a strong record of excellent teamwork, successful coding & project management. Specialises in problem identification and proposal of alternative solutions. Provided knowledge and individual mentoring to team members as needed
Among top 3% overall in terms of contribution on Stack Overflow (~2.3 million people reached my posts). Part of the top 1% Stack Overflow answerers in ASP.NET technology.
DZone’s Most Valuable Blogger (MVB)
Created and actively maintain the TechCartNow.com tech blog while also editing, writing, and researching topics for publication.
Excellent skills in Application Development using C#/Vb.Net, .NET Framework, ASP.NET, MVC, ADO.NET, WCF, WPF, Web API, SQL Server, jQuery, Angular, React, BackboneJS