Tag Archives: Single Page Applications

Editing code dirrectly in browser (WebKit)

May be some people already know this feature, but I was discovered it only today. A lot of JS debugging bring some benefits and today I found possibility to edit code and save it on disk with Google Chrome (or any other WebKit based browser). Hope this will simplify some debugging tasks for you. See how to enable this feature bellow:

  1. Add folders(s) with your source code to browser’s workspace
    Add folder to  Workspace

  2. Allow browser to access file system (I have Ukrainian browser in English you have to click “Allow”)
    Allow access to file system

  3. Select file and map it on file system resource
    Map to file system

  4. Select resource from mapping from your workspace (added in first step)
    Select file from workspace

  5. Don’t forget to restart dev tool

  6. Edit your file in browser

  7. See changes in VisualStudio or any other IDE
    See changes in your VS

Hope this help you save some time and save Atl and Tab keys on your keyboard 😉

Fornt-end with Knockout.js, require.js and TypeScript

Let’s talk about how to correctly organize front-end with Knockout.js require.js and TypeScript.

The problem

If we will read TypeScript handbook we will find a lot information about how to load modules with AMD and require.js, and everywhere in samples we will find something like this

    import module=require('./module');

But in real application we always have some folder structure to keep the files organized, we are using different package managers and so on, thus in most cases import should look like

    import ko=require('./node_modules/knockout/build/output/knockout-latest')

Unfortunately for some unknown reason this is not working with TypeScript, at least with versions 1.3 and 1.4. Really, why current folder path is working but more complex path is not? We have to deal somehow with this.

An the only way is to use import ko=require(knockout) instead of full path to knockout.

In this post I will describe the way how to build HTML application with MS VisualStudio and I will use node package manger to load all the libraries, but the same idea will work for nuget or any other package managed and IDE.

Application structure

  • node_modules
    • knockout
      • build
        • output
          • knockout-latest.js
    • requirejs
      • require.js
    • moment
      • moment.js
  • typings
    • knockout
      • knockout.d.ts
    • moment
      • moment.d.ts
  • config.js
  • application.ts
  • mainViewmodel.ts
  • bindings.ts
  • index.html

Require.js enabled JavaScript (or TypeScript) application should start with single “ tag in html. In our case it looks like:

    <script data-main='config.js' src='node_modules/requirejs/require.js'></script>

This config.js is the only JavaScript file, all other logic is done in TypeScript. May be there is some way to write it on TypeScript, but I’m not sure that it makes any sense, because you have to do JS specific low level things here. The config.js looks like following:

    require.config({
        baseUrl: "",
        paths: {
            knockout: "./node_modules/knockout/build/output/knockout-latest",
            moment: "./node_modules/moment/moment"
        }
    });

    define(["require", "exports", 'application'], function (require, exports, app) {
        app.Application.instance = new app.Application();
    });

First of all in this file we are configuring require.js to make it understand where to search for libraries. We will load our index.html from file system and of course in real app you should not use folder structure but think about URLs. Please note that you should not specify file extension.

Now require.js will understand how to load knockout. But this will tell nothing our TypeScript compiler and compiler will report errors about undefined module.

To fix this problem with compiler simply add corresponding typings to the project. Now TypeScript will build everything without errors. Please note that in this case TypeScript will not verify correctness of path to modules because it can’t determine the real URL structure of the application. That may be the reason why complex path is not working in import.

Note: don’t forget to switch TypeScript module type to AMD (Asynchronous Module Definition). This will conflict with node.js and next time I will explain how to deal with node.js and AMD.

Application startup

Our application entry point (after config.js) is application.ts file with following content:

    import vm = require('mainViewModel');
    import ko = require('knockout');
    import bindings = require('bindings');

    export class Application{
        public static instance: Application;

        constructor(){
            bindings.register();
            ko.applyBindings(new vm.mainViewModel());
        }
    }

Here we load module(s) (as dependency) with all custom bindings, create main view model and apply it to whole page.

Note that we don’t need to specify path to bindings and mainViewModel in config.js because they are located at the same directory. You can use more complex structure and everything will work with TypeScript just don’t forget to explain require.js how to find all your modules.

Custom bindings

Custom binding are wrapped in single module and can be loaded as any other module. Binding handlers will be registered with bindings.register() call. This can be done with following content of bindings.ts:

    import ko = require("knockout")
    import moment = require("moment")

    export function register(): void {

        ko.expressionRewriting["_twoWayBindings"].datevalue = true;

        var formatValue = function (value, format) {
            format = ko.unwrap(format);
            if (format == null) {
                format = "DD.MM.YYYY";
            }
            return moment(ko.unwrap(value).toString()).format(format);
        }

        ko.bindingHandlers["datevalue"] = {
            init: function (element: HTMLInputElement, valueAccessor, allBindings, viewModel) {
                element.value = formatValue(valueAccessor(), allBindings.get("dateFormat"));

                element.addEventListener("change", function (event) {
                    var dateValue: any
                        = moment(element.value, ko.unwrap(allBindings.get("dateFormat")))
                            .format("YYYY-MM-DD") + "T00:00:00";

                    if (ko.unwrap(valueAccessor()) instanceof Date) {
                        dateValue = new Date(dateValue);
                    }

                    if (ko.isObservable(valueAccessor())) {
                        valueAccessor()(dateValue);
                    }
                    else {
                        allBindings()._ko_property_writers.datevalue(dateValue);
                    }
                });
            },
            update: function (element: HTMLInputElement, valueAccessor, allBindings) {
                element.value = formatValue(valueAccessor(), allBindings.get("dateFormat"));
            }
        }
    }

