Friday, April 24, 2015

Using AngularJS with MVC

This is a small example of combining the power of Web API with Angular JS. I have looked at many sites for a good post, but I could not find one which was precise and focused. In this post I intend to do just that.

The Project Objective:
In this example I will try to show a basic interaction between a UI created by the Angular JS and an MVC Web API service.

1) Create the Service Project:

Using VS 2013, create a WebApi project called MvcAngular.WebApi. I have called the solution MvcWithAngularSolution. The images below show can be used as a guideline.

Image 1

Image 2
2) Create a new class in Models folder and call it Pet:


     public class Pet
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string PetType { get; set; }
        public int Weight { get; set; }
    }

3) Delete the ValueController from the Controllers folder.

4) Create a new "Web API 2 Controller with read/write actions"  in the Controllers folder. Call it PetApiController.

Image 3

Image 4
5) To stay focus, we will not connect to a database. Instead we will create a list of pets and use that as our data source. Image 5 below shows the modifications needed to define the list and populate it.

Image 5 
6) We still need to do two more modification to the PetApiController. The original template uses an array of string. In this project we need to change the string type to Pet type. That will off course results in the logic change in bothe Get() and Get(int id) methods. So modify these methods to look like the once in Image 6 below:

Image 6

7) Before we move on, it is well worth to test the service. To do so, run the project in the development environment by hitting Ctrl-F5. Once the landing page appears add /api/PetApi for the first test and then /api/petApi/2 for the 2nd test. The first test should display all pets in the browser (see Image 7) while the 2nd test will display Id, Name, PetType and the Weight of pet with Id = 2 (see Image 8). Please do not proceed until both of these tests are working satisfactorily.

Image 7

Image 8

8) We are done with the back-end. Let's think about the UI. For this project, we need to have 2 views; a list view and a details view. Let add 2 actions to the Home MVC controller: PetsList() and PetById(). Image 9 below shows these actions (also not that I have changed the Index() action to be consistent with the new actions):



9) Now we need to create 2 views, one for each action; PetsList.cshtml and PetById.cshtml. Just right click inside each action and select "Add View". Accept all details and click the OK. Image 10 below shows the views created for these actions.

Image 10

10) At this stage we need to add 2 buttons to the landing page (and remove all unnecessary stuffs) and connect their click actions to the controller actions we just created. So change the index.cshtml in Views >  Home folder so that it looks like the following:

Image 11

11) At this stage if you run the application, you we should see screens similar to Image 12 - Image 14 below:

   
Image 12

Image 13
Image 14
12) Now we will start working on the Angular integration in to the application. To use Angular JS you need to use NuGet package manager console and install the "AngularJS.Core" in to the project. To do so, simply click Tools > NuGet Package Manager > Package Manager Console. The Package Manager Console will appear at the bottom of the bottom of the VS screen showing its prompt: PM>. Assuming you are connected to the internet, just type "Install-package AngularJS.Core". You can also specify the name of your target project. See Image below:

Image 15
13) You also need to insert the angular directive in the html tag in the _layout.cshtml. The basic directive is "ng-app", this will initialize the Angular JS library so that your application can execute other Angular command. No need to mention that we need to reference the Angular.js in the header of the page in order to enable the browser to find it. With these 2 mods, the _Layout.cshtml looks like this (Image 16):

Image 16

14) At this stage we need to ensure that AngularJS is installed and configure correctly and it is actually working. The test is simple: we will see if a basic angular expression such as {{ 2 + 3 }} is handled correctly. You just need to add this one of your views, if the view shows "5" the you are good, if not need to go back and see what got messed up! Image 17 highlights the test and Image 18 shows the test result.

Image 17
Image 18
15) Now it is time to create the Angular Controller which is simply a javascript file. In order to keep javascript files organized, it is good practice to create a subfolder inside the Scripts folder. Let's call this new folder "app". Let's add a JavaScript file as a new item to this folder and name it as "PetJsController.js".  Add the following to this new file:

Image 19
As you can see the controller is placed inside a Javascript Closure. This is an Immediate Function and will be run only once. Read about the JavaScript Closures here. The controller is inside an Angular Module named PetsApp. Modules are containers for different parts of an angular application. Read about Angular Modules here. The Controller has 3 methods, GetAllPets(), GetPetById and ClearScreen. We will use these methods to access the WebApi service. The service will be accessed by using the $http Angular Service. $http is a core Angular service that communicate with http based services via XmlHttpRequest object. Read more about $http here. $scope offers an execution context for the methods of the Controller. Read more about $scopes here and Controllers here.

16) Now it is time to modify the view files so that they can use the controller module. The first step is to tell the views to use the PetsApp module. This is simple done by modifying the ng-app directive in the html tag i _Layout.cshtml. You need to change the directive from "ng-app" to "ng-app='PetsApp'". No need to say that you also need to add a reference to the controller file in "app" subfolder. See Image 20 below.

