Another MVC Application
Transcript of Another MVC Application
-
7/24/2019 Another MVC Application
1/43
Another MVC Application: Introduction
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. or this article ! am trying to design a website
for any individuals to maintain their contact details online. "owever, 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.
!n this article ! will tal# about creating and designing $ser !nterface ($!) in such a manner so
that $! will be separated from the business logic, and can be created independently by any
designer%developer. or this part we will use AS&.'et *, +noc#out -uery and ootstrap.later in this article we will discuss more about database design and implementing business
logic using structured layers using S/l Server 0112, Entity ramewor#, and *astle 3indsor
for 4ependency !njection.
Separation of Concern: Primary Objective
5he #ey concept is stripping out most or all logic. 6ogic should not be bound to a page. 3hat
if we need to re7use the logic from one page in another8 !n that case we will be tempted to
copy7and7paste. !f we are doing this then our project will become maintainable. Another
important concept is to separate data access layer from any business logic, as we are planning
to use Entity ramewor# this is less of a problem as E should already have this end separate.
3e should be able to easily move all our E files to another project and simply add a
reference to the projects that need it. elow is the high level design9
-
7/24/2019 Another MVC Application
2/43
And, the final solution will loo# li#e below image in isual Studio 9
Seven Projects in One Solution : Is it required
!t is all about your decision:5he proposed designed will offer some rather relevant
benefits, which include9
Separation of *oncern9 4esign should allow clear and defined layers; means
segregate application into distinct areas whose functionality does not overlap. such
that $!7designers can focus on their job without dealing with the business logic
(Application.3eb), and the core developer can only wor# on main business logic
-
7/24/2019 Another MVC Application
3/43
&roductivity9 !t is easier to add new features to e>isting software, since the structure is
already in place, and the location for every new piece of code is #nown beforehand,
so any issue can be easily identified and separated to cope with comple>ity, and to
achieve the re-uired engineering -uality factors such as ? ? robustness, adaptability,
maintainability, and re7usability.
aintainability9 !t is easier to maintain application, due to clear and defined structure of the
code is visible and #nown, so it@s easier to find bugs and anomalies, and modify them with
minimal ris#.
Adaptability9 'ew 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.
e7usability9 e7usability is another concern on designing any application, because it is one
of the main factors to decrease the total cost of ownership, and our design should consider to
what e>tent we can reuse the created 3eb application and different layers.
!n last section of this article, we will discuss the functionality of each individual layer@s in
details.
!ools " !echnolo#y
5o achieve the final solution, we need below tools%dll9
isual Studio 01B0
AS&.'E5 * C with azor iew Engine
Entity ramewor# D.1
*astle 3indsor for 4!
S/6 Server 0112%01B0
+noc#out.js /uery
*astle 3indsor for 4!
ootstrap *SS
$hat %e are tryin# to achieve: &equirement
Screen ': Contact (ist ) Vie% all contacts
B.B 5his screen should display all the contacts available in 4atabase.
B.0 $ser should be able to delete any contact.B.F $ser should able to edit any contact details.
-
7/24/2019 Another MVC Application
4/43
B.C $ser should be able to create a new contact.
!nitial s#etch9
Screen *: Create +e% Contact
5his screen should display one blan# screen to provide functionality as.
0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address.
0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.
0.F $ser should able to remove any phone number.
0.C $ser should able to add any number of Addresses by clic#ing on Add new address.0.D $ser should able to remove any address.
0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in
*ontact 6ist page.
0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.
-
7/24/2019 Another MVC Application
5/43
!nitial s#etch9
Screen ,: -pdate ./istin# Contact
5his screen should display screen with selected contact information details.
F.B $ser should be able to modify his%her irst name, 6ast 'ame and Email Address.
F.0 $ser should able to modify %delete%Add any number of &hone numbers by clic#ing on
Add numbers or delete lin#.
F.F $ser should able to modify %delete%Add any number of Addresses by clic#ing on Add new
address or delete lin#.
F.C *lic# on save button should update *ontact details in 4atabase and user will return bac#
in *ontact 6ist page.
F.D *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.
-
7/24/2019 Another MVC Application
6/43
!nitial S#etch9
Part ': Create $eb Application 01noc2out3js4 Asp3+etMVC and 5ootstrap6: 7or 8esi#ners
efore #ic#7off the $! part, let us see what benefits we are getting using +noc#outjs and
ootstrap along with AS&.'E5 * C.
$hy 1noc2outjs:+noc#out is an pattern that wor#s with a javascript iewodel.
5he reason this wor#s well with * is that serialization to and from javascript models in
S=' is very simple, and it is also included with * C. !t allows us to develop rich $!ible "56, *SS, and avascript for
popular user interface components and interactions. !t comes with bundles of *SS styles,
*omponents and avascript plugins. !t provides *ross platform support, which eliminates
major layout inconsistencies. Everything just wor#sI Jood documentation and the 5witter
ootstrapample. And, finally, it saves
me plenty of times, as it cut the development time to half with very less testing and almost
zero browser issues. Some other benefits we can get by using this framewor# are9
B07*olumn Jrid, fi>ed layout, fluid or responsive layout.
-
7/24/2019 Another MVC Application
7/43
ase *SS for 5ypography, code (synta> highlighting with Joogle prettify), 5ables,
orms, uttons and uses !cons by Jlpyhicons .
3eb $! *omponents li#e uttons, 'avigation menu, 6abels, 5humbnails, Alerts,
&rogress bars and misc.
avascript plugins for odal, 4ropdown, Scrollspy, 5ab, 5ooltip, &opover, Alert,
utton, *ollapse, *arousel and 5ypehead.
!n below steps, we will wor# through the layout and design to build $! for above re-uirement
by using dummy javascript data.
Step ':
*reate a new project as lan# Solution; name it as KApplicationL
Step *:
ight *lic# on Solution folder and add new &roject of type AS&.'E5 * C as an !nternet
Application 5emplate with iew engine as azor.
-
7/24/2019 Another MVC Application
8/43
-
7/24/2019 Another MVC Application
9/43
After Step 0 7 the project structure should loo# li#e the below image
Step ,:
ight clic# on eferences and clic# on anage 'uJet &ac#ages. 5ype ootstrap on search
bar then clic# on !nstall button.
Step 9:
-
7/24/2019 Another MVC Application
10/43
add below line of code into undle*onfig.cs file under AppMStart folder to add +noc#outjs
and ootstrap for every page
bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
"~/Scripts/knockout-{version}.js"))
bundles.Add(new
St!leBundle("~/ontent/css").Include("~/ontent/bootstr#p.css"))
Also in M6ayout,cshtml file under iews%Shared folder add below line to register #noc#out
files as 9
$Scripts.%ender("~/bundles/knockout")
Step :
Add a new folder name as *ontact inside iews, and then add !nde>.cshtml as new iewpage. 5hen add a new *ontroller name it *ontact*ontroller.cs inside *ontroller folder, and
add a new *ontact.js file under Scripts folder. efer to below image.
-
7/24/2019 Another MVC Application
11/43
Step ;:
inally modify the default map route in oute.config to point to *ontact controller as9
routes.p%oute(
n#'e "e*#ult"+ url "{controller}/{#ction}/{id}"+
de*#ults new { controller , "ont#ct"+ #ction , "Inde"+
id , rl#r#'eter.0ption#l }
)
And also modify the M6ayout.cshtml file inside iew%Shared as per the ootStrap Synta>.
elow is the modified code9120345 6t'l7
16t'l l#n8,"en"7
16e#d7
1'et# c6#rset,"ut*-9" /7
1title7$:iewB#8.3itle - ont#ct '#n#8er1/title7 1link 6re*,"~/*#vicon.ico" rel,"s6ortcut icon" t!pe,"i'#8e/-icon" /7
1'et# n#'e,"viewport" content,"widt6,device-widt6" /7
$Scripts.%ender("~/bundles/j;uer!")
$Scripts.%ender("~/bundles/knockout")
$St!les.%ender("~/ontent/css")
$Scripts.%ender("~/bundles/'oderni
-
7/24/2019 Another MVC Application
12/43
'ow we are done with initial setup to run the application. 5he output is as below9
3e will use this page to display the re-uirement for Screen B i.e. *ontact 6ist 7 iew all
contacts
Step =:
irst we will create a dummy profile data as array in *ontact.js (later we will fetch it from
database), and then we will use this data to populate Jrid.
v#r u''!ro*ile , @
{
"ro*ileId" +
"irst?#'e" "An#nd"+
"C#st?#'e" "#nde!"+
"5'#il" "#n#nd$#n#ndp#nde!.co'"
}+ {
"ro*ileId" D+
"irst?#'e" "Eo6n"+
"C#st?#'e" "en#"+
"5'#il" "jo6n$cen#.co'"
}
F
'e>t, we will create &rofilesiewodel, a viewmodel class which hold &rofiles, an array
holding an initial collection of 4ummy&rofile data. 'ote that it
-
7/24/2019 Another MVC Application
13/43
Step >:
'e>t we will write code in !nde>.cshtml page, that
-
7/24/2019 Another MVC Application
14/43
Add one button for *reate new profile and bind it with clic# event using create&rofile
function.
So out final code for !nde>.cshtml is 9
1input t!pe,"button" cl#ss,"btn btn-s'#ll btn-pri'#r!" v#lue,"?ewont#ct" d#t#-bind,"clickGroot.cre#tero*ile" /7
16r /7
1t#ble cl#ss,"t#ble t#ble-striped t#ble-bordered t#ble-condensed"7
1tr7
1t67irst ?#'e1/t67
1t67C#st ?#'e1/t67
1t675'#il1/t67
1t671/t67
1/tr7
1tbod! d#t#-bind,"*ore#c6 ro*iles"7
1tr7
1td cl#ss,"n#'e"71# d#t#-bind,"tet irst?#'e+ click
Gp#rent.editro*ile"71/#71/td7 1td d#t#-bind,"tet C#st?#'e"71/td7
1td d#t#-bind,"tet 5'#il"71/td7
1td71button cl#ss,"btn btn-'ini btn-d#n8er" d#t#-bind,"click
Gp#rent.re'overo*ile"7re'ove1/button71/td7
1/tr7
1/tbod!7
1/t#ble7
1script src,"~/Scripts/ont#ct.js"71/script7
And output is9
'one of the added button and lin# will wor#, because we have not written any code for that,
so let that in ne>t step.
Step '':
Add events for create&rofile, edit&rofile and remove&rofile in *ontact.js
sel*.cre#tero*ile , *unction () {
#lert("re#te # new pro*ile")
}
sel*.editro*ile , *unction (pro*ile) {
-
7/24/2019 Another MVC Application
15/43
#lert("5dit tis pro*ile wit6 pro*ile id #s " H pro*ile.ro*ileId)
}
sel*.re'overo*ile , *unction (pro*ile) {
i* (con*ir'("Are !ou sure !ou w#nt to delete t6is pro*ile")) {
sel*.ro*iles.re'ove(pro*ile)
} }
'ow when we run our application and clic# on emove button, then it will remove that
profile from current array. As we define this array as observable, the $! will be #ept in sync
with changes to that array. 5ry it by clic#ing on emove button. Edit lin# and *reate &rofile
button will simply display the alert. So, let us implemented this functionality in ne>t steps9
Step '*:
'e>t we will add9
A new azor iew inside iews%*ontact as *reateEdit.cshtml, and register it in
*ontact*ontroller.cs class.
public Action%esult re#te5dit()
{
return :iew()
}
A new js file named as *reateEdit.js inside Scripts folder.
odify create&rofile and edit&rofile method of *ontact.js, so that it will point to the
*reateEdit page.
sel*.cre#tero*ile , *unction () {
window.loc#tion.6re* , J/ont#ct/re#te5dit/KJ
}
sel*.editro*ile , *unction (pro*ile) {
window.loc#tion.6re* , J/ont#ct/re#te5dit/J H pro*ile.ro*ileId
}
unning the application will now wor# for all the events in *ontact 6ist (screen 7B). And
create and Edit should redirect to *reateEdit page with re-uired parameters.
Step ',:
irst we will start with adding &rofile !nformation to this *reateEdit page. or that we need
to do9
3e need to get profile!d from url so, add below two lines on top of the *reateEdit.js page
v#r url , window.loc#tion.p#t6n#'e
-
7/24/2019 Another MVC Application
16/43
v#r pro*ileId , url.substrin8(url.l#stInde0*(J/J) H )
e*ine # du''! ro*ile d#t# #s #rr#! in re#te5dit.js
oll#pse L op! ode
v#r u''!ro*ile , @
{
"ro*ileId" +
"irst?#'e" "An#nd"+ "C#st?#'e" "#nde!"+
"5'#il" "#n#nd$#n#ndp#nde!.co'"
}+
{
"ro*ileId" D+
"irst?#'e" "Eo6n"+
"C#st?#'e" "en#"+
"5'#il" "jo6n$cen#.co'"
}
F
&rofile, a simple avaScript class constructor that stores a profile@s irst'ame, 6ast'ame and
Email selection.
v#r ro*ile , *unction (pro*ile) {
v#r sel* , t6is
sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId K)
sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e JJ)
sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ)
sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ)
}
&rofile*ollection, a viewmodel class that holds profile (a avaScript object providing &rofiledata) along with binding for save&rofile and bac#5o&rofile6ist events.
v#r ro*ileollection , *unction () {
v#r sel* , t6is
//i* ro*ileId is K+ It 'e#ns re#te new ro*ile
i* (pro*ileId ,, K) {
sel*.pro*ile , ko.observ#ble(new ro*ile())
}
else {
v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return
e.ro*ileId ,, pro*ileId })
sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF)) }
sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,
J/cont#ctJ }
sel*.s#vero*ile , *unction () {
#lert("#te to s#ve is " H
ES0?.strin8i*!(ko.toES(sel*.pro*ile())))
}
}
And finally, activate +noc#out using #o.applyindings().
-
7/24/2019 Another MVC Application
17/43
ko.#ppl!Bindin8s(new ro*ileollection())
Step '9:
'e>t we will write code in *reateEdit.cshtml page, thatisting record with profile !d B9
-
7/24/2019 Another MVC Application
18/43
$pdate any e>isting record and clic# on save with give below output9
As per re-uirement for this screen, we already have done9
0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address
0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in
*ontact 6ist page.
0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.
!n ne>t step we will try to achieve below re-uirement9
0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.
0.F $ser should able to remove any phone number
Step ':
5o achieve re-uirement no. 0.0 and 0.F, we need to do9
4efine a dummy &hone5ype and &hone45= data as an array in *reateEdit.js.
phone5ype4ata will be used to bind dropdown to define the &hone 5ype for a particular
&hone number.
v#r p6one3!pe#t# , @
{
"6one3!peId" +
-
7/24/2019 Another MVC Application
19/43
"?#'e" "Mork 6one"
}+
{
"6one3!peId" D+
"?#'e" "erson#l 6one"
}
Fv#r 6one30 , @
{
"6oneId"+
"6one3!peId" +
"ro*ileId"+
"?u'ber" "-DDD-===="
}+
{
"6oneId" D+
"6one3!peId" D+
"ro*ileId" +
"?u'ber" "NNN-OOO-PPPP" }
F
&rofile, a simple avaScript class constructor that stores a &hone6ine information which
include its &hone 5ype i.e. whether its K3or# &honeL or K"ome &honeL etc. along with phone
number.
v#r 6oneCine , *unction (p6one) {
v#r sel* , t6is
sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K)
sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId K)
sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber JJ)}
odify &rofile*ollection viewmodel class to also holds phone'umbers along with binding
for add&hone and remove&hone events.
v#r ro*ileollection , *unction () {
v#r sel* , t6is
//i* ro*ileId is K+ It 'e#ns re#te new ro*ile
i* (pro*ileId ,, K) {
sel*.pro*ile , ko.observ#ble(new ro*ile())
sel*.p6one?u'bers , ko.observ#bleArr#!(@new 6oneCine()F)
}
else {
v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return
e.ro*ileId ,, pro*ileId })
sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF))
v#r currentro*ile6one , G.8rep(6one30+ *unction (e) { return
e.ro*ileId ,, pro*ileId })
sel*.p6one?u'bers ,
ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ile6one+ *unction (p6one)
{
return p6one
}))
}
sel*.#dd6one , *unction () {
-
7/24/2019 Another MVC Application
20/43
sel*.p6one?u'bers.pus6(new 6oneCine())
}
sel*.re'ove6one , *unction (p6one)
{ sel*.p6one?u'bers.re'ove(p6one) }
sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,J/cont#ctJ }
sel*.s#vero*ile , *unction () {
#lert("#te to s#ve is " H
ES0?.strin8i*!(ko.toES(sel*.pro*ile())))
}
}
Step ';:
'e>t we will add one more section to add &hone !nformation in *reateEdit.cshtml page,that
-
7/24/2019 Another MVC Application
21/43
And to edit e>isting *ontact is9
So now we left only with below re-uirement9
0.C $ser should able to add any number of Addresses by clic#ing on Add new address.
0.D $ser should able to remove any address
Step BH9 5he re-uirement no. 0.C and 0.D are similar to &hone !nformation, below is the final
code for *reateEdit.js and *reateEdit.cshtml files9
or *reateEdit.js
v#r url , window.loc#tion.p#t6n#'e
v#r pro*ileId , url.substrin8(url.l#stInde0*(J/J) H )
-
7/24/2019 Another MVC Application
22/43
v#r u''!ro*ile , @
{
"ro*ileId" +
"irst?#'e" "An#nd"+
"C#st?#'e" "#nde!"+
"5'#il" "#n#nd$#n#ndp#nde!.co'"
}+ {
"ro*ileId" D+
"irst?#'e" "Eo6n"+
"C#st?#'e" "en#"+
"5'#il" "jo6n$cen#.co'"
}
F
v#r 6one3!pe#t# , @
{
"6one3!peId" +
"?#'e" "Mork 6one"
}+ {
"6one3!peId" D+
"?#'e" "erson#l 6one"
}
F
v#r 6one30 , @
{
"6oneId"+
"6one3!peId" +
"ro*ileId"+
"?u'ber" "-DDD-===="
}+ {
"6oneId" D+
"6one3!peId" D+
"ro*ileId" +
"?u'ber" "NNN-OOO-PPPP"
}
F
v#r Address3!pe#t# , @
{
"Address3!peId" +
"?#'e" "S6ippin8 Address"
}+ {
"Address3!peId" D+
"?#'e" "Billin8 Address"
}
F
v#r Address30 , @
{
"AddressId" +
"Address3!peId" +
"ro*ileId" +
"AddressCine" "KKKK %ic6'ond Avenue"+
"AddressCineD" "Apt Q KKK"+ "ountr!" "SA"+
-
7/24/2019 Another MVC Application
23/43
"St#te" "3e#s"+
"it!" "ouston"+
"Tipode" "UKKKK"
}+
{
"AddressId" D+
"Address3!peId" D+ "ro*ileId" +
"AddressCine" "DKKKK i86w#! P"+
"AddressCineD" "Suite Q DKKK"+
"ountr!" "SA"+
"St#te" "3e#s"+
"it!" "ouston"+
"Tipode" "9KKKK"
}
F
v#r ro*ile , *unction (pro*ile) {
v#r sel* , t6is
sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId K)
sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e JJ)
sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ)
sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ)
sel*.6one30 , ko.observ#bleArr#!(pro*ile pro*ile.6one30 @F)
sel*.Address30 , ko.observ#bleArr#!(pro*ile pro*ile.Address30
@F)
}
v#r 6oneCine , *unction (p6one) {
v#r sel* , t6is
sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K) sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId K)
sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber JJ)
}
v#r AddressCine , *unction (#ddress) {
v#r sel* , t6is
sel*.AddressId , ko.observ#ble(#ddress #ddress.AddressId K)
sel*.Address3!peId , ko.observ#ble(#ddress #ddress.Address3!peId
K)
sel*.AddressCine , ko.observ#ble(#ddress #ddress.AddressCine JJ)
sel*.AddressCineD , ko.observ#ble(#ddress #ddress.AddressCineD JJ)
sel*.ountr! , ko.observ#ble(#ddress #ddress.ountr! JJ)
sel*.St#te , ko.observ#ble(#ddress #ddress.St#te JJ) sel*.it! , ko.observ#ble(#ddress #ddress.it! JJ)
sel*.Tipode , ko.observ#ble(#ddress #ddress.Tipode JJ)
}
v#r ro*ileollection , *unction () {
v#r sel* , t6is
//i* ro*ileId is K+ It 'e#ns re#te new ro*ile
i* (pro*ileId ,, K) {
sel*.pro*ile , ko.observ#ble(new ro*ile())
sel*.p6one?u'bers , ko.observ#bleArr#!(@new 6oneCine()F)
sel*.#ddresses , ko.observ#bleArr#!(@new AddressCine()F)
}
-
7/24/2019 Another MVC Application
24/43
else {
//or ro*ile in*or'#tion
v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return
e.ro*ileId ,, pro*ileId })
sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF))
//or 6one nu'ber
v#r currentro*ile6one , G.8rep(6one30+ *unction (e) { returne.ro*ileId ,, pro*ileId })
sel*.p6one?u'bers ,
ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ile6one+ *unction (p6one)
{
return p6one
}))
//or Address
v#r currentro*ileAddress , G.8rep(Address30+ *unction (e)
{ return e.ro*ileId ,, pro*ileId })
sel*.#ddresses ,
ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ileAddress+ *unction
(#ddress) {
return #ddress }))
}
sel*.#dd6one , *unction () { sel*.p6one?u'bers.pus6(new
6oneCine()) }
sel*.re'ove6one , *unction (p6one)
{ sel*.p6one?u'bers.re'ove(p6one) }
sel*.#ddAddress , *unction () { sel*.#ddresses.pus6(new
AddressCine()) }
sel*.re'oveAddress , *unction (#ddress){ sel*.#ddresses.re'ove(#ddress) }
sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,
J/cont#ctJ }
sel*.s#vero*ile , *unction () {
sel*.pro*ile().Address30 , sel*.#ddresses
sel*.pro*ile().6one30 , sel*.p6one?u'bers
#lert("#te to s#ve is " H
ES0?.strin8i*!(ko.toES(sel*.pro*ile())))
}
}
ko.#ppl!Bindin8s(new ro*ileollection())
and, for *reateEdit.cshtml
1t#ble cl#ss,"t#ble"7
1tr7
1t6 colsp#n,"="7ro*ile In*or'#tion1/t67
1/tr7
1tr71/tr7
1tbod! d#t#-bind,Jwit6 pro*ileJ7
1tr7
1td7 1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue irst?#'eJ
pl#ce6older,"irst ?#'e"/7
-
7/24/2019 Another MVC Application
25/43
1/td7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue C#st?#'eJ
pl#ce6older,"C#st ?#'e"/7
1/td7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue 5'#ilJpl#ce6older,"5'#il" /7
1/td7
1/tr7
1/tbod!7
1/t#ble7
1t#ble cl#ss,"t#ble"7
1tr7
1t6 colsp#n,"="76one In*or'#tion1/t67
1/tr7
1tr71/tr7
1tbod! d#t#-bind,J*ore#c6 p6one?u'bersJ7
1tr7 1td7
1select d#t#-bind,"options 6one3!pe#t#+ v#lue
6one3!peId+ options:#lue J6one3!peIdJ+ options3et J?#'eJ+
options#ption JSelect 6one 3!pe...J"71/select7
1/td7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue ?u'berJ
pl#ce6older,"?u'ber" /7
1/td7
1td7
1# cl#ss,"btn btn-s'#ll btn-d#n8er" 6re*,JQJ d#t#-bind,J
click Gp#rent.re'ove6oneJ7R1/#7
1/td7 1/tr7
1/tbod!7
1/t#ble7
1p7
1button cl#ss,"btn btn-s'#ll btn-pri'#r!" d#t#-bind,Jclick #dd6oneJ7Add
?ew 6one1/button7
1/p7
16r /7
1t#ble cl#ss,"t#ble"7
1tr71t6 colsp#n,"O"7Address In*or'#tion1/t671/tr7
1tbod! d#t#-bind,"*ore#c6 #ddresses"7
1tr7
1td colsp#n,"O"7 1select d#t#-bind,"options Address3!pe#t#+ v#lue
Address3!peId+ options:#lue JAddress3!peIdJ+ options3et J?#'eJ+
options#ption JSelect Address 3!pe...J"71/select7
1/td7
1/tr7
1tr7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue AddressCineJ
pl#ce6older,"Address Cine" /7
1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"
d#t#-bind,Jv#lue St#teJ pl#ce6older,"St#te" /71/p7
1/td7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,J v#lue AddressCineDJ
pl#ce6older,"Address CineD" /7
-
7/24/2019 Another MVC Application
26/43
1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"
d#t#-bind,Jv#lue ountr!J pl#ce6older,"ountr!" /71/p7
1/td7
1td7
1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue it!J
pl#ce6older,"it!" /7
1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"d#t#-bind,Jv#lue TipodeJ pl#ce6older,"Tip ode" /7
1# cl#ss,"btn btn-s'#ll btn-d#n8er" 6re*,JQJ d#t#-
bind,Jclick Groot.re'oveAddressJ7R1/#71/p7
1/td7
1/tr7
1/tbod!7
1/t#ble7
1p7
1button cl#ss,"btn btn-s'#ll btn-pri'#r!" d#t#-bind,Jclick #ddAddressJ7Add
?ew Address1/button7
1/p7
16r /7
1button cl#ss,"btn btn-s'#ll btn-success" d#t#-bind,Jclicks#vero*ileJ7S#ve ro*ile1/button7
1input cl#ss,"btn btn-s'#ll btn-pri'#r!" t!pe,"button" v#lue,"B#ck 3o
ro*ile Cist" d#t#-bind,"clickGroot.b#ck3oro*ileCist" /7
1script src,"~/Scripts/re#te5dit.js"71/script7
So finally, Application will display screens as per the re-uirement9
Screen B9 *ontact 6ist 7 iew all contacts
Screen 09 *reate 'ew *ontact 7 5his screen should display one blan# screen to providefunctionality as.
-
7/24/2019 Another MVC Application
27/43
Screen F9 $pdate E>isting *ontact 7 5his screen should display screen with selected contact
-
7/24/2019 Another MVC Application
28/43
information details.
Validation:
3e are almost done with designing part of our application .5he only thing left now, is to
manage validation when the user clic#s on KSaveL button. alidation is the major re-uirement
and now a day@s most ignorant part for any web application. y proper validation, user can
#now what data needs to be entered. 'e>t in this article, ! am going to discuss +noc#outS
alidation library which can be downloaded using 'uJet. 6et us chec# some of the most
common validation scenarios, and how to achieve it using #noc#out validation.
Scenario ': 7irst +ame is a required field in form
this.irst'ame P #o.observable().e>tend(Q re-uired9 true R);
Scenario *: Ma/imum number of character for 7irst +ame should not e/ceed ? andshould not be less than , character
-
7/24/2019 Another MVC Application
29/43
this.irst'ame P #o.observable().e>tend(Q ma>6ength9 D1, min6ength9FR);
Scenario 9: A#e is a required field in form4 and should be al%ays #reater than '= and
less than '??
this.Age P #o.observable().e>tend(Q re-uired9 true, ma>9 B11, min9B2 R);
Scenario : .mail is a required field and should be in proper email format
this.Email P #o.observable().e>tend(Q re-uired9 true, email9 true R);
Scenario ;: 8ate of 5irth is a required field and should be in proper date format
this.4ate=firth P #o.observable().e>tend(Q re-uired9 true, date9 true R);
Scenario tend(Q re-uired9 true, number9 true R);
Scenario =: Phone +umber is a required field and should be in proper -S format
'ote9 A valid $S phone number has the following format9 B>dd>dddddd
5he TBT at the beginning of the string is optional and so are the dashes. > is any digit
between 0 and U while d can be any digit.
this.&hone P #o.observable().e>tend(Q re-uired9 true, phone$S9 true R);
Scenario >: !o8ate field must be #reater than 7rom8ate fieldt6is.3o#te , ko.observ#ble().etend({
e;u#l *unction () { return (v#l 7 G(VQro'#teW).v#l()) v#l v#l H
"L" }
})
Scenario '?: Phone number should accept only )@ 06 ?)> from usersv#r re8e , /X((@K-YF{=})X)(@ .-F)(@K-YF{=})XD(@K-YF{N})/
t6is.6one?u'ber , ko.observ#ble().etend({ p#ttern re8e })
So, far we have seen different type of validation scenarios and their synta>; now let us
implement it in our application. or that first download the library #noc#out.validation.js
using 'uJet. ight now our validation script is fully completed and should loo# li#e this 9
v#r ro*ile , *unction (pro*ile) {
v#r sel* , t6is
sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId
K).etend({ re;uired true })
sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e
JJ).etend({ re;uired true+ '#Cen8t6 OK })
sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ).etend({
re;uired true+ '#Cen8t6 OK })
sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ).etend({ re;uired true+ '#Cen8t6 OK+ e'#il true })
-
7/24/2019 Another MVC Application
30/43
sel*.6one30 , ko.observ#bleArr#!(pro*ile pro*ile.6one30 @F)
sel*.Address30 , ko.observ#bleArr#!(pro*ile pro*ile.Address30
@F)
}
v#r 6oneCine , *unction (p6one) {
v#r sel* , t6is sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K)
sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId
unde*ined).etend({ re;uired true })
sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber
JJ).etend({ re;uired true+ '#Cen8t6 DO+ p6oneS true })
}
v#r AddressCine , *unction (#ddress) {
v#r sel* , t6is
sel*.AddressId , ko.observ#ble(#ddress #ddress.AddressId K)
sel*.Address3!peId , ko.observ#ble(#ddress #ddress.Address3!peId
unde*ined).etend({ re;uired true })
sel*.AddressCine , ko.observ#ble(#ddress #ddress.AddressCine JJ).etend({ re;uired true+ '#Cen8t6 KK })
sel*.AddressCineD , ko.observ#ble(#ddress #ddress.AddressCineD
JJ).etend({ re;uired true+ '#Cen8t6 KK })
sel*.ountr! , ko.observ#ble(#ddress #ddress.ountr!
JJ).etend({ re;uired true+ '#Cen8t6 OK })
sel*.St#te , ko.observ#ble(#ddress #ddress.St#te
JJ).etend({ re;uired true+ '#Cen8t6 OK })
sel*.it! , ko.observ#ble(#ddress #ddress.it!
JJ).etend({ re;uired true+ '#Cen8t6 OK })
sel*.Tipode , ko.observ#ble(#ddress #ddress.Tipode
JJ).etend({ re;uired true+ '#Cen8t6 O })
}
After validation our final solution loo#s li#e below screen after clic# on KSaveL button9
-
7/24/2019 Another MVC Application
31/43
So far, we tal#ed about achieving our $! without #nowing any actual implementation
(databases interaction) i.e. $! is created independently by any designer%developer without#nowing the actual business logic. III5hat@s greatIII
'e>t, ! will tal# about how to design database and how to implement business logic using
structured layers.
Part *: Create 8atabase 8esi#n 0S( Server *??= r*6: 7or
85A
rom database design prospective below are the main functionality which need to be
achieved9
A contact can have irst 'ame, 6ast 'ame and Email.
A *ontact can have multiple addresses.
A contact can have multiple phone numbers.
5o achieve the contact manager functionality, below database design has been used.
-
7/24/2019 Another MVC Application
32/43
5he relationship between tables are as per below database diagram9
Part ,: 8esi#n (o#ical (ayers: 7or Core 8evelopers
-
7/24/2019 Another MVC Application
33/43
As discussed earlier, our final structure is9
'e>t 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. 6ayers 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.*ommon.
Application3Common :
5his is a class library application, with some common functionality and can be used by
different logical layers. or e.g. Security, 6ogging, 5rac#ing, alidation etc. 5he componentsdefined in this layer can not only reuse by other layers in this solution, but can also be utilize
by other applications. 5o ma#e it easier to change in future, we can use 4ependency !njection
and abstraction, with minimal change in our application.
or e>ample, !n this layer we are going to use, a validator component to validate data entry,
and custom 6ogger to log error or warning.
elow is the screen shot for Solution folder after adding common class library 9
-
7/24/2019 Another MVC Application
34/43
'e>t, we will add a class library in our solution and name it as Application.*ore.
Application3Core:
5he components of this layer implement the system core functionality and encapsulate all the
relevant business logic. asically, this layer usually contains classes which implement the
domain logic within their methods. 5his layer also defined a $nit of 3or# contract within the
*ore 6ayer so it will be able to comply with the &! principle. 5he 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 lin#ed to aparticular technology such as =, or simply data access libraries or even cross7cutting
aspects of the architecture. 5hus, by isolating the application *ore functionality, we will
drastically increase the maintainability of our system and we could even replace the lower
layers (data access, =, and databases) with low impact to the rest of the application.
'e>t, we will add a class library in our solution and name it as Application.4A6.
Application38A(:
-
7/24/2019 Another MVC Application
35/43
5he responsibility of 4A6 is to and provides data access and persistence operations against
the storage database; maintain multiple sessions and connection with multiple database, etc.
5he &rimary goal here is to wrap the E *onte>t with an !nterface%*ontract so we can use it
from the anger and *ore 6ayers with no direct dependencies to E. 5he 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 =$'4E4 *='5EV5), and also to the datae>posed outside the boundaries of our system, such as 3eb services of e>ternal systems.
5herefore, it has components li#e KepositoriesL that provide such functionality to access the
data hosted within the boundaries of our system, or KService AgentsL that will consume 3eb
Services e>posed by other e>ternal bac#7end systems. !n addition, this layer will usually have
base classes%components with reusable code for all the repository classes.
'e>t, we will add a class library in our solution and name it as Application. epository.
Application3&epository:
5his is a *lass 6ibrary and can be accessible only by Application.anager. or each main
root E'5!5W in our domain, we need to create one repository. asically, epositories are
classes%components that encapsulate the logic re-uired to access the application data sources.
5herefore, they centralize common data access functionality so the application can have a
better maintainability and de7coupling between technology and logic owned by the
KanagerL and K*oreL layers.
'e>t, we will add a class library in our solution and name it as Application. 45=.
-
7/24/2019 Another MVC Application
36/43
Application38!O:
Again this is a class library, which contains different container classes that e>poses properties
but no methods and communicate between &resentation 6ayer (Application.3eb) and Service
6ayer (Application.anager). A 4ata 5ransfer =bject is an object that is used to encapsulate
data, and send it from one subsystem of an application to another. "ere we are going to use
45=s by the anager layer to transfer data between itself and the $! layer. 5he main benefit
here is that it reduces the amount of data that needs to be sent across the wire in distributed
applications. 5hey also ma#e great models in the * pattern. 3e can also use 45=@s to
encapsulate parameters for method calls. 5his can be useful if a method ta#es more than C or
D parameters.
'e>t, we will add a class library in our solution and name it as Application. anager.
Application3Mana#er :
5his is a *lass 6ibrary and can be accessible only by Application.3eb. or each module we
need to declare one anager. 5he primary responsibilities of anager are to accept re-uest
from $!%3eb layer, then communicate with re-uired repositories and manipulate data based
on condition then return bac# the response. 5his layer is intermediate between $! and
-
7/24/2019 Another MVC Application
37/43
epositories.
Application3$eb:
!n previous article, we have already implemented this layer using avascript dummy data.
5his is independent AS&.'E5 * web application, and contains only $ser !nterface
components, li#e html, .asp>, cshtml, * etc. !t can also be any windows application. !t
communicates with some methods from manager layer, then evaluate results and choosewhether to show an error or pageB or page0 etc. etc. 5his layer use javascript to load a model
for the presentation, but the data is processed in the server through an aja> re-uest, so the
server only manage the business logic and the javascript manages the presentation logic.
5o better understand how layers are communicating with each other, let us recall the initial
re-uirement9
Screen ': Contact (ist ) Vie% all contacts
-
7/24/2019 Another MVC Application
38/43
B.B 5his screen should display all the contacts available in 4atabase.
B.0 $ser should be able to delete any contact.
B.F $ser should able to edit any contact details.
B.C $ser should be able to create a new contact.
5o populate the grid data, on page load, we call JetAll&rofiles() method of
*ontact*ontroller. 5his method returns all &rofiles e>ist in database as an S=' object, then
we bind it with avaScript object self.&rofiles. elow is the code to call JetAll&rofiles() from
contact.js9
v#r ro*iles:iew&odel , *unction () {
v#r sel* , t6is
v#r url , "/cont#ct/ZetAllro*iles"
v#r re*res6 , *unction () {
G.8etES0?(url+ {}+ *unction (d#t#) {
sel*.ro*iles(d#t#)
}) }
=n emove button clic# we call 4elete&rofile () method of *ontact*ontroller. 5his method
returns removes that profile from database. elow is the code to call 4elete&rofile() from
contact.js9
sel*.re'overo*ile , *unction (pro*ile) {
i* (con*ir'("Are !ou sure !ou w#nt to delete t6is pro*ile")) {
v#r id , pro*ile.ro*ileId
w#itin8i#lo8({})
G.#j#({
t!pe J5C535J+ url Jont#ct/eletero*ile/J H id+ success *unction () { sel*.ro*iles.re'ove(pro*ile) }+
error *unction (err) {
v#r error , ES0?.p#rse(err.response3et)
G("1div71/div7").6t'l(error.&ess#8e).di#lo8({ 'od#l true+
title "5rror"+ buttons { "0k"
*unction () { G(t6is).di#lo8("close") } } }).s6ow()
}+
co'plete *unction () { closeM#itin8i#lo8() }
})
}
}
or both *reate 'ew button and Edit lin#, we only redirect to *reateEdit page with id as 1
-
7/24/2019 Another MVC Application
39/43
for *reate new and for edit the profile id of selected row. elow is the code for create&rofile
and edit&rofile from contact.js9
sel*.cre#tero*ile , *unction () {
window.loc#tion.6re* , J/ont#ct/re#te5dit/KJ
}
sel*.editro*ile , *unction (pro*ile) {
window.loc#tion.6re* , J/ont#ct/re#te5dit/J H pro*ile.ro*ileId
}
elow is the diagram representation of communication between three major layers9
Screen *: Create +e% Contact
5his screen should display one blan# screen to provide functionalities as.
0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address.
0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.
0.F $ser should able to remove any phone number.
0.C $ser should able to add any number of Addresses by clic#ing on Add new address.
0.D $ser should able to remove any address.
0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in
*ontact 6ist page.
-
7/24/2019 Another MVC Application
40/43
0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.
Screen ,: -pdate ./istin# Contact
5his screen should display screen with selected contact information details.
F.B $ser should be able to modify his%her irst name, 6ast 'ame and Email Address.
F.0 $ser should able to modify %delete%Add any number of &hone numbers by clic#ing on
Add numbers or delete lin#.
F.F $ser should able to modify %delete%Add any number of Addresses by clic#ing on Add new
address or delete lin#.
F.C *lic# on save button should update *ontact details in 4atabase and user will return bac#
in *ontact 6ist page.F.D *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.
-
7/24/2019 Another MVC Application
41/43
As discussed in previous implementation, for both K*reate newL and KEdit e>istingL
re-uirement we are using single page as *reateEdit.cshtml, by identifying the $6 value for
profile!d i.e. if profile!d in $6 is 1, then it is re-uest for creating a new profile, and if it is
some value, the re-uest is for edit e>isting profile. elow id the implementation details9
!n any case (*reate or Edit), on page load we need to initialize data for &hone5ype and
Address5ype. or that we have one method in *ontact*ontroller as !nitialize&age4ata().
elow is the code in *reateEdit.js to initialize both arrays9
v#r Address3!pe#t#
v#r 6one3!pe#t#
G.#j#({
url urlont#ct H J/Initi#li
-
7/24/2019 Another MVC Application
42/43
}
})
'e>t, or Edit profile we need to get the profile data, for that we have Jet&rofiley!d()
method in our *ontact*ontroller. 3e modify our e>isting *reateEdit.js code as9
G.#j#({
url urlont#ct H J/Zetro*ileB!Id/J H pro*ileId+
#s!nc *#lse+
d#t#3!pe JjsonJ+
success *unction (json) {
sel*.pro*ile , ko.observ#ble(new ro*ile(json))
sel*.p6one?u'bers ,
ko.observ#bleArr#!(ko.utils.#rr#!p(json.6one30+ *unction (p6one) {
return p6one
}))
sel*.#ddresses ,
ko.observ#bleArr#!(ko.utils.#rr#!p(json.Address30+ *unction (#ddress) {
return #ddress
}))
}
})
inally, or Save data we have two methods in database. !f it@s *reate new the we will call
Save&rofile!nformtion() method of *ontact*ontroller else we will call
$pdate&rofile!nformation() method. 3e modify our e>isting *reateEdit.js code as9
G.#j#({
t!pe (sel*.pro*ile().ro*ileId 7 K J3J J0S3J)+
c#c6e *#lse+
d#t#3!pe JjsonJ+
url urlont#ct H (sel*.pro*ile().ro*ileId 7 K J/pd#tero*ileIn*or'#tionid,J H
sel*.pro*ile().ro*ileId J/S#vero*ileIn*or'#tionJ)+
d#t# ES0?.strin8i*!(ko.toES(sel*.pro*ile()))+
content3!pe J#pplic#tion/json c6#rset,ut*-9J+
#s!nc *#lse+
success *unction (d#t#) {
window.loc#tion.6re* , J/cont#ctJ
}+
error *unction (err) {
v#r err , ES0?.p#rse(err.response3et)
v#r errors , ""
*or (v#r ke! in err) {
i* (err.6#s0wnropert!(ke!)) { errors H, ke!.repl#ce("pro*ile."+ "") H " " H err@ke!F
}
}
G("1div71/div7").6t'l(errors).di#lo8({ 'od#l true+
title ES0?.p#rse(err.response3et).&ess#8e+ buttons { "0k"
*unction () { G(t6is).di#lo8("close") } } }).s6ow()
}+
co'plete *unction () {
}
})
-
7/24/2019 Another MVC Application
43/43
Conclusion
5hat