Another MVC Application
Another MVC Application
Another MVC Application
All websites are growing faster these days, and once it grows, it is very hard to write,
organize and maintain. As we add new functionality or developer to a project, any large web
applications with poor design may be go out of control. So the idea behind this article is to
design a website architecture that must be simple, easily understandable by any web designer
(beginner to intermediate) and Search Engines. For this article I am trying to design a website
for any individuals to maintain their contact details online. However, in future, the same
application could be used by large community all over the world with added functionality and
modules. So, the design should be easily adaptable in order to cope with the future growth of
business.
In this article I will talk about creating and designing User Interface (UI) in such a manner so
that UI will be separated from the business logic, and can be created independently by any
designer/developer. For this part we will use ASP.Net MVC, Knockout Jquery and Bootstrap.
later in this article we will discuss more about database design and implementing business
logic using structured layers using SQl Server 2008, Entity Framework, and Castle Windsor
for Dependency Injection.
And, the final solution will look like below image in Visual Studio :
Separation of Concern: Design should allow clear and defined layers; means
segregate application into distinct areas whose functionality does not overlap. such
that UI-designers can focus on their job without dealing with the business logic
(Application.Web), and the core developer can only work on main business logic's
(Application.DTO or Application.Manager).
Productivity: It is easier to add new features to existing software, since the structure is
already in place, and the location for every new piece of code is known beforehand,
so any issue can be easily identified and separated to cope with complexity, and to
achieve the required engineering quality factors such as * * robustness, adaptability,
maintainability, and re-usability.
Maintainability: It is easier to maintain application, due to clear and defined structure of the
code is visible and known, so its easier to find bugs and anomalies, and modify them with
minimal risk.
Adaptability: New technical features, such a different front ends, or adding a business
rule engine is easier to achieve, as your software architecture creates a clear
separation of concerns.
Bootstrap CSS
Initial sketch:
Initial Sketch:
Base CSS for Typography, code (syntax highlighting with Google prettify), Tables,
Forms, Buttons and uses Icons by Glpyhicons .
Javascript plugins for Modal, Dropdown, Scrollspy, Tab, Tooltip, Popover, Alert,
Button, Collapse, Carousel and Typehead.
In below steps, we will work through the layout and design to build UI for above requirement
by using dummy javascript data.
Step 1:
Create a new project as Blank Solution; name it as Application
Step 2:
Right Click on Solution folder and add new Project of type ASP.NET MVC 4 as an Internet
Application Template with View engine as Razor.
After Step 2 - the project structure should look like the below image
Step 3:
Right click on References and click on Manage NuGet Packages. Type Bootstrap on search
bar then click on Install button.
Step 4:
add below line of code into BundleConfig.cs file under App_Start folder to add Knockoutjs
and Bootstrap for every page
bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
"~/Scripts/knockout-{version}.js"));
bundles.Add(new
StyleBundle("~/Content/css").Include("~/Content/bootstrap.css"));
Also in _Layout,cshtml file under Views/Shared folder add below line to register knockout
files as :
@Scripts.Render("~/bundles/knockout")
Step 5:
Add a new folder name as Contact inside Views, and then add Index.cshtml as new View
page. Then add a new Controller name it ContactController.cs inside Controller folder, and
add a new Contact.js file under Scripts folder. Refer to below image.
Step 6:
Finally modify the default map route in Route.config to point to Contact controller as:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Contact", action = "Index",
id = UrlParameter.Optional }
);
And also modify the _Layout.cshtml file inside View/Shared as per the BootStrap Syntax.
Below is the modified code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title - Contact manager</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/knockout")
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@RenderSection("scripts", required: false)
</head>
<body>
<div class="container-narrow">
<div class="masthead">
<ul class="nav nav-pills pull-right">
</ul>
<h3 class="muted">Contact Manager</h3>
</div>
<div id="body" class="container">
@RenderSection("featured", required: false)
<section>
@RenderBody()
</section>
</div>
<hr />
<div id="footer">
<div class="container">
<p class="muted credit">© @DateTime.Now.Year - Design
and devloped by <a href="http://www.anandpandey.com">Anand Pandey</a>.</p>
</div>
</div>
</div>
</body>
</html>
Step 7:
Now we are done with initial setup to run the application. The output is as below:
We will use this page to display the requirement for Screen 1 i.e. Contact List - View all
contacts
Step 8:
First we will create a dummy profile data as array in Contact.js (later we will fetch it from
database), and then we will use this data to populate Grid.
var DummyProfile = [
{
"ProfileId": 1,
"FirstName": "Anand",
"LastName": "Pandey",
"Email": "anand@anandpandey.com"
},
{
"ProfileId": 2,
"FirstName": "John",
"LastName": "Cena",
"Email": "john@cena.com"
}
]
Next, we will create ProfilesViewModel, a viewmodel class which hold Profiles, an array
holding an initial collection of DummyProfile data. Note that it's a ko.observableArray , and
it is the observable equivalent of a regular array, which means it can automatically trigger UI
updates whenever items are added or removed.
And finally we need to activate Knockout using ko.applyBindings().
var ProfilesViewModel = function () {
var self = this;
var refresh = function () {
self.Profiles(DummyProfile);
};
// Public data properties
self.Profiles = ko.observableArray([]);
refresh();
};
ko.applyBindings(new ProfilesViewModel());
Step 9:
Next we will write code in Index.cshtml page, that's supposed to display the Profile List. We
need to use the foreach binding on <tbody> element, so that it will render a copy of its child
elements for each entry in the profiles array, and then populate that <tbody> element with
some markup to say that you want a table row (<tr>) for each entry.
<table class="table table-striped table-bordered table-condensed">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<tbody data-bind="foreach: Profiles">
<tr">
<td data-bind="text: FirstName"></td>
<td data-bind="text: LastName"></td>
<td data-bind="text: Email"></td>
</tr>
</tbody>
</table>
<script src="~/Scripts/Contact.js"></script>
If you run the application now, you should see a simple table of profile information as:
Remember for table style we are using Bootstraps css class. In above example it is ;
<table class="table table-striped table-bordered table-condensed">
Step 10:
Now we need to add Edit and Remove functionality for each row, and one more button at top
to create a new profile. So let us do:
Add one more <th> and <td> in our table template and bind its click event with
removeProfile function in js.
Modify First Name row to add link for Edit Profile and then bind its click event with
editProfile function.
Add one button for Create new profile and bind it with click event using createProfile
function.
None of the added button and link will work, because we have not written any code for that,
so let's fix that in next step.
Step 11:
Add events for createProfile, editProfile and removeProfile in Contact.js
self.createProfile = function () {
alert("Create a new profile");
};
self.editProfile = function (profile) {
};
Now when we run our application and click on Remove button, then it will remove that
profile from current array. As we define this array as observable, the UI will be kept in sync
with changes to that array. Try it by clicking on Remove button. Edit link and Create Profile
button will simply display the alert. So, let us implemented this functionality in next steps:
Step 12:
Next we will add:
Modify createProfile and editProfile method of Contact.js, so that it will point to the
CreateEdit page.
self.createProfile = function () {
window.location.href = '/Contact/CreateEdit/0';
};
self.editProfile = function (profile) {
window.location.href = '/Contact/CreateEdit/' + profile.ProfileId;
};
Running the application will now work for all the events in Contact List (screen -1). And
create and Edit should redirect to CreateEdit page with required parameters.
Step 13:
First we will start with adding Profile Information to this CreateEdit page. For that we need
to do:
We need to get profileId from url so, add below two lines on top of the CreateEdit.js page
var url = window.location.pathname;
Profile, a simple JavaScript class constructor that stores a profiles FirstName, LastName and
Email selection.
var Profile = function (profile) {
var self = this;
};
ProfileCollection, a viewmodel class that holds profile (a JavaScript object providing Profile
data) along with binding for saveProfile and backToProfileList events.
var ProfileCollection = function () {
var self = this;
//if ProfileId is 0, It means Create new Profile
if (profileId == 0) {
self.profile = ko.observable(new Profile());
}
else {
var currentProfile = $.grep(DummyProfile, function (e) { return
e.ProfileId == profileId; });
self.profile = ko.observable(new Profile(currentProfile[0]));
}
self.backToProfileList = function () { window.location.href =
'/contact'; };
self.saveProfile = function () {
alert("Date to save is : " +
JSON.stringify(ko.toJS(self.profile())));
};
};
ko.applyBindings(new ProfileCollection());
Step 14:
Next we will write code in CreateEdit.cshtml page, that's supposed to display the Profile
information. We need to use the with binding for profile data, so that it will render a copy
of its child elements for a particular profile, and then assign the appropriate values. He code
for CreateEdit.cshtml is as below:
<table class="table">
<tr>
<th colspan="3">Profile Information</th>
</tr>
<tr></tr>
<tbody data-bind='with: profile'>
<tr>
<td>
<input class="input-large" data-bind='value: FirstName'
placeholder="First Name"/>
</td>
<td>
<input class="input-large" data-bind='value: LastName'
placeholder="Last Name"/>
</td>
<td>
<input class="input-large" data-bind='value: Email'
placeholder="Email" />
</td>
</tr>
</tbody>
</table>
<button class="btn btn-small btn-success" data-bind='click:
saveProfile'>Save Profile</button>
<input class="btn btn-small btn-primary" type="button" value="Back To
Profile List" data-bind="click:$root.backToProfileList" />
<script src="~/Scripts/CreateEdit.js"></script>
Update any existing record and click on save with give below output:
Step 15:
To achieve requirement no. 2.2 and 2.3, we need to do:
Define a dummy PhoneType and PhoneDTO data as an array in CreateEdit.js.
phoneTypeData will be used to bind dropdown to define the Phone Type for a particular
Phone number.
var phoneTypeData = [
{
"PhoneTypeId": 1,
},
{
}
];
var PhoneDTO = [
{
"PhoneId":1,
"PhoneTypeId": 1,
"ProfileId":1,
"Number": "111-222-3333"
},
{
"PhoneId": 2,
"PhoneTypeId": 2,
"ProfileId": 1,
"Number": "444-555-6666"
}
];
Profile, a simple JavaScript class constructor that stores a PhoneLine information which
include its Phone Type i.e. whether its Work Phone or Home Phone etc. along with phone
number.
var PhoneLine = function (phone) {
var self = this;
self.PhoneId = ko.observable(phone ? phone.PhoneId : 0);
self.PhoneTypeId = ko.observable(phone ? phone.PhoneTypeId : 0);
self.Number = ko.observable(phone ? phone.Number : '');
};
Modify ProfileCollection viewmodel class to also holds phoneNumbers along with binding
for addPhone and removePhone events.
var ProfileCollection = function () {
var self = this;
//if ProfileId is 0, It means Create new Profile
if (profileId == 0) {
self.profile = ko.observable(new Profile());
self.phoneNumbers = ko.observableArray([new PhoneLine()]);
}
else {
var currentProfile = $.grep(DummyProfile, function (e) { return
e.ProfileId == profileId; });
self.profile = ko.observable(new Profile(currentProfile[0]));
var currentProfilePhone = $.grep(PhoneDTO, function (e) { return
e.ProfileId == profileId; });
self.phoneNumbers =
ko.observableArray(ko.utils.arrayMap(currentProfilePhone, function (phone)
{
return phone;
}));
}
self.addPhone = function () {
};
self.phoneNumbers.push(new PhoneLine())
Step 16:
Next we will add one more section to add Phone Information in CreateEdit.cshtml page,
that's supposed to display the Phone information. As one profile can have multiple phone of
different types, So, we will use the foreach binding for Phone numbers data, so that it will
render a copy of its child elements for a particular profile, and then assign the appropriate
values. Add below section in CreateEdit.cshtml just after Profile Information and before Save
button.
<table class="table">
<tr>
<th colspan="3">Phone Information</th>
</tr>
<tr></tr>
<tbody data-bind='foreach: phoneNumbers'>
<tr>
<td>
<select data-bind="options: phoneTypeData, value:
PhoneTypeId, optionsValue: 'PhoneTypeId', optionsText: 'Name',
optionsCaption: 'Select Phone Type...'"></select>
</td>
<td>
<input class="input-large" data-bind='value: Number'
placeholder="Number" />
</td>
<td>
<a class="btn btn-small btn-danger" href='#' data-bind='
click: $parent.removePhone'>X</a>
</td>
</tr>
</tbody>
</table>
<p>
<button class="btn btn-small btn-primary" data-bind='click: addPhone'>Add
New Phone</button>
</p>
var DummyProfile = [
{
"ProfileId": 1,
"FirstName": "Anand",
"LastName": "Pandey",
"Email": "anand@anandpandey.com"
},
{
"ProfileId": 2,
"FirstName": "John",
"LastName": "Cena",
"Email": "john@cena.com"
}
];
var PhoneTypeData = [
{
"PhoneTypeId": 1,
"Name": "Work Phone"
},
{
"PhoneTypeId": 2,
"Name": "Personal Phone"
}
];
var PhoneDTO = [
{
"PhoneId":1,
"PhoneTypeId": 1,
"ProfileId":1,
"Number": "111-222-3333"
},
{
"PhoneId": 2,
"PhoneTypeId": 2,
"ProfileId": 1,
"Number": "444-555-6666"
}
];
var AddressTypeData = [
{
"AddressTypeId": 1,
"Name": "Shipping Address"
},
{
"AddressTypeId": 2,
"Name": "Billing Address"
}
];
var AddressDTO = [
{
"AddressId": 1,
"AddressTypeId": 1,
"ProfileId": 1,
"AddressLine1": "10000 Richmond Avenue",
"AddressLine2": "Apt # 1000",
"Country": "USA",
},
{
"State": "Texas",
"City": "Houston",
"ZipCode": "70000"
"AddressId": 2,
"AddressTypeId": 2,
"ProfileId": 1,
"AddressLine1": "20000 Highway 6",
"AddressLine2": "Suite # 2000",
"Country": "USA",
"State": "Texas",
"City": "Houston",
"ZipCode": "80000"
];
else {
//For Profile information
var currentProfile = $.grep(DummyProfile, function (e) { return
e.ProfileId == profileId; });
self.profile = ko.observable(new Profile(currentProfile[0]));
//For Phone number
var currentProfilePhone = $.grep(PhoneDTO, function (e) { return
e.ProfileId == profileId; });
self.phoneNumbers =
ko.observableArray(ko.utils.arrayMap(currentProfilePhone, function (phone)
{
return phone;
}));
//For Address
var currentProfileAddress = $.grep(AddressDTO, function (e)
{ return e.ProfileId == profileId; });
self.addresses =
ko.observableArray(ko.utils.arrayMap(currentProfileAddress, function
(address) {
return address;
}));
}
self.addPhone = function () { self.phoneNumbers.push(new
PhoneLine()) };
self.removePhone = function (phone)
{ self.phoneNumbers.remove(phone) };
self.addAddress = function () { self.addresses.push(new
AddressLine()) };
self.removeAddress = function (address)
{ self.addresses.remove(address) };
self.backToProfileList = function () { window.location.href =
'/contact'; };
self.saveProfile = function () {
self.profile().AddressDTO = self.addresses;
self.profile().PhoneDTO = self.phoneNumbers;
alert("Date to save is : " +
JSON.stringify(ko.toJS(self.profile())));
};
};
ko.applyBindings(new ProfileCollection());
</td>
<td>
<input class="input-large" data-bind='value: LastName'
placeholder="Last Name"/>
</td>
<td>
<input class="input-large" data-bind='value: Email'
placeholder="Email" />
</td>
</tr>
</tbody>
</table>
<table class="table">
<tr>
<th colspan="3">Phone Information</th>
</tr>
<tr></tr>
<tbody data-bind='foreach: phoneNumbers'>
<tr>
<td>
<select data-bind="options: PhoneTypeData, value:
PhoneTypeId, optionsValue: 'PhoneTypeId', optionsText: 'Name',
optionsCaption: 'Select Phone Type...'"></select>
</td>
<td>
<input class="input-large" data-bind='value: Number'
placeholder="Number" />
</td>
<td>
<a class="btn btn-small btn-danger" href='#' data-bind='
click: $parent.removePhone'>X</a>
</td>
</tr>
</tbody>
</table>
<p>
<button class="btn btn-small btn-primary" data-bind='click: addPhone'>Add
New Phone</button>
</p>
<hr />
<table class="table">
<tr><th colspan="5">Address Information</th></tr>
<tbody data-bind="foreach: addresses">
<tr>
<td colspan="5">
<select data-bind="options: AddressTypeData, value:
AddressTypeId, optionsValue: 'AddressTypeId', optionsText: 'Name',
optionsCaption: 'Select Address Type...'"></select>
</td>
</tr>
<tr>
<td>
<input class="input-large" data-bind='value: AddressLine1'
placeholder="Address Line1" />
<p style="padding-top: 5px;"><input class="input-large"
data-bind='value: State' placeholder="State" /></p>
</td>
<td>
<input class="input-large" data-bind=' value: AddressLine2'
placeholder="Address Line2" />
Screen 2: Create New Contact - This screen should display one blank screen to provide
functionality as.
Screen 3: Update Existing Contact - This screen should display screen with selected contact
information details.
Validation:
We are almost done with designing part of our application .The only thing left now, is to
manage validation when the user clicks on Save button. Validation is the major requirement
and now a days most ignorant part for any web application. By proper validation, user can
know what data needs to be entered. Next in this article, I am going to discuss KnockoutJS
Validation library which can be downloaded using NuGet. Let us check some of the most
common validation scenarios, and how to achieve it using knockout validation.
Scenario 1: First Name is a required field in form
this.FirstName = ko.observable().extend({ required: true });
Scenario 2: Maximum number of character for First Name should not exceed 50 and
should not be less than 3 character
Scenario 10: Phone number should accept only -+ () 0-9 from users
var regex = /\(?([0-9]{3})\)?([ .-]?)([0-9]{3})\2([0-9]{4})/
this.PhoneNumber = ko.observable().extend({ pattern: regex });
So, far we have seen different type of validation scenarios and their syntax; now let us
implement it in our application. For that first download the library knockout.validation.js
using NuGet. Right now our validation script is fully completed and should look like this :
var Profile = function (profile) {
var self = this;
self.ProfileId = ko.observable(profile ? profile.ProfileId :
0).extend({ required: true });
self.FirstName = ko.observable(profile ? profile.FirstName :
'').extend({ required: true, maxLength: 50 });
self.LastName = ko.observable(profile ? profile.LastName : '').extend({
required: true, maxLength: 50 });
self.Email = ko.observable(profile ? profile.Email :
'').extend({ required: true, maxLength: 50, email: true });
After validation our final solution looks like below screen after click on Save button:
So far, we talked about achieving our UI without knowing any actual implementation
(databases interaction) i.e. UI is created independently by any designer/developer without
knowing the actual business logic. !!!Thats great!!!
Next, I will talk about how to design database and how to implement business logic using
structured layers.
To achieve the contact manager functionality, below database design has been used.
Next we will discuss the overall structure for our application, in terms of the logical group of
components into separate layers, which communicates with each other with/without any
restrictions and each logic has its own goals. Layers are an architectural style, and it resolves
the maintenance and enhancement problems in the long run.
So let us proceed with adding a class library in our solution and name it as
Application.Common.
Application.Common :
This is a class library application, with some common functionality and can be used by
different logical layers. For e.g. Security, Logging, Tracking, Validation etc. The components
defined in this layer can not only reuse by other layers in this solution, but can also be utilize
by other applications. To make it easier to change in future, we can use Dependency Injection
and abstraction, with minimal change in our application.
For example, In this layer we are going to use, a validator component to validate data entry,
and custom Logger to log error or warning.
Below is the screen shot for Solution folder after adding common class library :
Next, we will add a class library in our solution and name it as Application.Core.
Application.Core:
The components of this layer implement the system core functionality and encapsulate all the
relevant business logic. Basically, this layer usually contains classes which implement the
domain logic within their methods. This layer also defined a Unit of Work contract within the
Core Layer so it will be able to comply with the PI principle. The primary goal is to
differentiate and clearly separate the behavior of the core domain/business and the
infrastructure implementation details such as data access and specific repositories linked to a
particular technology such as ORM, or simply data access libraries or even cross-cutting
aspects of the architecture. Thus, by isolating the application Core functionality, we will
drastically increase the maintainability of our system and we could even replace the lower
layers (data access, ORM, and databases) with low impact to the rest of the application.
Next, we will add a class library in our solution and name it as Application.DAL.
Application.DAL:
The responsibility of DAL is to and provides data access and persistence operations against
the storage database; maintain multiple sessions and connection with multiple database, etc.
The Primary goal here is to wrap the EF Context with an Interface/Contract so we can use it
from the Manger and Core Layers with no direct dependencies to EF. The data persistence
components provide access to the data hosted within the boundaries of our system (e.g., our
main database which is within a specific BOUNDED CONTEXT), and also to the data
exposed outside the boundaries of our system, such as Web services of external systems.
Therefore, it has components like Repositories that provide such functionality to access the
data hosted within the boundaries of our system, or Service Agents that will consume Web
Services exposed by other external back-end systems. In addition, this layer will usually have
base classes/components with reusable code for all the repository classes.
Next, we will add a class library in our solution and name it as Application. Repository.
Application.Repository:
This is a Class Library and can be accessible only by Application.Manager. For each main
root ENTITY in our domain, we need to create one repository. Basically, Repositories are
classes/components that encapsulate the logic required to access the application data sources.
Therefore, they centralize common data access functionality so the application can have a
better maintainability and de-coupling between technology and logic owned by the
Manager and Core layers.
Next, we will add a class library in our solution and name it as Application. DTO.
Application.DTO:
Again this is a class library, which contains different container classes that exposes properties
but no methods and communicate between Presentation Layer (Application.Web) and Service
Layer (Application.Manager). A Data Transfer Object is an object that is used to encapsulate
data, and send it from one subsystem of an application to another. Here we are going to use
DTOs by the Manager layer to transfer data between itself and the UI layer. The main benefit
here is that it reduces the amount of data that needs to be sent across the wire in distributed
applications. They also make great models in the MVC pattern. We can also use DTOs to
encapsulate parameters for method calls. This can be useful if a method takes more than 4 or
5 parameters.
Next, we will add a class library in our solution and name it as Application. Manager.
Application.Manager :
This is a Class Library and can be accessible only by Application.Web. For each module we
need to declare one Manager. The primary responsibilities of Manager are to accept request
from UI/Web layer, then communicate with required repositories and manipulate data based
on condition then return back the response. This layer is intermediate between UI and
Repositories.
Application.Web:
In previous article, we have already implemented this layer using Javascript dummy data.
This is independent ASP.NET MVC web application, and contains only User Interface
components, like html, .aspx, cshtml, MVC etc. It can also be any windows application. It
communicates with some methods from manager layer, then evaluate results and choose
whether to show an error or page1 or page2 etc. etc. This layer use javascript to load a model
for the presentation, but the data is processed in the server through an ajax request, so the
server only manage the business logic and the javascript manages the presentation logic.
To better understand how layers are communicating with each other, let us recall the initial
requirement:
1.1 This screen should display all the contacts available in Database.
1.2 User should be able to delete any contact.
1.3 User should able to edit any contact details.
1.4 User should be able to create a new contact.
For both Create New button and Edit link, we only redirect to CreateEdit page with id as 0
for Create new and for edit the profile id of selected row. Below is the code for createProfile
and editProfile from contact.js:
self.createProfile = function () {
window.location.href = '/Contact/CreateEdit/0';
};
self.editProfile = function (profile) {
window.location.href = '/Contact/CreateEdit/' + profile.ProfileId;
};
2.7 Click on Back to Profile button should return back the user to Contact List page.
As discussed in previous implementation, for both Create new and Edit existing
requirement we are using single page as CreateEdit.cshtml, by identifying the URL value for
profileId i.e. if profileId in URL is 0, then it is request for creating a new profile, and if it is
some value, the request is for edit existing profile. Below id the implementation details:
In any case (Create or Edit), on page load we need to initialize data for PhoneType and
AddressType. For that we have one method in ContactController as InitializePageData().
Below is the code in CreateEdit.js to initialize both arrays:
var AddressTypeData;
var PhoneTypeData;
$.ajax({
url: urlContact + '/InitializePageData',
async: false,
dataType: 'json',
success: function (json) {
AddressTypeData = json.lstAddressTypeDTO;
PhoneTypeData = json.lstPhoneTypeDTO;
});
Next, For Edit profile we need to get the profile data, for that we have GetProfileById()
method in our ContactController. We modify our existing CreateEdit.js code as:
$.ajax({
url: urlContact + '/GetProfileById/' + profileId,
async: false,
dataType: 'json',
success: function (json) {
self.profile = ko.observable(new Profile(json));
self.phoneNumbers =
ko.observableArray(ko.utils.arrayMap(json.PhoneDTO, function (phone) {
return phone;
}));
self.addresses =
ko.observableArray(ko.utils.arrayMap(json.AddressDTO, function (address) {
return address;
}));
}
});
Finally, For Save data we have two methods in database. If its Create new the we will call
SaveProfileInformtion() method of ContactController else we will call
UpdateProfileInformation() method. We modify our existing CreateEdit.js code as:
$.ajax({
type: (self.profile().ProfileId > 0 ? 'PUT' : 'POST'),
cache: false,
dataType: 'json',
url: urlContact + (self.profile().ProfileId > 0 ?
'/UpdateProfileInformation?id=' +
self.profile().ProfileId : '/SaveProfileInformation'),
data: JSON.stringify(ko.toJS(self.profile())),
contentType: 'application/json; charset=utf-8',
async: false,
success: function (data) {
window.location.href = '/contact';
},
error: function (err) {
var err = JSON.parse(err.responseText);
var errors = "";
for (var key in err) {
if (err.hasOwnProperty(key)) {
errors += key.replace("profile.", "") + " : " + err[key];
}
}
$("<div></div>").html(errors).dialog({ modal: true,
title: JSON.parse(err.responseText).Message, buttons: { "Ok":
function () { $(this).dialog("close"); } } }).show();
},
complete: function () {
}
});
Conclusion
That's it!!! Hope you enjoy this article. I am not an expert, and also I have not followed the
entire industry standard at the time of writing this article. And that, in a nutshell, is about all I
know to get started designing ASP.NET MVC 4 application. I hope you enjoyed this tutorial
and learned something.
All comment/Vote are more than welcome. , If you have any questions feel free to ask; I
will be glad to talk more in the comments. Thanks for your time!