Geeks With Blogs
Alex Hildyard

Envisage almost any data-driven user interface, and sooner or later you will probably come across a requirement like:

- present the user with a list (of users, addresses, products, etc.)
- allow the user to edit or delete an existing item in the list
- allow the user to create a new item and add it to the list

I decided to have a look at several web-based technologies to see how easy it might be to fulfil this requirement in as generic and extensible a way as possible, with the absolute minimal in the way of coding or configuration. I ended up opting for:

- angularJS for a data-bound UI
- Bootstrap for icons and element styling
- Parse for remote object definition, serialisation and persistence

My implementation involves:

- creating an angularJS Parse-based persistence service, which serialises the objects within a named Parse table
- creating a generic angularJS controller to present data supplied by the service and respond to user events

In order to use it, you just need to:

1. Subclass the service, specifying the name of the table you want bound to the UI

The following code creates an angular service bound to a Parse table called "Users":

angular.module('crudApp').service('UserPersistenceService', function () {
    return new PersistenceService("Users");
});


At the same time, you should create a table named "Users" within Parse, with columns for any information you want to bind. In this example, I created the fields "name", "bio" and "score." But I am electing just to bind the "name" and "bio" elements.


2. Subclass the controller, and add hooks to define application behaviour when items are added or edited

The controller exploits "backing fields" to store partially edited information, as well as supply default values when a new record is created. These operate as follows:

- initBackingFields(): called when a new record is created
- copyBackingFields(): called when you choose to edit a record
- commitBackingFields(): called when you have edited a record and elect to commit your changes 

So in the example above, we have just two editable fields, "name" and "bio", which we will back with two further fields, "newName" and "newBio", reflecting information that has been edited but not yet committed. The full implementation of the subclassed controller looks like this:

angular.module('crudApp').controller('UserListCtrl', ['$scope', '$controller', 'UserPersistenceService', function ($scope, $controller, persistenceService) {

    $controller('PersistenceController', {
        $scope: $scope,
        persistenceService: persistenceService,
        initBackingFields: function (item) {
            item.name = "";
            item.newName = item.name;
            item.bio = "";
        },
        copyBackingFields: function (item) {
            item.newName = item.attributes.name;
            item.newBio = item.attributes.bio;
        },
        commitBackingFields: function (item) {
            item.set("name", item.newName);
            item.set("bio", item.newBio);
        }
    });

} ]);


3. Bind the fields that interest you to the UI with appropriate directives 

Create an HTML page to display the items in the table in a list. The Persistence controller exposes three helper functions to facilitate record editing:

- toggleItemEdit(): switch a record in the UI between "editable" and "read only"
- deleteItem(): delete the currently selected item, and persist changes to the back end
- commitItemEdit(): update an existing or add a new record, and persist the changes

The resultant HTML page now looks like this:

<html>
<head>
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="Stylesheet" href="./styles.css" />
    <script src="http://www.parsecdn.com/js/parse-1.6.7.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script src="./crudController.js"></script>
    <title>CRUD Test</title>
</head>
<body ng-app="crudApp">

    <!-- item controller -->
    <div class="userContainer" ng-controller="UserListCtrl">
        <h3>Users</h3>
          <table class="table table-striped">
          <thead><tr>
            <th>Name</th>
            <th>Bio</th>
          </tr></thead>
          <tbody>
            <tr ng-repeat="item in items">
                <td>
                    <span ng-show="!item.isEditing">{{item.attributes.name}}</span>
                    <input ng-model="item.newName" ng-show="item.isEditing" type="text"/>
                </td>
                <td>
                    <span ng-show="!item.isEditing">{{item.attributes.bio}}</span>
                    <input ng-model="item.newBio" ng-show="item.isEditing" type="text"/>
                </td>
                <td>
                  <button ng-hide="item.isEditing" class="btn" ng-click="toggleItemEdit(item)">
                    <span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;Edit
                  </button>
                  <button ng-hide="item.isEditing" class="btn" ng-click="deleteItem(item)">
                    <span class="glyphicon glyphicon-remove"></span>Delete
                  </button>
                  <span ng-show="item.isEditing">
                      <button ng-disabled="item.newName==''" class="btn" ng-click="commitItemEdit(item)">
                        <span class="glyphicon glyphicon-pencil"></span>Update
                      </button>
                      <button class="btn" ng-click="toggleItemEdit(item)">
                        <span class="glyphicon glyphicon-pencil"></span>Cancel
                      </button>
                  </span>
                </td>
            </tr>
          </tbody>
        </table>
        <table>
            <tr>
                <td>
                    <button class="btn btn-success" ng-click="addNewItem()">
                        <span class="glyphicon glyphicon-item"></span>New User
                    </button>
                </td>
            </tr>
        </table>

        <hr>
     </div>

