jQuery-UI autocomplete in Aurelia

It’s relatively easy to setup jQuery-UI’s autocomplete in Aurelia, but I did encounter an error (like this user on SO) when I tried to use it. Unfortunately installing by “jspm install jquery-ui” resulted in an $(…).autocomplete is not a function” error.

No problem. Let’s load it through npm:

jspm install npm:jquery-ui

Now we can add the imports it in our viewmodel:

import {autocomplete} from ‘jquery-ui’;
import ‘jquery-ui/themes/cupertino/jquery-ui.css!’; // whatever theme you want

After this it’s pretty straightforward. Let’s insert an image before the label to make things more interesting:

attached(){
 var members = [
  {
  value: "Rick",
  label: "Rick",
  desc: "group leader",
  icon: "styles/images/rick.jpg"
   },
  {
  value: "Coral!",
  label: "Carl",
  desc: "future leader w/ eyepatch",
  icon: "styles/images/carl.jpg"
  }
 ];

 $( "#members" ).autocomplete({
  minLength: 0,
  source: members,
  select: function( event, ui ) {
     console.log(ui.item.value);
  }
  }).data("uiAutocomplete")._renderItem = function (ul, item) {
  return $("<li />")
    .data("item.autocomplete", item)
    .append("<a><img height='25' width='25' src='" + item.icon + "' />" + item.label +               "</a>")
    .appendTo(ul);
  };
 }

View

<label for=”members”>Members: </label>
<input id=”members” type=”text” />

Advertisements

Bundles of Karma

Setting up bundling in Aurelia (as simple as following the skeleton navigation’s latest update) has decreased my load time to a fraction of what it was before, really impressive! I also like how they separated the bundles from the bundling – as we have a new bundles.js file to hold everything that we’re (you guessed it) bundling.

However, after creating my bundle, I hit a snag when testing my application. Karma was looking for the aurelia-[*] bundle file, resulting in a 404:

bundle-warn

A workaround is to call ‘unbundle’ before running the ‘gulp test’. This means we need a new gulp task in our test.js file (thanks to @tomtomau for the idea on this):

gulp-unbundle

By firing off ‘gulp run-tests‘ we can see our tests are now back up and running.

 

 

 

Mashup: Browser-Sync, Aurelia & VS 2015

I’ve been experimenting with (and loving) Aurelia’s skeleton navigation application (v 18.1 as of this post) and been working on merging it into a VS 2015 Web API Project. You can find it here.

One thing I stumbled on was getting browser-sync, as defined in the gulp task, to work in visual studio. (I wanted to try and see if I could make this work rather than the Browser Link built into VS).

A few quick changes…

The serve gulp task (serve.js) is where the configuration for browser-sync is defined. When it’s called from the watch task, it will reload the browser after the build tasks are run (btw this workflow with the tdd task running and browser-sync in the background is a tremendous improvement. I have instant feedback on terrible decisions).

Project properties

Verify your port matches what’s configured for browser-sync. In my case, 9000 matches what’s in my gulp task. Then uncheck the Launch Browser option:

2015-09-25 09_57_19

Unfortunately, after starting the watch task with the default configuration, I kept getting a 404  (Cannot GET /) error, so I ended up using http-proxy-middleware to get things running on my corporate intranet.

updated serve.js

var gulp = require('gulp');
var browserSync = require('browser-sync');
var proxyMiddleware = require('http-proxy-middleware'); 

// this task utilizes the browsersync plugin
var proxy = proxyMiddleware('**', { target: 'http://localhost:9000' });

gulp.task('serve', function (done) {
   
    browserSync({
        open: false, // Stop the browser from automatically opening
        port: 9000, // Use a specific port (instead of the one auto-detected by Browsersync) 
        server: {
            baseDir: ".",
            middleware: [proxy]
        }
    }, done);
});

Finally, start without debugging (or debug if you have breakpoints) and fire off the gulp watch task (if you don’t have vs running first you may get an access denied error)

The browser-sync instance will be running on port 9001.

Rhythm Section: AngularJS and Windows Authentication w/ .NET Web API

Sometimes you can spend far too long making decisions about how to organize your project. Guidance can be lacking, and we don’t want to have spend time reorganizing our project at a later stage. This is one of many reasons why creating .NET applications using John Papa’s AngularJS HotTowel Template is highly recommended. The template directs you on the ‘what goes where’ questions and you can learn a lot by the answers.

