Wednesday, April 20, 2016

Scrollable Windows 10 Universal JavaScript App

As I'm working more and more on Windows 10 Universal JavaScript Application development, I just can not help but wonder how more people are not on this platform.  With all those web developers out there, you'd think it would be an easy win.

One thing that I found extremely hard to find information about is how to create vertically scrollable areas that are full window height (or full minus some other content area).

At any rate, searching for how to make a UWP app scrollable if the body content exceeds the window height returns barely anything relevant and mostly related to the XAML side of things.  The best answer I could find was to set an explicit height on the element and set the overflow-y: scroll.  This does not work if your screen size changes so I decided to dig in a little bit.

Here are a few different approaches you could take...

Body Fixed Height

You can apply styling to the body element via css to assign a fixed height and set the overflow-y to scroll.

body {
    height: 500px;
    overflow-y: scroll;
}
This will give you a 500px tall, scrollable body.  This is very unlikely to be what you want as you probably want the height to be the window height.

Body Dynamic Height

Since the height of the body should be the height of the window, you'll need to execute some javascript to get this to work.  The page height is not known until the page is rendered.  Even then, the window could be resized, etc.  Here is how to deal with those concerns.

body {
    overflow-y: scroll;
}

function setBodyToWindowHeight() {
    $('body').height($(window).innerHeight());
}

$(function(){ 
    setBodyToWindowHeight(); 
    window.resize(setBodyToWindowHeight); 
});
This will set the body to window height on document ready and whenever the window is resized.

Element Dynamic Height

There may be cases where you do not want the entire body to scroll, but instead some region in the page.  Maybe there is a static header and footer and the middle section needs to scroll.  In this case, you can do something similar if the header and footer do not change after page load.

body {
    overflow-y: scroll;
}

function setElementToRemainingWindowHeight(selector, usedHeight) {
    $(selector).height($(window).innerHeight() - usedHeight);
}

function calculateUsedHeight() {
    return $('.header').innerHeight() + $('footer').innerHeight();
}

$(function(){
    setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    window.resize(function() {
        setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    });
});
This will find the used height by finding the inner height of the header and footer and subtracting from the window height.

Element Dynamic Height with Dynamic Layout

There may be a case where the element's height depends on what other content happens to be present and visible on the page.  For instance, maybe the header has a slider that pushes the body and footer down.  In the previous example, the footer would be pushed off the page and you would not be able to scroll to it.

This starts to get a little messy since we have to register callbacks with everything that can change the page.  Assume it's some knockout observable like isInfoExpanded that tells us when the information panel is open.

body {
    overflow-y: scroll;
}

function setElementToRemainingWindowHeight(selector, usedHeight) {
    $(selector).height($(window).innerHeight() - usedHeight);
}

function calculateUsedHeight() {
    return $('.header').innerHeight() + $('footer').innerHeight() + $('#infoPanel').innerHeight();
}

$(function(){
    setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    window.resize(function() {
        setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    });

    // this is what I would like to do, but it has a problem...
    // when calculating the used height, the info panel's height is 0 when newVal = true
    // because it has not rendered yet, only the observable value has updated
    // and it is not obvious how to wait until afterRender or throttle a subscribe call
    //isInfoExpanded.subscribe(function(newVal) {
    //    setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    //});

    // instead, gotta be dirty and use a computed
    // further, since we don't actually need to use the value returned by the computed, but need to still
    // access it for updates, more dirty code can be used :)
    ko.computed(function() { isInfoExpanded() || !isInfoExpanded() &&
        setElementToRemainingWindowHeight('#scrollingRegion', calculateUsedHeight());
    }).extend({throttle: 100}); // this allows time for the rendering
});

This will update the scrolling region's height based on the available height of the window, considering whether the info panel is showing.

Dynamic Elements with Dynamic Height and Dynamic Layout

Phew, so in the previous case, we have an element in the page that will always exist that is having its height manipulated.  What happens if the element whose height is dynamic does not exist in the page when the layout changes.

Consider a button in the layout panel that creates a div whose height should be the available window height.  The above example will not work without having another subscription that somehow is fired when the new div is created and it waits until the div is rendered to set the height, ...

Instead, lets just alter the css rules for the page to accommodate any element popping into the page.

<div data-bind="visible: observableValue">Info Panel</div>
<div data-bind="if: observableValue">
    <div class="scrollableContent">True observable scrolling area</div>
</div>
<div data-bind="ifnot: observableValue">
  <div class="scrollableContent">False observable scrolling area</div>
<div>

In the above markup, the divs with class scrollableContent are only in the page when the condition is true so we cannot target them via jQuery to set the height without the complications mentioned earlier.

Instead, we'll simply alter the rule for scrollableContent to be the desired height.


