2016-02-04

VSTO Office plugin not appearing in Office 2007

If you have an office VSTO plugin that is working in other versions of Office but not appearing in Office 2007 then try setting the following registry value

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\Common\General\

Name = EnableLocalMachineVSTO
Value (DWORD) = 1


2016-02-01

Watching a single property in an array in AngularJS

A typescript example that converts the array's property into a single string that can be watched.
        $scope.$watch(
            () => this.someArray.map(x => x.selected ? "1" : "0").join(""),
            (newValue, oldValue, scope) => this.onSelectionChanged(this.getSelectedItems()));

Getting an AngularJS directive to call back a method on its parent's controller

Here is a TypeScript example of how to call back a method on the controller from an embedded directive. The most important thing to note is that the directive's parameter name for your callback uses a & when defined, and when calling that callback you should not use positional parameters but instead use an object with properties having the names of the parameters in the target.
Register the directive when you create your app module:
module MyApp {
    var app: angular.IModule = angular.module("MyApp");
    MyApp.Directives.FileUploader.register(app);
}
The registration code is as follows:
module MyApp.Directives.FileUploader {
  class FileUploaderDirective implements angular.IDirective {
      public restrict: string = "E";
      public templateUrl: string = "/app/Directives/FileUploader/FileUploaderDirective.html";

      //IMPORTANT - Use & to identify this as a method reference
      public scope: any = {
        onFileItemClicked: "&"
      };
      public controller: string = "MyApp.Directives.FileUploader.Controller";
      public controllerAs: string = "controller";
      public bindToController: boolean = true;
      public transclude: boolean = true;
      public replace: boolean = true;
  }

  export function register(app: angular.IModule) {
      app.controller("MyApp.Directives.FileUploader.Controller", Controller);
      app.directive("fileUploader", () => new FileUploaderDirective());
  }
}
The directive's controller would look like this
module MyApp.Directives.FileUploader {
    export class Controller {
        public files: string[] = ["One", "Two", "Three"];
        //The callback specified in the view that created this directive instance
        public onFileItemClicked: (fileItem) => void;

