Search sort Insert update and delete are the basic features we need to learn first to learn any language, In this article we will try to create these features with Web API, bootstrap modal popup and ui.router. We will use enetity framework code first to save and retrieve data from database. We will extend our previous article example to achieve our goal.
Here is the list of features we are going to cover and the final image:
Let's analyse how the page will open and work, on page load we will load the left side "Search Customers" section, the parent view, where we can search the customer from database, delete customer and on clicking the link will show the customer detail "first child" without loading the entire page but only refresh the customer detail section which contains another child "contact detail", on clicking the contact detail link it will show all the contacts without refresh the entire page but only the second child. This is the beauty of UI Routing.
Here is the routing configuration for these three parent child view:
.state('customer', {
url: '/customer',
templateUrl: 'app/customer/customer.html',
controller: 'customerCtrl'
})
.state('customer.detail', {
url: '^/customer/detail/{customerId:[0-9]{1,5}}',
templateUrl: 'app/customer/customerdetail.html',
controller: 'customerCtrl'
})
.state('customer.detail.contact', {
url: '^/customer/detail/contact/{customerId:[0-9]{1,5}}',
templateUrl: 'app/customer/contact.html',
controller: 'customerContactCtrl'
})
{customerId:[0-9]{1,5}}
customer Id parameter which must be a number of 1 to 5 digitsFirst we will see the Web Method "Search" in CustomerController derived from ApiController, don't worry for rest of the code, I will share the link to download the complete code, try to understand the current code only
DataContext db = new DataContext();
[Route("api/Customer/search")]
[HttpGet]
public IEnumerable<Customer> SearchCustomers(string searchText)
{
searchText = searchText ?? "";
return db.Customers
.Where(x => x.FullName.Contains(searchText) ||
x.Country.Contains(searchText) ||
x.City.Contains(searchText)
)
.Take(20);
}
We are returning only 20 records, if you want to search some specific record by name country or city just type that it will search the record and fill with animation, we will see the HTML latter, let's create the service to call the above method
var app = angular.module('MyApp');
app.factory('Customer', ['$q', '$http', function ($q, $http) {
var baseUrl = 'api/customer/';
var contactBaseUrl = 'api/Contact/';
var customerService = {};
customerService.customers = [];
customerService.currentCustomer = {};
// Search Customers
customerService.search = function (text) {
var deferred = $q.defer();
return $http({
url: baseUrl + 'search',
method: 'GET',
params: { 'searchText': text },
cache: true
}).success(function (data) {
deferred.resolve(
customerService.customers = data);
}).error(function (error) {
deferred.reject(error);
})
return deferred.promise;
}
return customerService;
}]);
Time to write the controller to call this service method, create a JavaScript file customerCtrl
var app = angular.module("MyApp")
app.controller('customerCtrl', ['$scope', '$state', '$stateParams', '$modal', '$log', 'Customer', function ($scope, $state, $stateParams, $modal, $log, Customer) {
var customerId = $stateParams.customerId;
$scope.searchText = '';
$scope.customers = searchCustomers();
$scope.contacts = [];
$scope.customer = {};
$scope.currentCustomer = {};
function searchCustomers() {
Customer.search($scope.searchText)
.then(function (data) {
$scope.customers = Customer.customers;
});
};
}]);
Create an HTML file customer.html and add following code
<div class="row">
<div class="col-lg-10">
<h1>Manage Customers</h1>
</div>
<div class="col-lg-2">
<button ng-click="addCustomer()" class="btn btn-success btn-lg">
<i class="glyphicon glyphicon-plus"></i> Add Customer
</button>
</div>
</div>
<div class="row">
<div class="col-lg-3" style="border:1px solid #999; overflow-y:hidden">
<!-- Search customers -->
<h3>Search Customers</h3>
<div class="input-group">
<input type="text"
ng-model="searchText"
class="form-control"
placeholder="Enter search text">
<span class="input-group-addon" id="basic-addon1">
<i class="glyphicon glyphicon-search"></i>
</span>
</div>
<div class="col-lg-12 customer-row animate" ng-repeat="customer in customers">
<as-customer></as-customer>
</div>
</div>
<div class="col-lg-9" style="border:1px solid #999; overflow-y:auto">
<div ui-view></div>
</div>
</div>
Right pan have a div vi-view to render the child view (customer detail)
Here is the code for directive, it is not necessary even you can use the HTML directly here, create a JavaScript file say asCustomer.js, and add following code in it, it's and element directive
var app = angular.module('MyApp'); app.directive("asCustomer", function () { return { restrict: 'E', replace: 'true', templateUrl: 'app/customer/userListRow.html' } });
Create a separate HTML file in customer folder with name userListRow.html and add following code in it
<div class="row">
<div class="col-lg-3">
<img ng-src="{{customer.Image ? customer.Image : 'customerimages/default-image.png'}}"
class="img-circle"
style="max-width:50px"
alt='customer image' />
</div>
<div class="col-lg-9">
<div style="font-weight:bold;">
<a ng-click="customerDetail(customer.CustomerId)" style="cursor:pointer">{{customer.FullName}}</a>
<span class="pull-right" title="Delete customer">
<i ng-click="deleteCustomer($event, customer.CustomerId)" class="glyphicon glyphicon-trash"></i>
</span>
</div>
<div>{{customer.Address}} {{customer.Country}} {{customer.ZipCode}}</div>
</div>
</div>
If you don't want to use the directive then add the above html in customer.html to replace
Now if you will run the code, user list will be filled with customers because in controller we already calling the searchCustomer method and assign the data on scope, $scope.customers = searchCustomers(); This code is not enough to search by typing into the text box because we have not written any event method yet. To make it working we will add a $watch to check the changes in text search and call the search method, it's eady so let's add that
$scope.$watch('searchText', function (newVal, oldVal) {
if (newVal != oldVal) {
searchCustomers();
}
}, true);
This watch method will listen any change in search text and match with the old value if different then it will call the search method again for us. Run the application and check it.
In our directive we used a function to delete the customer record deleteCustomer($event, customer.CustomerId), here is the complete html
<span class="pull-right" title="Delete customer">
<i ng-click="deleteCustomer($event, customer.CustomerId)" class="glyphicon glyphicon-trash"></i>
</span>
In our controller we need a function deleteCustomer with two parameters, one for event and other for customer Id, on the scope and a service method to call the Web API method so a Web API Action, let's add all together:
// Delete a customer in controller
$scope.deleteCustomer = function ($event, id) {
var ans = confirm('Are you sure to delete it?');
if (ans) {
Customer.delete(id)
.then(function () {
var element = $event.currentTarget;
$(element).closest('div[class^="col-lg-12"]').hide();
})
}
};
Let's add the service and Web API function, they are easy, see:
// Delete Customer in service
customerService.delete = function (id) {
var deferred = $q.defer();
return $http.post(baseUrl + "delete/" + id)
.success(function (data) {
deferred.resolve();
}).error(function (error) {
deferred.reject(error);
})
return deferred.promise;
}
// Web API Method
[Route("api/Customer/delete/{id}")]
[HttpPost]
public void DeleteCustomer(int Id)
{
var customer = db.Customers.FirstOrDefault(x => x.CustomerId == Id);
db.Customers.Remove(customer);
db.SaveChanges();
}
Whatever the features on the left pan is completed, now time to code to show the detail on selecting any customer from left pan by using the ui-state than redirecting to a new page, if you can go back and see the code what we written to show the detail, just and ng-click on an anchor tag ng-click="customerDetail(customer.CustomerId)".
For it we need a Web API method to get the data from database by customer Id and a service to call this method and a method on controller to call from page, so let's add all the three functions
// Add it in Web API Controller
[Route("api/Customer/detail/{id}")]
[HttpGet]
public Customer GetDetail(int Id)
{
return db.Customers.FirstOrDefault(x=>x.CustomerId == Id);
}
// In Service: Customers detail by customer id
customerService.customerDetail = function (id) {
var deferred = $q.defer();
return $http.get(baseUrl + "detail/" + id)
.success(function (data) {
deferred.resolve(
customerService.currentCustomer = data);
})
.error(function (error) {
deferred.reject(error);
})
return deferred.promise;
}
// In Controller: Customer Detail
$scope.customerDetail = function (id) {
if (!id) return;
Customer.customerDetail(id)
.then(function (data) {
$scope.currentCustomer = Customer.currentCustomer;
$state.go('customer.detail', { 'customerId': id });
});
};
See the app.js file where we putt our all the configuration, let me add that code one more time here
.state('customer.detail', {
url: '^/customer/detail/{customerId:[0-9]{1,5}}',
templateUrl: 'app/customer/customerdetail.html',
controller: 'customerCtrl'
})
Once we change the state, our application know from above code, which template it need to be bind, since it is a change view of customer view "customer.detail" it, check the ui-view
in parent and attach this template into it. Is not it a nice feature, this is the feature for which UI Routing is popular for. Still we have not created our customerdetail.html
template so let's add a HTML file in app/customer
folder:
<div class="form-horizontal">
<fieldset>
<legend>Customer Detail</legend>
<div class="col-lg-3">
<img ng-src="{{currentCustomer.Image ? currentCustomer.Image : 'customerimages/default-image.png'}}"
class="img-circle"
alt='customer image' />
</div>
<div class="col-lg-8">
<div class="form-group">
<label for="inputEmail" class="col-lg-3 control-label">Full Name</label>
<div class="col-lg-8">
<input type="text" class="form-control" ng-model="currentCustomer.FullName" disabled>
</div>
<div class="col-lg-1">
<button ng-click="editCustomer(currentCustomer.CustomerId)" class="btn btn-primary">Edit</button>
</div>
</div>
<div class="form-group">
<label for="inputEmail" class="col-lg-3 control-label">Address</label>
<div class="col-lg-8">
<input type="text" class="form-control" ng-model="currentCustomer.Address" disabled>
</div>
</div>
<div class="form-group">
<label for="inputEmail" class="col-lg-3 control-label">City</label>
<div class="col-lg-8">
<input type="text" class="form-control" ng-model="currentCustomer.City" disabled>
</div>
</div>
<div class="form-group">
<label for="inputEmail" class="col-lg-3 control-label">Country</label>
<div class="col-lg-8">
<input type="text" class="form-control" ng-model="currentCustomer.Country" disabled>
</div>
</div>
<div class="form-group">
<label for="inputEmail" class="col-lg-3 control-label">Zip Code</label>
<div class="col-lg-8">
<input type="text" class="form-control" ng-model="currentCustomer.ZipCode" disabled>
</div>
</div>
<div class="form-group">
<div class="col-lg-8 col-lg-offset-3">
<a ui-sref="customer.detail.contact({contactCustomerId: currentCustomer.CustomerId})">Contact Detail</a>
</div>
</div>
</div>
</fieldset>
</div>
<div ui-view></div>
If you can note at the very last div
, it uses another ui-view
to hold child view on it, in which we will show the contact list for this customer. This template have a button to edit the current customer and at the top of the page to add new customer. Which we will see in our next article because I am using here a modal popup so want to give full detail that how to open a modal popup and pass values from current controller to modal popup controller but before completing this article I want to show the code, how we can render contact detail list.
How to read the customer Id from url, it's simple, we need to inject $stateParams and call the parameter name which we defined in our config file, in this case customerId
var customerId = $stateParams.customerId;
See in above code how we are changing the state from an anchor tag and pass current customer Id as parameter
<a ui-sref="customer.detail.contact({customerId: currentCustomer.CustomerId})">
Contact Detail
</a>
Let's see this configuration:
.state('customer.detail.contact', {
url: '^/customer/detail/contact/{customerId:[0-9]{1,5}}',
templateUrl: 'app/customer/contact.html',
controller: 'customerContactCtrl'
})
customer.detail.contact
Controller and HTML for the contact detail:
Create a separate js file for controller:
// Conatact controller
var app = angular.module("MyApp");
app.controller("customerContactCtrl", ['$scope', '$state', '$stateParams', '$modal', '$log', 'Customer',
function ($scope, $state, $stateParams, $modal, $log, Customer) {
var customerId = $stateParams.customerId;
$scope.customerContacts = function (id) {
return Customer.customerContacts(id)
.then(function (data) {
$scope.contacts = Customer.conntacts;
});
};
$scope.customerContacts(customerId);
}]);
// Customers Contact: Created a function in customer service
customerService.customerContacts = function (id) {
var deferred = $q.defer();
return $http.get(contactBaseUrl + "ByCustomerId/" + id)
.success(function (data) {
deferred.resolve(customerService.conntacts = data);
}).error(function (error) {
deferred.reject(error);
})
return deferred.promise;
}
Create a separate controller Contact
in Web API and add following code in it
[Route("api/Contact/ByCustomerId/{Id}")]
public IEnumerable<Contact> GetCustomerContacts(int Id)
{
var contacts = db.Contacts.Where(x => x.CustomerId == Id);
return contacts;
}
Finally here is the Customer and Contact models to create the database tables:
public class Customer
{
[Key]
public int CustomerId { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
[Required]
public string ZipCode { get; set; }
[Required]
public string Country { get; set; }
public string Image { get; set; }
}
public class Contact
{
[Key]
public int ContactId { get; set; }
[Required]
public int CustomerId { get; set; }
[Required]
public string Phone { get; set; }
public string Fax { get; set; }
public string Email { get; set; }
}
If you notice we have not written anything for progress bar or progress image to show while data is fetching from database, it is added in application for more detail you can see my previous article Angularjs Progress bar directive for entire application.
How to add new customer or edit existing customer: Angularjs CRUD with Web API, Entity Framework & Bootstrap modal popup part 2
Code is uploaded on GitHub so you can Download it from there and try.
![]() |
Having 13+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 9 years and still with the same company. Always ready to learn new technologies and tricks.
|
By Ali Adravi On 28 Jul, 15 Viewed: 31,462 |
Paging on table is the basic requirement and most of us look for some grid so it can provide the complete feature of paging and sorting but in Angularjs it is so easy that anyone can achieve it by writing some code. I checked and found most of the available code give the paging feature in for 1 to... By Ali Adravi On 14 Aug 2015 Viewed: 20,947
Cascading dropdown with angularjs is easier than MVC and ASP.Net. In Angularjs ng-change is the best event to hook the function to get the second dropdown list items. In this article, we will try to fill the state dropdown on selection change of country and then fill the city dropdown on change... By Ali Adravi On 06 Aug 2015 Viewed: 57,780
Add/Edid Customer in Modal Popup --- In previous article we discussed how to search, delete and show detail of customer and contact list. In this article we will try to open the modal popup to add new customer and edit an existing customer record. I divided article in two parts because of the... By Ali Adravi On 29 Jul 2015 Viewed: 19,719
Angularjs custom directives are the common to avoid the duplicate code, it is easy powerful and really nice. There are already too many directive have been created by experts but as a developer we need to write our own to achieve our specific goal. ng-model, ng-if, ng-show, ng-repeat all these are... By Ali Adravi On 29 Jul 2015 Viewed: 4,497
Progress bar for Angularjs application by using directives is simple and real easy. I was googling and could not found any directive way, if you will check most of the people says before call the http method open the progress bas and close once you get the result or get any error. Do you think it... By Ali Adravi On 24 Jul 2015 Viewed: 14,201