Yet as helpful as it is, no template can answer everything.

Let’s modify it to use Windows Authentication in a Single Sign On manner.

check out the Gist, located here: Source

First, we’ll add in our authentication factory:

authentication.js

angular.module('app')
.factory("authentication", ["$http", "$q", "$window", authentication]);

function authentication($http, $q, $window) {

    var user;

    function login() {
        
        // check if the user already exists for this session
        if (user) {
           return $q.when(user); // resolve with given value, necessary because calling function expects a promise.
        }
        
        var url = 'api/users/current/';
        return $http.get(url).then(function (result) {
            var result = result.data;
          
            user = {
                id: result.UserId,
                displayName: result.DisplayName,
                guid: result.ADGuid,
                isAdmin: result.IsAdmin
            };

            addUserToStorage();

            console.log("user created.");
            return $q.when(user);
        });
    }

    function addUserToStorage() {
        $window.sessionStorage["user"] = JSON.stringify(user);
    }

    function getUser() {
        return user;
    }

    function init() {
        if ($window.sessionStorage["user"]) {
            user = JSON.parse($window.sessionStorage["user"]);
        }
    }

    init();

    return {
        user: user,
        init: init,
        addUserToStorage: addUserToStorage,
        login: login,
        getUser: getUser
    };
};

The above is a factory that checks to see if a user already exists (by looking in session storage). If they’re not yet there, it calls the specified Web API URL to get a new user. A call to getUser executes once, without making additional calls to the server.

example-controller.js

// in the controllers, we can get the current user by deferring to authentication service

(function () {
    'use strict';
    var controllerId = 'sales';
    
    angular.module('app')
        .controller(controllerId, ['$q', 'common', 'authentication', sales]);
    
    function sales($q, common, authentication)
    {
      // ...
       
    // each controller has a call to getUser(), 
    // which defers to the authentication service for auth status
    var vm = this;
    vm.user = {};
    
    activate();

        function activate() {
            
            // call the getUser promise
             common.activateController([getUser()], controllerId)
                .then(function () {  });
        }
        function getUser() {
            return authentication.login().then(function(data) {
                vm.user = data;
                return $q.when(data);
            });
        }
        
        // ...
        
    }
})();

For my particular application, I never know what section of the SPA the user will arrive at first, so each ViewModel should know how to authenticate the current user on its own.

And now on to the Server Side:

User.cs

public class User
    {  
        public int UserId { get; set; }
        public string DisplayName { get; set; }
        public string ADGuid { get; set; }
        public bool IsAdmin { get; set; }

    }

A simple class that represents an Entity Framework user entity designed for transferal to the front end.

UserFactory.cs

public class UserFactory
    {
        private readonly IDbContext db = new DbContext(); // demo, could be injected

        public User Create(WindowsIdentity currentWindowsUser)
        {
            var user = new User();
            
            string name = currentWindowsUser.Name.Replace("DOMAIN\\", ""); 
               
            // a much simplified case for example (better to retrieve by GUID found in AD)
            User userInDatabase = (from u in db.Users
                                         where u.FirstName.ToLower() + u.LastName.ToLower() == name.ToLower()
                                         select u).FirstOrDefault();

            if (userInDatabase != null)
            {
                user.UserId = userInDatabase.UserId;
                user.DisplayName = userInDatabase.DisplayName;
                user.IsAdmin = userInDatabase.IsAdmin;
                user.ADGuid = userInDatabase.ADGuid;
            }
            
            return user;
        }
    }

In this UserFactory class, we’re getting the current logged in user account and verifying the name exists and matches up in the database.

And now the Web API:

UsersController:

[RoutePrefix("api/users")]
public class UsersController : ApiController
    {
        /// 
        ///     Gets the currently logged in user account in AD as a User object
        /// 
        /// 
        [Route("current/")]
        public User GetCurrentUser()
        {
            User user = new UserFactory().Create(WindowsIdentity.GetCurrent());
           
            return user;
        }
    }

The “current/” route calls the UserFactory with the current windows identity, which creates a user we can pass back to the front end.

With this in place, we now have a working Single-Sign On version of our HotTowel template!