        // This is the controller method called from its HTML's ng-click
        public fileItemClicked(fileItem) {
            //IMPORTANT: Don't use comma separated parameters,
            //instead use an object with property names to act as named parameters
            this.onFileItemClicked({
                fileItem: fileItem
            });
        }
    }
}
The directive's HTML would look something like this
  • ng-repeat="item in controller.files" ng-click="controller.onFileItemSelected(item)"> {{ item }}
  • The main view will have an instance of your directive like so
     ng-app="MyApp" ng-controller="MainController as controller">
       on-file-item-clicked="controller.fileItemClicked(fileItem)"/>
    
    Now all you need on your MainController is a method
    public fileItemClicked(fileItem) {
      alert("Clicked " + fileItem);
    }

    2016-01-29

    AngularJs - binding HTML

    The directive ng-bind will escape HTML to avoid data acting maliciously. If you want to output html you need to use ng-bind-html

    <div ng-bind-html="someHtmlBody"/>

    The important step to getting this working is to ensure the script angular-sanitize.js is referenced on your page, and it is specified as a dependency when creating a module....

    var app = angular.module("MyApp", ["ngSanitize", "OtherDependencies"]);

    AngularJS routes with ASP MVC Forms authentication

    The ASP MVC app I am working on uses forms authentication with a timeout. This means that when the session has timed out and the user clicks refresh they get redirected to a login page, and after that they get directed back to the original page without the deep-linked client-side # part of the url. 

    The solution to this is as follows:

    The first thing to do is to have the ASP MVC server side capture any URLs that should related to your client-side angular routing and return the single-page app HTML. When registering your ASP MVC routes add the following rule
            routes.MapRoute(
                name: "Angular",
                url: "x/{*clientPath}",
    Where the "x/" is the base part of your client app, of course you can do without the "x" in the URL if your entire app is a single-page angular app, in which case you will need to add a preceding rule to render your ASP MVC server side account-login page.
    Then in your Angular app make sure you use Html5 mode, like so
    app.config(["$locationProvider", "$routeProvider", function ($locationProvider, $routeProvider) {
    
        $locationProvider.html5Mode({
            enabled: true,
            requireBase: false,
            rewriteLinks: true
        });
    
        $routeProvider.when("/x/docs/upload", {
            ......
        });
        $routeProvider.when("/x/docs/view", {
            ......
        });
        $routeProvider.otherwise({
            redirectTo: "/x/docs/upload"
        });
    }]);
    When you open your browser and navigate to http://mysite/x/what/ever/you/like the server side will use XController.Index to render Views\x\Index.cshtml, which will load your single page app, and then ngRoute will take over and present the relevant view for the "x/what/ever/you/like" part.

    2015-01-07

    Node.js Express, form validation, and keeping previously posted form values

    I've been playing with Node.js and the Express webserver framework as a learning experience, it's good fun :)

    In the C# ASP MVC world using the Razor view engine I can define my user interface elements like this...

    @Html.TextBoxFor(x => x.EmailAddress)
    @Html.ValidationMessageFor(x => x.EmailAddress)

    This will do three things

    1. It will create the html output for an input element
    2. If the view is being rendered as a result of a POST it will set the value of the input to the value posted.  This is useful for when you have a form validation error and don't want to have to force the user to re-enter all of their input.
    3. If there is an error message registered for EmailAddress it will display the error text
    *Note that error messages are registered using ModelState.AddModelError("EmailAddress", "The error message")

    Node.js, Express, and Jade

    Express is a very light weight framework so doesn't do any of this stuff for you, so I had to hunt around for a solution which I am now blogging for the sake of record.

    To satisfy my validation requirement I used express-validator by Christoph Tavan (install it using "npm install express-validator").  It is used like this

    app.js
    var expressValidator = require('express-validator');
    app.use(expressValidator());

    mycontroller.js
    app.get('/signup', signUp);
    app.post('/attemptSignUp', attemptSignUp);
    
    function signUp(req, res) {
        res.render('signup', {
            title: 'Sign up',
            errors: []
        });
    }
    
    function attemptSignUp(req, res) {
        req.checkBody('username', 'Required').notEmpty();
        res.locals.errors = req.validationErrors(true);
        res.render('signup', {
            title: 'Sign up',
            errors: req.validationErrors(true)
        });
    }

    As you can see in the above source I have created two routes. The first will GET the view at /signup, the second will accept a POST from the form generated in the /signup request.  Because we have added the express-validation middleware in the main app we now have access to a new checkBody method on the request object.  Any checks that fail will be reported by the req.validationErrors method.

    The result of validationErrors is fed into the Jade view engine with the name "errors", in the /signup action this is an empty array because there are no errors, and in the /attemptSignUp the errors from the validation are passed in.

    To display the errors requires a simple addition to your Jade view

    signup.jade
    if (errors['username'])
        span.text-error #{errors['username'].msg}
    

    If there is an error for "username" then a span with the css-class "text-error" and with the error message as its contents.

    Keeping form input

    Having to re-enter form data whenever you get something wrong is annoying.  I haven't yet found Express middleware that deals with this so I made my own very simple library.

    To use it requires the following addition to your jade view

    signup.jade
    input(type="text", name="username", value="#{posted('username')}", required)

    I have added the "required" keyword for client-side validation, so in this example the username must be invalidated on the server, perhaps because the user name is already in use, but you can remove it for the sake of testing.

    For checkboxes you can use this
    input(type="checkbox", name="accepttermsandconditions", checked=posted.checked('accepttermsandconditions'))

    The new lib is located in ./lib/form-values/index.js

    exports = module.exports = function (req, res, next) {
        var body = req.body;
        if (typeof body === 'undefined') {
            return next(new Error('form-values must be used after body-parser'));
        }
        var bodyKeys = Object.keys(req.body);
        res.locals.posted = function (name, defaultValue) {
            if (bodyKeys.indexOf(name) === -1) {
                if (typeof defaultValue === 'undefined')
                    return '';
                return defaultValue;
            } else {
                return req.body[name];
            }
        }
        res.locals.posted.checked = function (name) {
            var postedValue = res.locals.posted(name, null);
            if (postedValue === null)
                return undefined;
            return '';
        }
        return next();
    };
    

    If you look closely you will see it is also possible to call posted('fieldname') with a second parameter which will act as a default value only if there is no posted value.

    2014-10-04

    AngularJS - Triggering code whenever ng-view updates


    //Create the module
    var app = angular.module('someapp', ['ngRoute']);
    
    //Config the routes
    app.config(configRoutes);
    
    function configRoutes($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: '/angular/viewtemplates/admin/index.html',
                controller: 'AdminController'
            })
            .when('/categories', {
                templateUrl: 'angular/viewtemplates/admin/categories/index.html',
                controller: 'CategoryIndexController'
            })
    }
    
    //Make sure we are notified whenever the ng-view is updated
    app.run(function($rootScope) {
        $rootScope.$on('$viewContentLoaded', function() {
           $('table[data-toggle="table"]').bootstrapTable();
        });
    });