Here we create very useful datevalue binding, which allows to edit and display dates as string in specific format. This binding is able to work with observables and flat values and store date in JSON compatible format or Date, depending on initial value of bound property. This binding contains some knockout and TypeScript tricks like ko.expressionRewriting["_twoWayBindings"].datevalue = true and allBindings()._ko_property_writers.datevalue(dateValue) but let’s talk in next blog posts about these tricks.

ViewModel

Nothing special just usual view model organized as module

    import ko = require('knockout');

    export class mainViewModel{

        constructor(){
        }

        public name = ko.observable("John Doe");
        public birthday = ko.observable("1983-01-01");
    }

Conclusion

Everybody are waiting for ECMASript 6 support in all browsers with all sweet things like classes, arrows, modules and so on. Life is too short to wait – let’s use TypeScript today! I’ve tested it in big project and yes sometime it looks a little raw but it’s working and make our life easier with type check and better intellisense.

Knockout.js components. What they are and what they are not

What are components?

Some time ago Knockout.js team released new feature – components. This feature allows developer to build some custom components that will have it’s own view and logic. Registration of component looks almost like binding registration

ko.components.register('mywidget', {
    viewModel: function(params) {
        //Define view model here
        this.title = ko.observable("Hello from component!!!");
    },
    template: '<div data-bind="text: title"></div>'
});

The example below looks very similar to definition of user control in technologies like WPF/Silverlight or even WinForms. We have template to define view of element and view-model to define logic.

Most interesting (for me personally) is usage of these components as custom elements – custom HTML tags. after registrations of my widget in previous examaple you can write following in your HTML code:

<mywidget></mywidget>

Brief description (skip it if you already familiar with components)

And this HTML tag will be replaced with template of the component with applied component view model.

Template can be defined in following way:

  • With existing element id. Template element can be any existing element div or template or anything else.
template: { element: 'my-component-template'}

Element content (only children of element) will be cloned to place where you apply your custom element

  • With exiting element instance
template: { element: getElementById('...') }
  • Directly with string of markup
template: '<div data-bind="text: title"></div>'
  • With array of element instances (elements will be add sequentially)

  • With AMD (Asynchronous Module Definition)

template: { require: 'text!some-template.html' }

Require.js or any other AMD module loader can be used. See https://github.com/amdjs/amdjs-api/blob/master/AMD.md for details.

For view-model configuration you can use following:

  • Constructor function
viewModel: function(params) {
    //Define view model here
    this.title = ko.observable("Hello from component!!!");
}
  • Existing instance of view model
viewModel: { instance: viewModelInstance }
  • Factory function
viewModel: {
              createViewModel: function(params, componentInfo) { 
                                  return new ViewModel(params); 
                               }
           }

Here we have additional parameter componentInfo. This parameter allow us to get access to our custom element with componentInfo.element. But unfortunately we can’t get access to this element before template is applied and analyze it as it was initially added to the document. I’ll describe why I’ve said unfortunately a little later

  • Load view model through AMD
viewModel: { require: 'some/module/name' }
  • Specify whole the component as single AMD module
define(['knockout'], function(ko) {
    return {
        viewModel: function(params) {
           this.title = ko.observable("Hello from component!!!");
        },
        template: '<div data-bind="text: title"></div>'
    };
});

And register it with

ko.components.register('my-component', { require: 'some/module' });

What components are not?

Let’s assume we would like to build a component for bootstrap button with popover. And we would like to open this popover when button is clicked and when another button inside popover is clicked we would like to call some handler in view-model. Something like button with confirmation. And we would like to add custom confirmation template with elements bound to view model.

donate-btn

It would be nice to have component with following syntax

<popover text="Donate" 
		 data-bind="command: makeDonation"
		 title="Enter amount of donation">
	<input class='form-control text-right' type='text' 
               data-bind='value: donationAmount' />
</popover>

But unfortunately it’s not possible. There is no way to read HTML content of the component applied as custom HTML tag, because everything view-model factory, view-model constructor and all other functions are called when template is applied to component and template is required parameter.
Thus you can’t build custom controls with templates inside. Only one possible option is to specify template id as parameter of the your custom control.

<template id='donate-template'>
    <input class='form-control text-right' type='text' 
           data-bind='value: donationAmount' />
</template>

<popover text="Donate" 
		 title="Enter amount of donation" 
		 data-bind="command: makeDonation" 
		 template="donate-template"></popover>

Or use usual binding instead of component to specify template control

<div class="btn btn-xs btn-primary">
   <div data-bind="popover: {title:'Enter ammount', command: makeDonation}"
                   class="hidden">
	 <input class='form-control text-right' type='text' 
                data-bind='value: donationAmount' />
   </div>
   Donate
</div>

More or less equivalent code but imagine how useful this “inline templateing” can be for controls like this http://grid.tesseris.com/Home/Documentation#!/General/general

Let’s hope for the future versions…