This is the fifth part in the series of AngularJS. We have discussed the basic components of AngularJS in our earlier posts. Please find the links of my earlier post on this series as
Learning {{AngularJS}} with Examples–Part 1
Learning {{AngularJS}} with Examples–Part 2
Learning {{AngularJS}} with ASP.NET MVC – Part 3
Learning {{AngularJS}} with ASP.NET MVC – Part 4
So let’s move to series tracker and see what are we going to learn today.
So far, we have discussed five main components. Today we will be discussing Views and Routing.
Earlier whenever we required to display multiple views on a single webpage then we normally put multiple divs on the page and show or hide div using JavaScript or jQuery. One of the main issues with this approach is that the URL does not change as view changes so bookmarking any particular view is not possible. It also becomes tough to maintain these pages as it contains lots of html and JavaScript code as well.
AngularJS provides a robust solution to handle these scenario. First it enables us to help in maintaining multiple views of the page in separate files, that will be used to load the particular view. It also allows us to have different URL based on view. Let’s understand Views and Routing.
Views – To have different views on the page, we have different templates which gets rendered on in ng-view directive based on the routes.
Routing – Routing plays a key role of having multiple views on the same page. It enables us to provide different templates (View) and controller based on URL. On clicking particular url, the corresponding template is loaded based on the assigned controller. Routing is supported by another angular service called $routeprovider.
There is major breaking changes took place in 1.2.*. In this version, routing ($routeprovider) is not part of main angular library and to use it we need to include another plugin (angular-route.js) and while creating the module we need to pass ngModule. We’ll see in coming example.
Note – These Views are similar to ASP.NET MVC views. For each view there is a Controller associated, which uses the View and add required data to render it on the UI.
So let’s discuss the example. We’ll take the last post’s sample and scale it up. In that example, we displayed Talk details on the page. Now we will display speaker details on that page as well. So we’ll have two tabs: Talk Details and Speaker Details. And by clicking on the tabs, corresponding details will be displayed.
So let’s write the server side code first
1- Added a new method GetSpeakers in EventRepository that returns an array of speakers. The code is as
public SpeakerVM[] GetSpeakers() { var speakers = new[] { new SpeakerVM { Id= "S001", Name= "Brij Bhushan Mishra", Expertise= "Client Script, ASP.NET", TalksDelivered= 28 }, new SpeakerVM { Id= "S002", Name= "Dhananjay Kumar", Expertise= "Node.js, WCF", TalksDelivered= 54 }, new SpeakerVM { Id= "S003", Name= "Gaurav", Expertise= "Microsoft Azure", TalksDelivered= 68 } }; return speakers; }
2- Added a new method in GetSpeakerDetails (similar to GetTalkDetails) in EventsController that gets the data from Repository and returns the JSONResult. This method will be called via Angular Service. The method is as
public ActionResult GetSpeakerDetails() { var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; var jsonResult = new ContentResult { Content = JsonConvert.SerializeObject(eventsRepository.GetSpeakers(), settings), ContentType = "application/json" }; return jsonResult; }
Now we have added server side code. Let’s move client side coding
1- Added a new service getSpeakers in Event Service as
getSpeakers: function () { // Get the deferred object var deferred = $q.defer(); // Initiates the AJAX call $http({ method: 'GET', url: '/events/GetSpeakerDetails' }).success(deferred.resolve).error(deferred.reject); // Returns the promise - Contains result once request completes return deferred.promise; }
It is similar to getTalks that we discussed in my last post.
2- As every view has one controller so let’s create another controller for speaker details named as speakerController (speakerController.js) and put the code similar to eventController as
eventModule.controller("speakerController", function ($scope, EventsService) { EventsService.getSpeakers().then(function (speakers) { $scope.speakers = speakers }, function () { alert('error while fetching speakers from server') }) });
3- Now it’s time to create to templates that will be rendered on the UI. For that, I have created Templates folder. In this folder we’ll create templates for talk and speaker. So let’s create an HTML file named as Talk.html and copy the html from Index,html and it looks like as
<div class="container"> <h2>Talk Details</h2> <div class="row"> <table class="table table-condensed table-hover"> <tr> <th>Id</th> <th>Name</th> <th>Speaker</th> <th>Venue</th> <th>Duration</th> </tr> <tr ng-repeat="talk in talks"> <td> {{talk.id}}</td> <td> {{talk.name}}</td> <td> {{talk.speaker}}</td> <td> {{talk.venue}}</td> <td> {{talk.duration}}</td> </tr> </table> </div> </div>
Let’s create similar view for Speaker View as well, It is as
<div class="container"> <h2>Speaker Details</h2> <div class="row"> <table class="table table-condensed table-hover"> <tr> <th>Id</th> <th>Name</th> <th>Expertise</th> <th>Talks Delivered</th> </tr> <tr ng-repeat="speaker in speakers"> <td> {{speaker.id}}</td> <td> {{speaker.name}}</td> <td> {{speaker.expertise}}</td> <td> {{speaker.talksDelivered}}</td> </tr> </table> </div> </div>
4- Let’s go to index.cshtml. Now in this file we’ll have only tabs and one more new item ng-view as
<div class="container"> <div class="navbar navbar-default"> <div class="navbar-header"> <ul class="nav navbar-nav"> <li class="navbar-brand"><a href="#Talks">Talks</a></li> <li class="navbar-brand"><a href="#Speakers">Speakers</a></li> </ul> </div> </div> <div ng-view> </div> </div>
Here I have used navbar to create the navigation links. Please refer the href links, one is for Talks and another for Speakers.
As discussed, inside ng-view the required templates will be rendered.
5-Now we have only one item that is left, to initialize the route that is defined in the module. As mentioned earlier, that to use routing we need to use another plugin (angular-route.js) and initialize in module
var eventModule = angular.module("eventModule", ['ngRoute']).config(function ($routeProvider) { //Path - it should be same as href link $routeProvider.when('/Talks', { templateUrl: '/Templates/Talk.html', controller: 'eventController' }); $routeProvider.when('/Speakers', { templateUrl: '/Templates/Speaker.html', controller: 'speakerController' }); });
As we earlier discussed that module is like container to all the features and services. Route is configured via config method in module. Path of the link should be same as the path used in the navigation links.
Now our application is ready and let’s run this.
Here I ran the application and clicked on the Talks link (red encircled). After the url got changed(refer url) and talks got populated. similarly let’s click on Speakers
When I clicked the speaker the URL also got changed which enables us to bookmark the URL based on the view.
In both the URL, one thing we notice that route that we defined got added with # which also helps in conflicting the other routing like ASP.NET MVC routing.
Hope you have enjoyed this post. To navigate to previous and next post of the series, use the links at the bottom of the post.
Cheers,
Brij
[14th Aug 2015 : Updated this post for Angular version 1.4.*]
Pingback: Learning {{AngularJS}} with ASP.NET MVC – Part 6 | Code Wala
Sort of academically, but no stranger to taming complexity through abstractions, DI, etc, for simple routes this is great. Thoughts concerning the Angular team’s direction re: decoupling routing (I understand they are revamping the whole thing, too), competition between ‘third party’ extensions, like UI Router, Angular Route Segment, etc?
I have downloaded the code.but i’m not getting the url “Events/Talks” which u have given in index.cshtml…could u pls explain me regarding this
Just to reconfirm, I downloaded the code and it is running fine. Try this
Open the url ../Events/
then you will find two menu items – Talks and Speakers
Try clicking on that.
If still face error, then please share error details
Hi Brij, Just now i tried this codes , i have included this code in default mvc project. Then i got some issues with Routing concepts, once i clicked the Angular JS page, then it will not work our mvc default routing concept[ex : i can’t able to see the Home,AboutUs ,Contact pages ]. Please let me know, is there any limitation with both routing concepts?
Please refer https://codewala.net/2015/03/19/asp-net-mvc-and-angular-routing-together-part-11/
After running the application,Talks and Speakers links are not working. Throwing error -The resource cannot be found.
Please refer to new post. https://codewala.net/2015/03/19/asp-net-mvc-and-angular-routing-together-part-11/
Try Cntrl-F5 in browser. You probably have the eventModule.js version of the previous lesson cached. And that version lacks the routing.
Thanks for your comment. I checked and found it working. Routes are already defined in eventModule.js. Can you please let me know what do you see when you run?
Pingback: ASP.NET MVC and Angular Routing together – Part 11 | Code Wala
Pingback: Learning {{AngularJS}} with ASP.NET MVC – Part 4 | Code Wala
Hi Brij,
Very nice explanation.
I have gone through this blog and its just working fine.
Only my doubt is that once both views for Talks and Speakers are rendered on page and after that whenever I click on any button, it is not retrieving data from controller. Instead it just shows and hide views. So in this case, I will not retrieve data from database every time.
Is there any solution for this ?
You are right. As we are making an http ajax call to server, it is cached at browser which is a feature of http and by default enabled to get performance. But if you want to disable then can easily do that set the header as while making $http call as $http({ method: ‘GET’, url: ‘/events/GetTalkDetails’, headers: { ‘Cache-Control’ : ‘no-cache’ } })
Aah ok. Thanks for the Quick Response 🙂