export class ScrollableContentHelper {
    getCSSRule(ruleName:string, deleteFlag?:string) {
        for (var i = 0; i < document.styleSheets.length; i++) {
            var styleSheet : any = document.styleSheets[i];
            var ii = 0;
            do { // For each rule in stylesheet
                if (styleSheet.rules[ii] && styleSheet.rules[ii].selectorText == ruleName) {
                    return styleSheet.rules[ii]; // return the style object.
                }
                ii++;
            } while (styleSheet.rules[ii]) // end While loop
        } // end For loop
        return false; // we found NOTHING!
    }

    giveSelectorRemainingHeight(ruleName, usedHeightCalculation: () => number) {
        this.getCSSRule(ruleName).style.height = ($(window).innerHeight() - usedHeightCalculation()) + "px";
    }
}


Here is a TypeScript class that can help manage this process.  The giveSelectorRemainingHeight function takes a callback that is run to calculate the used height before setting the element height.

Then, in your default.js file (or whatever is powering your page), instead of using jQuery to set the height of elements by a selector, we'll modify the css rules to do the same thing.


args.setPromise(WinJS.UI.processAll().then(function () { 
    window['applicationViewModel'] = new ApplicationViewModel(); 
    var scrollHelper = new scrollableHelper.ScrollableContentHelper(); 
    ko.computed(() => window['applicationViewModel'].observableValue() &&
                         scrollHelper.giveSelectorRemainingHeight(".scrollContent",
                                                                  () => $("div.header").innerHeight() +  $('footer').innerHeight() + 225))
      .extend({throttle: 100}); 
}));


With this, when the observable value changes, instead of finding the elements and adjusting their height, the css rule is changed to set the height of that selector globally (so that elements that appear after the code is run are still affected).

So it would seem this is not really a UWP concern as much as how to use javascript to dynamically set the height of elements in the page.  By default, web pages viewed in a browser are scrollable when the content exceeds the browser window's height.  In a UWP app, however, no scrolling is provided automatically when the body content exceeds the window's height.  This was the confusing part of me and hopefully this code helps the other developer that is actually creating a UWP application using javascript...

Monday, April 4, 2016

Windows 10 Universal JS + Typescript = Broken in 2015 Update 2

I have been working on a windows universal 10 javascript application and everything was going great.  All my familiar tools were working perfectly and very little change needed to get things working the same way I usually develop.

I needed to re-pave my laptop and tried using a boxstarter script (more on this in another post).  Just about everything was going well, but when I opened the previously working project, it no longer had support for typescript.  Everything that was included with TypeScriptCompile tags were not included in the project and it would only run one time before erroring out saying that it was unable to find the produced js files.

After hours of wondering if my install failed, I was finally able to find an issue submitted on github.  Using Azure Virtual Machines, I was able to identify that the problem was actually related to Visual Studio 2015 Update 2 (see my comments on the issue with included pictures).  TypeScript seemed to work fine in other projects, but just not universal javascript apps.

There seems to be no one else on the internet with this problem, however.  It was insanely difficult to find even this issue, which frankly had very little information inside.  It makes me wonder if people are just not writing universal JS apps or if they are just no using typescript (which is crazy to me).

So far the universal javascript app experience has been quite amazing and very familiar to typical web development.  Hopefully people will begin writing these apps more and support for them improves.

Wednesday, March 30, 2016

Windows 10 Universal JavaScript Debugging TypeScript

Overall, still loving the new Windows 10 Universal Platform JavaScript apps.  Feels just like building for the web.  The one big difference I'm noticing now is that debugging is actually easier in web pages.  How is this possible?

In the browser, when things go wrong, you hit F12 and you're off to the races.  Right click, inspect anything on the page, execute scripts in the console, hover over scripts (including TypeScript) and see the property value, etc.  All good stuff, especially the fact that I can debug my typescript files.  I do not need to work with the generated javascript and figure out what that was in the typescript.

In universal apps, it's closer to traditional development with visual studio where you can set breakpoints and view watch window or locals.  I will say that breakpoints work better in studio than in the browser generally as you can more easily set breakpoints on inline lambda functions that I have not figured out how to do in the browser.

Eg,

this.controller.fetchData().then((result) => [breakpoint] this.storeResult(result));

Breaking into the lambda as written here is difficult in the browser, I would normally have to convert the statement to:

this.controller.fetchData().then((result) => {
    [breakpoint] return this.storeResult(result);
});

so that the function body has a line number which to set the break.

However, the horrible downside to the windows universal stuff is that in the debugger, if you are working on typescript, it will tell you everything is undefined.  Why?  Because of JavaScript's scoping for the this keyword.  In the generated code, you'll find _this is the reference you'll need to work with the object in the console.