Image 20

17) We need to modify both the PetsList.cshtml and PetById.cshtml views as shown in Image 21 below.

Image 21

18) Now it is time to run the application and see if it works. You should have the following series of screens.






Conclusion:

The UX/UI is not great at all. But the purpose of this post was not to show you how to create a great User Interface. The purpose was to focus on getting the AngularJS to work with the WebApi. And this is exactly what this solution does. Basically we passed the Web Service URL to the AngularJS $http service in the AngularJS controller.


Friday, July 25, 2014

Cisco AnyConnect Client Error

The VPN client driver encountered an error. Please restart your computer or device, then try again

Let me save you sometimes off the bat. Ignore Cisco's advice in this error message. Don't bother restarting your device. It is unlikely to fix your issue, unless you are running XP.

Also, you can most probably safely ignore the fix that Cisco offers for the recorded bug CSCsm54689. Why? Simply because their solution is to disable the RRAS (Routing and Remote Access Service). On my Windows 7 box, this service was disabled already. For the fun of it (!!??), I actually enabled it, restarted the box, then disabled it and retested. No change, the same old frustrating error pops up (the VPN client driver encountered blah blah).

I even checked the "Cisco AnyConnect Secure Mobility Client Administrator Guide" Release 3.0, with the last update date of April 7th, 2014. This issue has been blamed on Microsoft updates on page 12-18 of the Guide. That's really an unfair sending the guys "chasing the wild goose" kind of action. So much so that at the end of their solution, they are embarrassingly admitting that this solution won't work and connection attempt will still fail, so just go ahead and open a case with Microsoft!!! I was so disparate that I went ahead and did the steps to no avail. I didn't bother opening a case with MS though. Read for yourselves: "Even though the steps taken above may indicate that the catalog is not corrupt, the key file(s) may still have been overwritten with an unsigned one. If the failure still occurs, open a case with Microsoft to determine why the driver signing database is being corrupted."

I also went through PeteNetLive solution. The first part of it is taken from the the Admin Guide mentioned above. Based on the notes in this site I ended up removing the client and reinstalling the AnyConnect latest version with a brand new version of Java VM. This cost me an hour or two and guess what? It didn't help either!!

But there is still hope. So, don't despair!

The Solution

Enventually I threw every thing at our great network engineer Ken. Including the message log shown below:

[7/25/2014 9:08:07 AM] Ready to connect.
[7/25/2014 9:08:14 AM] Contacting vpn.domain.blah.blah
[7/25/2014 9:08:27 AM] User credentials entered.
[7/25/2014 9:08:29 AM] Please respond to banner.
[7/25/2014 9:08:31 AM] User accepted banner.
[7/25/2014 9:08:31 AM] Establishing VPN session...
[7/25/2014 9:08:31 AM] Checking for profile updates...
[7/25/2014 9:08:31 AM] Checking for product updates...
[7/25/2014 9:08:31 AM] Checking for customization updates...
[7/25/2014 9:08:31 AM] Performing any required updates...
[7/25/2014 9:08:36 AM] Establishing VPN session...
[7/25/2014 9:08:36 AM] Establishing VPN - Initiating connection...
[7/25/2014 9:08:37 AM] Establishing VPN - Examining system...
[7/25/2014 9:08:37 AM] Establishing VPN - Activating VPN adapter...
[7/25/2014 9:08:37 AM] Disconnect in progress, please wait...
[7/25/2014 9:08:43 AM] Connection attempt has failed.
[7/25/2014 9:08:43 AM] Ready to connect.

Ken looked at this log for sometime. Then he checked the UAC settings. The slider was set at "Always Notify". He moved it to "Never Notify" and that fixed everything.

What? Hold on! But Why?

