Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

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

4.38/5 (6 votes)
23 Nov 2015CPOL1 min read 33.5K  
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)