</body>
</html>


Finally, here is the Javascript file that does all the work, "crudController.js" and the accompanying "styles.css", which adds some spacing to the table elements.

-- styles.css --

input
{
    width: 200px;
}

td
{
    width: 200px;
}


-- crudController.js --

var app = angular.module('crudApp', []);

function PersistenceService(parseTable) {

    var objParse = Parse.initialize("YOUR PARSE ADMIN KEY", "YOUR PARSE APP KEY");
    var items = Parse.Object.extend(parseTable);
    var queryItems = new Parse.Query(items);
    
    // Store the type of the persistence service created
    this.persistenceTable = parseTable;
    
    // Load a list of items from the back end
    this.load = function (itemsLoadedCallback) {

        queryItems.find(
        {
            success: function (results) {
                itemsLoadedCallback(results);
            },
            error: function (results) {
                alert("Failed to load items: " + results.message)
            }
        });
    }

    // Add a new or update an existing item
    this.update = function (item, itemUpdateCallback) {

        item.save(
        {
            success: function (results) {
                itemUpdateCallback(results);
            },
            error: function (results) {
                alert("Failed to update item: " + results.message)
            }
        });
    }

    // Delete an item
    this.remove = function (item, itemDeletedCallback) {

        item.destroy(
        {
            success: function (results) {
                itemDeletedCallback(results);
            },
            error: function (results) {
                alert("Failed to delete item: " + results.message)
            }
        });
    }
}

angular.module('crudApp').controller('PersistenceController', ['$scope', 'persistenceService', 'initBackingFields', 'copyBackingFields', 'commitBackingFields', function ($scope, persistenceService, initBackingFields, copyBackingFields, commitBackingFields) {

    // Callback to receive list of users, and bind them to the scope
    $scope.itemsLoaded = function (items) {

        // Set the isEditing and newName attributes on each user; these aren't persisted
        // but the controller uses them to facilitate editing
        for (i = 0; i < items.length; i++) {
            // We add these non-persisted fields to facilitate editing; "parse" won't serialise them
            copyBackingFields(items[i]);
            items[i].isEditing = false;
        }

        $scope.items = items;

        // Force scope update, to rebind updated item list to the view
        $scope.$apply();
    }

    // Called by the service when an item has been deleted, added or updated
    $scope.OnDatabaseChanged = function (items) {
        // Refresh the view
        persistenceService.load($scope.itemsLoaded);
    }

    $scope.toggleItemEdit = function (item) {
        item.isEditing = !item.isEditing;
    }

    $scope.commitItemEdit = function (item) {
        commitBackingFields(item)
        item.isEditing = false;

        // Commit the changes to the database
        persistenceService.update(item, $scope.OnDatabaseChanged);
    }

    $scope.addNewItem = function () {

        // This method doesn't commit the new user to the database; it just adds a row to the view,
        // and makes it editable
        var Item = Parse.Object.extend(persistenceService.persistenceTable);
        var item = new Item();
        item.isEditing = true;
        initBackingFields(item);
        $scope.items[$scope.items.length] = item;
    }

    $scope.deleteItem = function (item) {
        persistenceService.remove(item, $scope.OnDatabaseChanged);
    }

    // Start with an empty collection of items
    $scope.items = [];

    // Load the items from the service
    persistenceService.load($scope.itemsLoaded);
} ]);










Posted on Tuesday, February 23, 2016 3:42 PM | Back to top


Comments on this post: Simple Generic CRUD with AngularJS, Parse and Bootstrap

# re: Simple Generic CRUD with AngularJS, Parse and Bootstrap
Requesting Gravatar...
Hi,

I am learning angular and like your concept of generic crud.
could you please share the code of working example ?

Regards,

Amit
Left by Amit Jain on Sep 23, 2016 2:17 PM

# re: Simple Generic CRUD with AngularJS, Parse and Bootstrap
Requesting Gravatar...
so that there are no such questions or if you say help with my home work then our service will help you in writing homework
Left by codysartony on Dec 06, 2017 10:49 AM

# Bootstrap
Requesting Gravatar...
Bootstrap is a free and open-source front-end library for outlining sites and web applications. It contains HTML-and CSS-based plan formats for typography, frames, catches, route and other interface parts, and in addition discretionary JavaScript augmentations.Can Someone Do My Essay For Me
Left by Angelica  Mandy on Apr 27, 2018 1:36 PM

Your comment:
 (will show your gravatar)


Copyright © Alex Hildyard | Powered by: GeeksWithBlogs.net