Also, I cannot find anyway to 'inspect' the output in the simulator so that finding elements in the page to modify is definitely more difficult.  You do get all the nice features of being able to edit style sheets or markup, but when you modify TypeScript files, it doesn't actually use the modifications!  This can be terribly confusing as the debugger will walk the script with the change, but just not use the modified code.  This may not be true if you directly modify the javascript (haven't tried), but you definitely have to restart the app for type script changes to take hold; which honestly is OK since the TS is compiled during the build, however, it should not give the appearance of being modified and running the modified code.  Simply disallow it or give some grayed out indicator that the code is not actually being used.

Hopefully the 'this' scoping will be fixed soon and a way to inspect would make the development experience better than web development.

Thursday, March 24, 2016

Windows 10 Universal JS with Application Insights

Man oh man oh man.  So much confusion around what is possible with Windows 10 Universal JS apps.  If you google around for what you can and cannot do or how to do things, you'll likely find references to Windows Phone 8 vs 8.1 and silver light and all kinds of stuff so I'm trying to track down what actually works and the proper way of including things in these new projects.

There are even articles that say you cannot do certain things in JS applications or that they are different than the XAML versions, but often no reference is even given to the JS applications.  Store Apps only have XAML references and often JavaScript is referred to as 'JavaScript Web Pages' which is even more confusing.

As of this article's writing, however, the following seems to be true about application insights with regards to Windows 10 Universal JavaScript Apps.

Yes, you can absolutely use Application Insights!

But how?

Normally, when creating a project, you'll have an option to automatically add application insights to your project.  However, this is not the case with Universal JavaScript Applications.  You simply get a blank slate with nothing really provided for you.

You will need to get a special version of the AI library, which can be found here on github.  There is also a minified version available in this repo.

Include this in your js folder, and add it to whatever page/s you want to track by adding a script reference in your HEAD section.

Go to the azure portal and create a new Application Insights Developer Service or locate the instrumentation key for an existing instance.

Then, in your app start code, initialize a new AppInsights object with your instrumentation key and specify the enpointUrl as 'http://dc.services.visualstudio.com/v2/track'.

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
 "use strict";

 var app = WinJS.Application;
 var activation = Windows.ApplicationModel.Activation;

 window.appInsights = window.appInsights || new Microsoft.ApplicationInsights.AppInsights({ instrumentationKey: '', endpointUrl: 'http://dc.services.visualstudio.com/v2/track' });
 window.appInsights.trackPageView();

 app.onactivated = function (args) {
  if (args.detail.kind === activation.ActivationKind.launch) {
   if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
    // TODO: This application has been newly launched. Initialize your application here.
   } else {
    // TODO: This application was suspended and then terminated.
    // To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
   }
   args.setPromise(WinJS.UI.processAll().then(function () {
    // for debugging, keep global reference
    window.applicationViewModel = new ApplicationViewModel();
       ko.applyBindings(window.applicationViewModel);
   }));
  }
 };

 app.oncheckpoint = function (args) {
  // TODO: This application is about to be suspended. Save any state that needs to persist across suspensions here.
  // You might use the WinJS.Application.sessionState object, which is automatically saved and restored across suspension.
  // If you need to complete an asynchronous operation before your application is suspended, call args.setPromise().
 };

 app.start();
})();

That's all there is to it!  You can now view page requests, dependencies, ... in the portal.  It seems that the Visual Studio integration does not work with this approach (at least not with the simulator) so you will not receive notifications or trace data in studio while running in the simulator.

Monday, March 21, 2016

Windows 10 Universal JS + Typescript

While investigating the Windows 10 universal javascript application story, I have been trying to use all the good things that I normally use when developing web applications.  As such, I would like to use TypeScript to help generate valid, working javascript.  Using Typescript is no problem for universal applications as the files are compiled into javascript and available at runtime.

The not-so-obvious part, however, is that by default, no Windows 10 Universal specific definitions are included or available for use by newly created projects.  When working with Web Applications and Windows Universal 10 JS applications, lib.d.ts is available when writing TypeScript files so that things like window.localStorage are available.

When working with universal apps, however, we do not want to use window.localStorage, but instead Windows.Storage.ApplicationData.current.localStorage should be used.  By default, however, this is not available to typescript files.  You must install the definition from nuget or elsewhere.

PM> Install-Package winjs.typescript.definitelytyped
PM> Install-Package winrt.typescript.definitelytyped

These packages will provide definitions for storage and other native APIs that can be called from javascript.

Hopefully this will save someone else hours of searching! :)

Thursday, March 17, 2016

Windows Universal 10 JavaScript App + Knockout.js

I finally decided to see what the universal app stuff is all about, and was ready to finally tackle XAML when, to my surprise, I found that you can write your app using JavaScript and HTML.  No thanks to the template being buried out of the way.  I figured it would have been an option once I picked Universal App just like when you create a Web App you can select WebApi, MVC, Web Forms, how accounts will be handled, ...  Thank god for channel 9.

Even though I found that I *could* do a JavaScript universal app, I figured it would be some really limited subset or quirky, but so far, it's been pretty much an outstanding process.  I was unsure what would and wouldn't work, so I decided to just throw everything I wanted into the project and see what worked.  So far, everything has worked!

In preparation for the project, I googled around about including Knockout.js into a Windows Universal 10 JS App and did not find my information.  In fact, the information that I did find turned out to not work.

I was able to use the package manager to install all of the following:

PM> Install-Package Knockoutjs
PM> Install-Package knockout.TypeScript.DefinitelyTyped
PM> Install-Package fontawesome
PM> Install-Package jquery
PM> Install-Package jquery.typescript.definitelytyped

Honestly, I feel dirty including jquery as I really only ever use the ajax and promises parts of it and should probably learn a better way of dealing with those concerns.  More on that another day.

In default.html, include knockout before the default.js file so that it is in scope when default.js runs.  Modify default.js to include a call to ko.applyBindings.

default.js:
    // For an introduction to the Blank template, see the following documentation:
    // http://go.microsoft.com/fwlink/?LinkId=232509
    (function () {
 "use strict";

 var app = WinJS.Application;
 var activation = Windows.ApplicationModel.Activation;

 app.onactivated = function (args) {
  if (args.detail.kind === activation.ActivationKind.launch) {
   if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
    // TODO: This application has been newly launched. Initialize your application here.
   } else {
    // TODO: This application was suspended and then terminated.
    // To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
   }

   args.setPromise(WinJS.UI.processAll().then(function () {
       ko.applyBindings(new ApplicationViewModel());
   }));

  }
 };

 app.oncheckpoint = function (args) {
  // TODO: This application is about to be suspended. Save any state that needs to persist across suspensions here.
  // You might use the WinJS.Application.sessionState object, which is automatically saved and restored across suspension.
  // If you need to complete an asynchronous operation before your application is suspended, call args.setPromise().
 };

 app.start();
})();



In this example I have a new ApplicationViewModel that is being created and bound.  The script file containing the view model also needs to be included on the default.html page.

It's just that simple.

So far, the only things that's really different about this compared to MVC is there is not a routing engine and razor (that I've come across at least).  Just simple page using relational links as you might do in a simple website.

At any rate, I highly encourage anyone that is a web developer to take a look at the Windows Universal 10 JS Apps.

Thursday, February 18, 2016

IEnumerable missing from platform 5.4 class lib

I was converting some code to use the new project templates for .net platform 5.4.  I love how it is setup for sharing by default by producing nuget packages.  I'll have to setup a private nuget server for my sharing code and I'll be sure to write about that when I get there.

Interestingly, the first couple projects I created had no minimal problems and I was able to produce nuget packages quite easily.  Yay.

This time around, however, I was refactoring out some models and interfaces for my web application and I ran in an interesting error...

The type 'IEnumerable<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

So, I'm thinking it must be some other error in the project, surely IEnumerable is in there somewhere, right?  WRONG!

Googling around found minimal information that was complete so I figured I'd put together the pieces.  This post got me in the right direction.

 Before:

 "frameworks": {
    "net451": {
      "dependencies": {
        "System.ComponentModel.Annotations": "4.0.0"
      }
    },
    "dotnet5.4": {
      "dependencies": {
        "Microsoft.CSharp": "4.0.1-beta-23516",
        "System.Collections": "4.0.11-beta-23516",
        "System.ComponentModel.Annotations": "4.0.11-beta-23516",
        "System.Linq": "4.0.1-beta-23516",
        "System.Runtime": "4.0.21-beta-23516",
        "System.Threading": "4.0.11-beta-23516"
      }
    }
  }

After:

  "frameworks": {
    "net451": {
      "dependencies": {
        "System.ComponentModel.Annotations": "4.0.0"
      },
      "frameworkAssemblies": {
        "System.Runtime": "4.0.10.0"
      }
    },
    "dotnet5.4": {
      "dependencies": {
        "Microsoft.CSharp": "4.0.1-beta-23516",
        "System.Collections": "4.0.11-beta-23516",
        "System.ComponentModel.Annotations": "4.0.11-beta-23516",
        "System.Linq": "4.0.1-beta-23516",
        "System.Runtime": "4.0.21-beta-23516",
        "System.Threading": "4.0.11-beta-23516"
      }
    }
  }