Click here to Skip to main content
15,893,588 members
Articles / Web Development / ASP.NET

Single Page CRUD Application (SPA) using ASP.NET MVC, Entity Framework and AngularJS

Rate me:
Please Sign up or sign in to vote.
4.38/5 (6 votes)
23 Nov 2015CPOL1 min read 32.9K   28   5
Single page CRUD application (SPA) using ASP.NET MVC, Entity Framework and AngularJS

In this article, I will give you the basics on creating Single Page CRUD Application (SPA) using ASP.NET MVC, Entity Framework and AngularJS.

Below is the project structure of the below explained application.

Image 1

Below are the screen prints of the below explained application.

Image 2

Image 3

Click on respective tab to view the code underneath.

Create the Models and Database Context

Under the Models folder, create the Post.cs, PostTags.cs and Tags (Db Entity).

C#
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace dotnetfundaAngularJS.Models
{
  public class Post
  {
    public Post()
    {
      this.postTags = new HashSet<PostTags>();
    }
    [Key]
    public Int64 PostId { get; set; }
    public string Author { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime LastmodifiedDate { get; set; }
    public virtual ICollection<PostTags> postTags { get; set; }
    public string Status { get; set; }
  }
}
C#
using System;
using System.ComponentModel.DataAnnotations;
namespace dotnetfundaAngularJS.Models
{
  public class PostTags
  {
    [Key]
    public Int64 PostTagId { get; set; }
    public Int64 TagId { get; set; }
    public Int64 PostId { get; set; }
    public virtual Post Post { get; set; }
    public virtual Tags Tag { get; set; }
  }
}
C#
using System;
using System.ComponentModel.DataAnnotations;
namespace dotnetfundaAngularJS.Models
{
  public class Tags
  {
    [Key]
    public Int64 TagId { get; set; }
    public string Tag { get; set; }
  }
}
C#
using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace dotnetfundaAngularJS.Models
{
  public class PostModel
  {
    public Int64 PostId { get; set; }
    [AllowHtml]
    [Required(ErrorMessage = "Title is required")]
    public string Title { get; set; }
    [AllowHtml]
    [Required(ErrorMessage = "Content is required")]
    public string Content { get; set; }
    [Display(Name = "Tags (Comma ',' Seperated)")]
    [Required(ErrorMessage = "Tags is/are required")]
    public string Tags { get; set; }
    public string Author { get; set; }
    public DateTime CreatedDate { get; set; }
  }
}

Under the Models folder, create dotnetfundaDbContext.cs (DbContext class)

C#
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace dotnetfundaAngularJS.Models
{
  public class dotnetfundaDbContext : DbContext
  {
    public dotnetfundaDbContext()
      : base("name=dotnetfundaDbContext")
    {
      Database.SetInitializer<dotnetfundaDbContext>(null);
    }
    public DbSet<Post> posts { get; set; }
    public DbSet<Tags> tags { get; set; }
    public DbSet<PostTags> posttags { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
      modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>();
    }
  }
}

Under the Models folder, create Utilities.cs (Utilities class)

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dotnetfundaAngularJS.Models
{
  public class Utilities
  {
    public static string ConvertCollectionToString(ICollection<Tags> tags)
    {
      StringBuilder builder = new StringBuilder();
      foreach (var tag in tags)
      {
        builder.Append(tag.Tag);
        builder.Append(',');
      }
      return builder.ToString();
    }
    public static List<PostTags> ConvertToCollection(PostModel model)
    {
      IRepository<Tags> tagrepository = new Repository<Tags>();
      List<PostTags> tag = new List<PostTags>();
      string[] temptag = model.Tags.Split(';');
      //var TagId = String.Empty;
      foreach (string tg in temptag)
      {
        var tags = tagrepository.SelectAll().Where(t => t.Tag == tg);
        foreach (var t in tags)
        {
          var exist = tag.Where(p => p.TagId == Convert.ToInt64(t.TagId)).ToList();
          if (exist.Count() == 0)
          {
            var taG = new PostTags
            {
              TagId = Convert.ToInt64(t.TagId)
            };
            tag.Add(taG);
          }
        }
      }
      return tag;
    }
  }
}

Create Generic Repository

Generic Repository Interface of the type class can be created as follows. If a particular repository requires additional operations, it can extend the generic repository interface.

C#
using System.Collections.Generic;
namespace dotnetfundaAngularJS.Models
{
  public interface IRepository<T> where T : class
  {
    IEnumerable<T> SelectAll();
    T SelectByID(object id);
    void Insert(T obj);
    void Update(T obj);
    void Delete(object id);
    bool Save();
  }
}

Repository.cs (Repository class)

C#
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace dotnetfundaAngularJS.Models
{
  public class Repository<T> : IRepository<T> where T : class
  {
    private dotnetfundaDbContext db = null;
    private DbSet<T> table = null;
    public Repository()
    {
      this.db = new dotnetfundaDbContext();
      table = db.Set<T>();
    }
    public Repository(dotnetfundaDbContext db)
    {
      this.db = db;
      table = db.Set<T>();
    }
    public IEnumerable<T> SelectAll()
    {
      try
      {
        return table.ToList();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
    public T SelectByID(object id)
    {
      try
      {
        return table.Find(id);
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
    public void Insert(T obj)
    {
      try
      {
        table.Add(obj);
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
    public void Update(T obj)
    {
      try
      {
        table.Add(obj);
        db.Entry(obj).State = EntityState.Modified;
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
    public void Delete(object id)
    {
      try
      {
        T existing = table.Find(id);
        table.Remove(existing);
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
    public bool Save()
    {
      try
      {
        db.SaveChanges();
        return true;
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }
}

Create Controller

Create Post Controller

C#
using dotnetfundaAngularJS.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Mvc;
namespace dotnetfundaAngularJS.Controllers
{
  public class PostController : Controller
  {
    private IRepository<Post> repository = null;
    private IRepository<Tags> tagrepository = null;
    private IRepository<PostTags> postagrepository = null;
    private bool Success = false;
    public PostController()
    {
      this.repository = new Repository<Post>();
      this.tagrepository = new Repository<Tags>();
      this.postagrepository = new Repository<PostTags>();
    }
    public PostController(IRepository<Post> repository,
    IRepository<Tags> tagrepository, IRepository<PostTags> postagrepository)
    {
      this.repository = repository;
      this.tagrepository = tagrepository;
      this.postagrepository = postagrepository;
    }
    public JsonResult PostList()
    {
      List<PostModel> postTags = new List<PostModel>();
      string tags = string.Empty;
      try
      {
        var posts = repository.SelectAll().ToList();
        foreach (Post post in posts)
        {
          var allpostags = postagrepository.SelectAll().Where(p => p.PostId == post.PostId);
          foreach(var postag in allpostags)
          {
            var tagName = tagrepository.SelectAll().Where(t => t.TagId == postag.TagId).ToList();
            foreach (var tag in tagName)
            {
              tags = tag.Tag + ";" + tags;
            }
          }
          postTags.Add(new PostModel { PostId = post.PostId,Title=post.Title,
          Content=post.Content,Tags = tags,Author = post.Author,CreatedDate=post.CreatedDate });
          tags = string.Empty;
        }
        return Json(postTags, JsonRequestBehavior.AllowGet);
      }
      catch (Exception e)
      {
      }
      return Json(postTags, JsonRequestBehavior.AllowGet);
    }
    public JsonResult New()
    {
      PostModel postModel = new PostModel();
      return Json(postModel, JsonRequestBehavior.AllowGet);
    }
    public ActionResult NewPost(PostModel model)
    {
      try
      {
        var post = new Post
        {
          Author = "Himen Patel",
          Title = model.Title,
          Content = model.Content,
          CreatedDate = DateTime.UtcNow,
          postTags = Utilities.ConvertToCollection(model),
        };
        repository.Insert(post);
        Success = repository.Save();
        return new HttpStatusCodeResult(HttpStatusCode.Created); // OK = 200
      }
      catch
      {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
    }
    public JsonResult Edit(string id)
    {
      PostModel postTags = new PostModel();
      string tags = string.Empty;
      try
      {
        var posts = repository.SelectAll().Where(p => p.PostId == Convert.ToInt64(id)).ToList();
        foreach (Post post in posts)
        {
          var allpostags = postagrepository.SelectAll().Where(p => p.PostId == post.PostId);
          foreach (var postag in allpostags)
          {
            var tagName = tagrepository.SelectAll().Where(t => t.TagId == postag.TagId).ToList();
            foreach (var tag in tagName)
            {
              tags = tag.Tag + ";" + tags;
            }
          }
          postTags.Title = post.Title;
          postTags.Content = post.Content;
          postTags.Tags = tags;
          postTags.PostId = post.PostId;
          tags = string.Empty;
        }
        return Json(postTags, JsonRequestBehavior.AllowGet);
      }
      catch(Exception ex)
      {
      }
      return Json(postTags, JsonRequestBehavior.AllowGet);
    }
    public ActionResult EditPost(PostModel model)
    {
      try
      {
        bool success = DeleteTagsByPostId(Convert.ToInt32(model.PostId));
        var post = new Post
        {
          PostId = model.PostId,
          Author = "Himen Patel",
          Title = model.Title,
          Content = model.Content,
          CreatedDate = DateTime.UtcNow,
          postTags = Utilities.ConvertToCollection(model),
        };
        repository.Update(post);
        Success = repository.Save();
        return new HttpStatusCodeResult(HttpStatusCode.Created); // OK = 200
      }
      catch
      {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
    }
    public ActionResult Delete(string PostId)
    {
      try
      {
        bool success = DeleteTagsByPostId(Convert.ToInt32(PostId));
        if (success)
        {
          repository.Delete(Convert.ToInt32(PostId));
          Success = repository.Save();
        }
        return new HttpStatusCodeResult(HttpStatusCode.Created); // OK = 200
      }
      catch
      {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
    }
    [NonAction]
    public bool DeleteTagsByPostId(int id)
    {
      try
      {
        var tags = postagrepository.SelectAll().Where(t => t.PostId == id);
        foreach (var t in tags)
        {
          postagrepository.Delete(t.PostTagId);
        }
        Success = postagrepository.Save();
        return Success;
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }
}

Create Tags Controller

C#
using dotnetfundaAngularJS.Models;
using System.Linq;
using System.Web.Mvc;
namespace dotnetfundaAngularJS.Controllers
{
  public class TagsController : Controller
  {
    private IRepository<Tags> repository = null;
    public TagsController()
    {
      this.repository = new Repository<Tags>();
    }
    public TagsController(IRepository<Tags> repository)
    {
      this.repository = repository;
    }
    //Get All Tags
    public JsonResult TagsList()
    {
      var TagsList = repository.SelectAll().ToList();
      return Json(TagsList, JsonRequestBehavior.AllowGet);
    }
  }
}

Create Angular Scripts As Below

Create module.js and paste the below code snippet.

JavaScript
 var app = angular.module('blogApp', 
['ngRoute', 'ngResource', 'ui.bootstrap']);  

Create route.js and paste the below code snippet.

C#
app.config(['$routeProvider', function ($routeProvider) {
  $routeProvider.when('/', {
    templateUrl: "/app/Home/home.html"
  }),
  $routeProvider.when('/post', {
    templateUrl: "/app/post/PostList.html",
    controller: "postListController"
  }),
  $routeProvider.when('/post/new', {
    templateUrl: "/app/post/Post.html",
    controller: "postController"
  }),
  $routeProvider.when('/post/:id', {
      templateUrl: "/app/post/Post.html",
      controller: "postController"
    }),
  $routeProvider.otherwise({
    redirectTo: '/'
  });
}]);

Create dataServices.js and paste the below code snippet.

JavaScript
app.service("PostService", function ($http) {
  // get All Post
  this.getPosts = function () {
    return $http.get("Post/PostList");
  };
  //get new post
  this.getNewPost = function () {
    return $http.get("Post/New");
  };
  // get Post By Id
  this.getPost = function (id) {
    var response = $http({
      method: "post",
      url: "Post/Edit",
      params: {
        id: id
      }
    });
    return response;
  }
  // Update Post
  this.updatePost = function (Post) {
    var response = $http({
      method: "post",
      url: "Post/EditPost",
      data: JSON.stringify(Post),
      dataType: "json"
    });
    return response;
  }
  // New Post
  this.newPost = function (Post) {
    var response = $http({
      method: "post",
      url: "Post/NewPost",
      data: JSON.stringify(Post),
      dataType: "json"
    });
    return response;
  }
  //Delete Post
  this.Delete = function (PostId) {
    var response = $http({
      method: "post",
      url: "Post/Delete",
      params: {
        PostId: JSON.stringify(PostId)
      }
    });
    return response;
  }
});
app.service("TagsService", function ($http) {
  //get All Tags
  this.getPosts = function () {
    return $http.get("Tags/TagsList");
  };
});

Create postController.js and paste the below code snippet.

C#
app.controller("postListController",
   ['$scope', 'PostService', '$window', '$routeParams',
  function ($scope, PostService, $window, $routeParams) {
    GetAllPosts();
    //Get All Posts
    function GetAllPosts() {
      var getData = PostService.getPosts();
      getData.then(function (pst) {
        $scope.posts = pst.data;
      }, function () {
        $scope.message = 'Unable to load post data: ' + error.message;
      });
    }
    //Delete Post
    $scope.deletePost = function (postId) {
      var getData = PostService.Delete(postId);
      getData.then(function (msg) {
        $scope.message = 'Post Successfully Deleted.';
      }, function () {
        $scope.message = 'Unable to delete post: ' + error.message;
      });
    }
  }]);
app.controller("postController",
   ['$scope', 'PostService', '$window', '$routeParams',
  function ($scope, PostService, $window, $routeParams) {
    //Get Post by PostId
    $scope.post = {};
    if ($routeParams.id) {
      var getData = PostService.getPost($routeParams.id);
      getData.then(function (pst) {
        $scope.post = pst.data;
        $scope.PostId = pst.data.PostId;
        $scope.Title = pst.data.Title;
        $scope.Content = pst.data.Content;
        $scope.Tags = pst.data.Tags;
      }, function () {
        $scope.message = 'Unable to load post data: ' + error.message;
      });
    }
    //Add or Update Post
    $scope.AddUpdatePost = function () {
      var Post = {
        Title: $scope.Title,
        Content: $scope.Content,
        Tags: $scope.Tags,
        PostId: $scope.PostId
      };
      if (Post.PostId != null) { //Update Post
        var getData = PostService.updatePost(Post);
        getData.then(function (msg) {
          $window.location = "#/post";
          $scope.message = 'Post Successfully Updated.';
        }, function () {
          $scope.message = 'Unable to update post: ' + error.message;
        });
      } else { //Add Post
        var getData = PostService.newPost(Post);
        getData.then(function (msg) {
          $window.location = "#/post";
          $scope.message = 'Post Successfully Created.';
        }, function () {
          $scope.message = 'Unable to create new post: ' + error.message;
        });
      }
    }
    //Adding Tags to Post Model
    $scope.addtag = function (param) {
      if ($scope.Tags != null) {
        $scope.Tags = param + ';' + $scope.Tags;
      }
      else { $scope.Tags = param; }
    };
  }]);
app.controller("tagsController", function ($scope, TagsService) {
  GetAllTags();
  //Get All Tags
  function GetAllTags() {
    var getData = TagsService.getPosts()
    getData.then(function (tags) {
      $scope.tags = tags.data;
    }, function () {
      $scope.message = 'Unable to load tags: ' + error.message;
    });
  }
});

Html/Views

Create PostList.html and paste the below code snippet:

HTML
<div>
  <br />
  <div class="panel panel-default">
    <div class="panel-heading">Posts | All</div>
    <div class="panel-body">
      <div class="input-group">
        <a class="btn btn-primary"
        href="#/post/new" ng-click="newPost()">
        New Post</a>
        <input type="text" id="searchText"
        class="form-control pull-right"
        placeholder="Search for..." ng-model="search_txt">
        <span class="input-group-btn">
          <button class="btn btn-primary" type="button">
            <i class="text-muted glyphicon glyphicon-search"></i>
          </button>
        </span>
      </div><!-- /input-group -->
      <div><p style="text-align:center;
      color:red">{{message}}</p></div>
      <div class="table table-responsive">
        <table class="table table-striped">
          <thead style="background-color:#333;color:#fff">
            <tr>
              <th>Title</th>
              <th>Content</th>
              <th>Tags</th>
              <th>Author</th>
              <th>Created Date</th>
              <th>Edit</th>
              <th>Delete</th>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="post in posts | filter:search_txt">
              <td>
                {{post.Title}}
              </td>
              <td>
                {{post.Content}}
              </td>
              <td>{{post.Tags}}</td>
              <td>{{post.Author}}</td>
              <td>{{post.CreatedDate.slice(6, -2) | date: 'yyyy-MM-dd' }}</td>
              <td>
                <a class="btn btn-primary" href="#/post/{{post.PostId}}">
                  <i class="glyphicon glyphicon-edit"></i>
                </a>
              </td>
              <td>
                <a id="deleteButton" name="submitForm" class="btn btn-danger"
                  ng-click="deletePost(post.PostId)">
                  <i class="glyphicon glyphicon-trash"></i> Delete
                </a>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</div>

Create post.html and paste the below code snippet:

HTML
<div><br />
  <div class="panel panel-default">
    <div class="panel-heading" ng-if="PostId">Posts | Update Post</div>
    <div class="panel-heading" ng-if="!PostId">Posts | New Post</div>
    <div><p style="text-align:center;color:red">{{message}}</p></div>
    <table>
      <tr>
        <td class="control-label col-md-2"><b>Title</b></td>
        <td>
          <input class="form-control" type="text"
          ng-required="true" ng-model="Title" placeholder="Title" />
        </td>
      </tr>
      <tr>
        <td class="control-label col-md-2"><b>Content</b></td>
        <td>
          <input class="form-control" type="text"
          ng-model="Content" placeholder="Content" />
        </td>
      </tr>
      <tr>
        <td class="control-label col-md-2"><b>Tags</b></td>
        <td>
          <input class="form-control" type="text"
          ng-required="true" ng-model="Tags" placeholder="Tags" />
        </td>
      </tr>
      <tr ng-controller="tagsController">
        <td class="control-label col-md-2"><b>Select Tags</b></td>
        <td>
          <span ng-repeat="tag in tags">
          <a ng-click="addtag(tag.Tag)" ng-model="tag.Tag">
          {{tag.Tag}}</a>,&nbsp;</span>
        </td>
      </tr>
      <tr>
        <td></td>
        <td>
          <button id="submitButton" name="submitButton"
          class="btn btn-primary" ng-click="AddUpdatePost()">Save</button>
          <a class="btn btn-warning" href="#/post">Cancel</a>
        </td>
      </tr>
    </table>
    <input type="hidden" ng-model="PostId" />
  </div>
</div>

Include your angular scripts files in _Layout.cshtml as below or add them in BundleConfig.cs file.

HTML
<html ng-app="blogApp">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>@ViewBag.Title - My ASP.NET Application</title>
  @Styles.Render("~/Content/css")
  @Scripts.Render("~/bundles/modernizr")
  @Scripts.Render("~/bundles/angular")
  @Scripts.Render("~/bundles/MyblogApp")
</head>

Replace Home/Index.html code with the below code snippet:

HTML
<div class="ng-view"> </div>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Hello Everyone! I’m Himen Patel and currently working as a Programmer Analyst.

I'm a Microsoft Certified Solutions Developer (MCSD) working on C#, ASP.NET MVC, WCF, AngularJS, Jquery, Javascript, SQL Server, Oracle11g and MongoDB.

You can visit my Technical blog at http://www.dotnet-fundamentals.com

Comments and Discussions

 
BugFix bug - can't save new data (Save button no response) Pin
anht3110-Oct-16 4:00
anht3110-Oct-16 4:00 
QuestionI need your code very help,my email is [goodemailaddress@163.com],Thanks! Pin
zuoyexinchen7-Dec-15 15:55
zuoyexinchen7-Dec-15 15:55 
GeneralMy vote of 5 Pin
Santhakumar M23-Nov-15 17:41
professionalSanthakumar M23-Nov-15 17:41 
QuestionSource Code??? Pin
bcpnatl@bellsouth.net20-Nov-15 19:21
bcpnatl@bellsouth.net20-Nov-15 19:21 
AnswerRe: Source Code??? Pin
Himen M Patel23-Nov-15 3:19
professionalHimen M Patel23-Nov-15 3:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.