So why did this fix it? Ken believed the failing occurred on "Performing any required updates.." stage. You can see that in the log messages. Right? (well, I don't! But I took his words for it!).

Just to dig deeper, I set the slider on the UAC settings dialogbox back to "Always Notify", rebooted the laptop and retried the connection via AnyConnect. What would you expect? Should it connect or should it fail again? I expected it to fail. But... To my surprise, it didn't fail! It connected happily! 

My Theory

According to Ken, "the client was failing on Performing any required updates...". If that's the case, then it shouldn't fail if there is no updates to perform. As the slider was moved to "Never Notify" a few minutes prior to my test, all "the required updates" must have been applied and there was no new updates to perform. Hence, no failure occurred. Bingo!

I will leave the slider on the Always Notify for now. This hopefully (!!) will cause the client to fail next time if and when there are some updates from Cisco. I will let you know, when I get this error again. However, next time I will just run the client As Administrator. I think that's a better, safer and less impactful (is there such a word?) approach than setting the slider to "Never Notify" for good.  

One more point: This is certainly a bug, even Cisco admits to it. I think the software doesn't know how to work with UAC. Maybe that's due to a mix-up between the x86 and x64 libraries. This is probably why Cisco's solution to  CSCsm54689 works for some system (like XP for instance) and doesn't work for others (Windows 7). Whether that is (or is not) the cause, Cisco owes it their user base to fix this annoying bug and keep their documentation up to date.

 


   

Thursday, March 20, 2014

Why MVC 4 Intranet Applications Throw Access Denied!



The Task/Issue:


Let's assume that you are logged-in to your company's domain. Your boss comes in your cube and asks you to create an intranet application for internal company use. She dictates the requirement but before she completes the first sentence, her cell phone rings and off she goes. You are the kind of go-get-it type that she likes. So you start your development process by simply firing up your VS and creating an MVC 4 application based on the Intranet template. You hit F5 for a quick test. Unexpectedly you get an access denied! That feels like a punch in the nose... If you, the developer get access denied to your own little creation, nobody else will be able access it. Is MS pulling your legs or what?


 The Questions:


Well, Windows Authentication is *disabled* by default in MVC application! More surprisingly, the Anonymous Authentication is *enabled* by default! This design is based on the understanding that the majority of developers who are creating Intranet application are not developing against an Active Directory Server sitting somewhere in you company’s premises or hosting cloud (I am *not* one of those… Wonder if you are!). Isn’t this a strange assumption? Why should one create an intranet app, if one doesn’t have an AD authentication service or a fire-walled environment?? Also, apart from creating a security hole in your app, what’s the point in enabling Anonymous Auth especially if it doesn’t allow you, the developer, in anyways? Nevertheless, using the approach mentioned in the following paragraph, your application be able to authenticate against the AD correctly.


The Fix:


Make sure the Intranet project is your active project by clicking its name in the Solution Explorer. Use the View > View Properties menu. Note the value of the "Windows Authentication" property. By default it is set to "Disabled". You need to set this value to "Enabled". Your company's AD does not normally allow "Anonymous Access". So you might as well set the value of this property to "Disabled" to ensure that your application is a little more secure. Hit F5 and you will note that the access denied is not thrown, instead your will see a greeting message on the top right corner of your application window. So you are in. So will everybody else with a company set of credential be able to log-in to your app magically (without filling a log-in form). Well, almost "magically"…


 IE Settings:


The behavior actually depends on two setting in the IE. Firstly, the http://Localhost must be identified as an intranet site. So please drop it in your intranet site using the "Internet Options" dialog box. Secondly, the User Authentication in the Intranet Security Should be set to “Automatic logon Only in Intranet Zone”. This setting can be configured via Internet Options, using “Local Intranet > Custom Level… > User Authentication > Logon”.  Then the “Magic” becomes more prevalent.










Thursday, May 23, 2013

MVC 4.0 Project Error: @Scirpts does not exist


MVC 4 enables the use of much anticipated Razor View engine. This is great, however, in my first trial run of MVC 4 in Visual Studio 2012, I faced a run-time error regarding @Scripts helper as shown below:
 

 
After some digging, it turned out that this helper is implemented in Microsoft.Web.Optimization assembly.  Don’t try to add a reference to this assembly to your project. It is unlikely that you don't find it the list of the .Net assemblies although it is sitting somewhere on your computer. The neater and cleaner approach is to click the Visual Studio “Package Manager Console” menu (Tools --> Library Package Manager).

 

The “Console” opens up in the lower pane of the Visual Studio UI displaying a PM> prompt.  We will have to use this console to install Microsoft.Web.Optimization package.  Type in the following command:
Install-Package  -id Microsoft.Web.Optimization –InstallPrerelease
 



Now it is time to tell your project to include this assembly and use it to access the @Scripts helper. This is done in the Web.Config just by adding the new namespace as follows:
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <dd namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.Optimization"/>
      </namespaces>
    </pages>
These two steps will get your issue resolved and makes the run-time happy.
Aside: The Package Manager Command Prompt is really a cut-down version of PowerShell integrated with the Visual Studio UI. You can test this by typing simple PowerShell commands such as “get-help”, “host” and see this for yourself. Specifically run the “get-help Install-Package” to get the command line syntax for Install-Package as you need to use it in this process. So you don’t need to memorize the command, instead you can as always, rely on PowerShell’s get-help command to come to the “rescue”.
Also browse through other available sub-menus in the “Library Package Manager”. There are a great deal of stuffs to learn and use. I especially liked the “Package Visualizer” and the “Package Manager Settings”.




Monday, March 26, 2012

SharePoint 2010 - Protecting the Security of the Security Groups

Sometimes ago I created an AD security group, dropped a handful of users in it, and dropped that group in the visitors group of a particular team site and named it some meaningful, but let's call it "TheTeamSite" just for our convenience here. Everything worked fine and I forgot about it.

This morning I received an email from one of those users complaining that he could not browse to TheTeamSite! He was adamant that he was using the right credentials!

I browsed to TheTeamSite and used 'Site Actions -> Site Permissions ->
Check Permissions" (on the ribbon), typed in the username and clicked the "Check Now" button on the popup. To my surprise I got "Permission levels given to UserName: None.

The appropriate AD group was in the Site Visitors and the UserName was in that group. Everything was in good order but that message was completely out of whack. I expected to see something like: "Permissions levels given to UserName: Read via visitors group or something".

So I created a test user and dropped it in the same security group. Logged on as this new test user, and I could happily browse to TheTeamSite. This solved one problem, the user was using the wrong credentials (which was confirmed and resolved in due course).

However, the "None" permission level, which was now popping up for my new test user too, kept bugging me.

It was coffee time, no doubt. After the first sip of bitter coffee, it just occurred to me that SharePoint is protecting the Security of the Security Groups. If SharePoint was allowed to tell me that our user had Read permission via the Visitors group, then a malicious user can run a bunch of check permission on a bunch of users and eventually finds out who are the members of a given security group. That would have been bad.

BTW: "SharePoint 2010 protects the Security of the Security Groups" sounds cool, but it may be semantically wrong. It could be the AD and the Security Group that are denying the SharePoint of this privileged information. But this has to be looked in to at another occasion.

Wednesday, October 5, 2011

Adding a margin to the text inside a Content Editor web part

I dumped a lump of text in a Content Editor web part in SharePoint 2010 and saved/checked-in the page.

Frankly it looked totally disgusting. The major problem seemed to be the lack of left and right margins so much so that the first pixel of every letter at the begining of every line was missing. I tried a couple of tricks like putting the text inside a HTML table with cellpadding=8. These didn't work.

Eventually I ended up in putting the text inside a "span". It worked nicely. Here's the code just to save you a minute or two messing about with html:


<span style="margin-left:8px;margin-right:8px" >

Your HTML code containing your text goes here - The code is already generated for you. Just click page edit... click on the text and then click the HTML button on the ribbon and select Edit HTML. then you need to add the closing "span" tag as the very last tag to your page.

</span>


Tuesday, August 31, 2010

Linq.Union Problem

Linq has created a lot of excitement in the developer communities and is a great tool for mixing the UI, Business Logic and the Infrastructure into a seriously entangled mess of unmaintainable code, if of course used improperly. So I am working hard on learning the "proper" way to use it.

Meanwhile, only yesterday I spent quite sometimes googling/binging around to find a way to apply aggregate filters dynamically to a query to no avail. Maybe this is something that I should put down as "not knowing the proper way of using Linq"!

Anyways, here's the use case, judge for yourself:

I have a list of suppliers and their residence States. I'd like to offer the user the ability to pull the list of suppliers who are based in a bunch of states of her own choice. i.e today she like to have the list of suppliers in CO, TX and CA. Tomorrow she may like to have the list of suppliers based in WA and CO.

If I were to create a fix function to get the supplier for CO, WA, TX and CA, the I would write something like this:



This works but it is not "Dynamic". Remember the users list of states changes everday. So I came up with the following solutions:



This is "Dynamic" (iterates through a list of states) but doesn't work!!

Here's the output... the middle states are missing!!

DynamicUnion Result
===Bad Results========
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

The problem seems to be the following statement:



For whatever reasons, this only remembers the suppliers for the first and the last states in the list. The ones in the middle are lost!! The syntax surely looks decent and "c-sharpish". It compiles too (but don't ship it yet). I am at a loss to explain this strange behavior. I shall tentatively dub it as the Linq.Union.Bug for now.

The work around is rather simple, just persist every set of records in a list as they are pulled from the data source. This should work except that it does not do the Union operation (as in set theory) properly, i.e. it doesn't get rid of repetition and you need to do some cleaning up before you present the list to the user.





Anyways, the whole program follows with the run output.



Run Output

As you will note in the Dynamic Results, TX and CA (the states between the first and last entries in the list) are eaten up by the "Union Bug"!

StaticUnion Result
===Good Results======
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier5 is based in TX
Supplier6 is based in TX
Supplier7 is based in TX
Supplier9 is based in TX
Supplier10 is based in TX
Supplier11 is based in CA
Supplier12 is based in CA
Supplier13 is based in CA
Supplier14 is based in CA
Supplier15 is based in CA
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

DynamicUnion Result
===Bad Results========
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

Dynamic WorkAround
===Good Results======
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier5 is based in TX
Supplier6 is based in TX
Supplier7 is based in TX
Supplier9 is based in TX
Supplier10 is based in TX
Supplier11 is based in CA
Supplier12 is based in CA
Supplier13 is based in CA
Supplier14 is based in CA
Supplier15 is based in CA
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA