Wednesday, May 29, 2013

HTML5+MVC Course, Day 4

Continued from day 1, day 2, day 3.

This lesson was mostly about writing Object Oriented in JavaScript.

 

Lets start by creating a basic model Customer, customer contains a first name, last name and full name that contains both:

function customer() {
    var _this = this;
    _this.firstName = 'Yair';
    _this.lastName = 'Siwek';
    _this.fullName = _this.firstName + ' ' + this.lastName;
}

Given that this is our body:

<body>
    <div>
        <span id="firstName"></span>
        --
        <span id="lastName"></span>
    </div>
    <div>
        <span id="fullName"></span>
    </div>
</body>

A basic use for the model will be:

<script src="Scripts/App/Customer_a.js"></script>
<script>
    $(function () {
        var cust = new customer();
        cust.firstName = "Moshe";

        $('#firstName').html(cust.firstName);
        $('#lastName').html(cust.lastName);
        $('#fullName').html(cust.fullName);
    });
</script>

This works.

But what if before using jQuery we would have changed firstName? then fullName would have still been Yair Siwek. In order for the right value for fullName to appear we need to change fullName to a method:

_this.fullName = function ()
{
    //if we wrote this then it's value would have been fullName, but _this is customer!
    return (_this.firstName + ' ' + _this.lastName);
};

TODO: _this

We also need to change our html:


$('#fullName').html(cust.fullName());

to use a method instead of a property.

 

We can now create another instance of customer:

var cust = new customer();
var cust1 = new customer();

But if we use a debugger we will see the method fullName is recreated for each instance of customer:

CustomerMethodDefinition

To fix that we will use a Prototype. A Prototype is common to all instances:

function customer() {
    var _this = this;
    _this.firstName = 'Yair';
    _this.lastName = 'Siwek';
}

customer.prototype = {
    fullName:  function ()
    {
        //_this does not exist here!
        return (this.firstName + ' ' + this.lastName);
    }
}

Note: We can’t use _this here because it doesn’t exist in the scope.

Now the instance doesn’t include the implementation (the Prototype has it).

CustomerPrototype

 

We could have also used an Anonymous Type:

<script>
    $(function () {
        var cust = {
            firstName: 'Joe',
            lastName: 'Sh',
            fullName: function () {
                return cust.firstName + ' ' + cust.lastName;
            }
        };

        $('#firstName').html(cust.firstName);
        $('#lastName').html(cust.lastName);
        $('#fullName').html(cust.fullName());
    });
</script>

Though we can’t use multiple instances the result is the same.

 

Book recommendation: Learning JavaScript Design Patterns by Addy Osmani (166 pages)

Shows how to create an empty object – 3 ways.

 

With the last example there was a problem: changing the values does not change the controls:

cust.firstName = "Moshe";

$('#firstName').html(cust.firstName);
$('#lastName').html(cust.lastName);
$('#fullName').html(cust.fullName());

cust.lastName = 'Blah';

Both last name and full name won’t contain “Blah”.

To solve that we will have to do a data binding with KnockOut.

var cust = new customer();
cust.firstName = "Moshe";

ko.applyBindings(cust,
                        //$('#divTop'));//doesn't work
                        //document.getElementById('divTop'));//works
                        $('#divTop')[0]);//work
cust.lastName = 'Blah';

model – the data

node - the element on which we want to the binding,

$(‘#Name’) returns an array, node needs a single element so we will have to pass the first element by adding [0].

 

Note: to add inteliscence to the JavaScript code we will drag and drop the KnockOut library to the top of the script, it will be converted to:

/// <reference path="../knockout-2.2.1.debug.js" />

And from now on you have inteliscence turned on (if you have Resharper then you can skip this step).

 

To make the model observable we will have to change each field to be an observable by:

_this.firstName = ko.observable('Yair');
_this.lastName = ko.observable('Siwek');

that will convert it to a method which is used by:

In HTML:

<input id="firstName" data-bind="value: firstName"/>

In JavaScript:

customer.prototype = {
    fullName:  function ()
    {
        return (this.firstName() + ' ' + this.lastName());
    }
}

When writing the data-bind from the HTML you can use either name() or name. But from JavaScript you must use name().

TODO:check why in JS

 

TODO: 18:06 not clear: when working with ko.computed +ko.observable than firstName() in the HTML data-bind doesn’t work. TODO: add code

TODO: There is difference between ko.computed and function(), computed is only called once?

 

Observable Array

When the array is simple (non object):

_this.grades = ko.observableArray([10, 11, 12]);

<ul data-bind="foreach: grades">
    <li>
        <span data-bind="text: $index" ></span>
        .
        <span data-bind="text: $data" ></span>
    </li>
</ul>

$index – the index of the item in the array.

$data – the item data

Output:

ObservableSimpleArray

 

When the array is of objects we can bind to the property names:

_this.grades = ko.observableArray([
    { subject: 'hist', mark: 90 },
    { subject: 'math', mark: 95 } ]);

<ul data-bind="foreach: grades">
    <li>
        <span data-bind="text: subject" ></span>
        -
        <span data-bind="text: mark" ></span>
    </li>
</ul>

Note: for intelliscence in KO you will have to install ASP.NET and Web Tools 2012.2.

 

KO Templates

HTML usage of a Template has at least 2 prameters:

name – the template name

data – the observable data

The template definition:

TODO: Add code

id – the name of the template

 

Adding templates to a page:

1. Using href:

TODO

Will cause the template to be downloaded to the client even when the template is not used.

2. Dynamically through JS code:

TODO

Tuesday, March 5, 2013

ArcObjects: Licensing

This post is the 7th post in my ArcObjects series.

ESRI has a very annoying licensing settings which I curse every time my license expires (once a year using EDN). Lets start by saying ESRI delivers the license in the form of ABC######: EFL123456, ESU123456, ECP123456, RUD123456 and each has a different set of instructions. I usually get an ECP license (ArcGis Server) and there is a guide for that.

Assuming you managed to pass the nightmare and tried to use ArcObjects in your code then you will get an Exception. In this case I just run this code:

IPoint point = new PointClass { X = x, Y = y, SpatialReference = CreateWgsSpatialReference() };

And got this exception:

System.Runtime.InteropServices.COMException : Retrieving the COM class factory for component with CLSID {00A5CB41-52DA-11D0-A8F2-00608C85EDE5} failed due to the following error: 80040111 ClassFactory cannot supply requested class (Exception from HRESULT: 0x80040111 (CLASS_E_CLASSNOTAVAILABLE)).

As you can see it is very descriptive. But basically it tells you that without initializing the license you won’t be able to use any ArcObjects (even IPoint). In version 9.3 the license was done using class RuntimeManager but in version 10 the IAoInitialize interface was added. I usually add them together like this:

private static bool Initialize(ProductCode product, esriLicenseProductCode esriLicenseProduct)
{
    if (RuntimeManager.Bind(product))
    {
        IAoInitialize aoInit = new AoInitializeClass();
        aoInit.Initialize(esriLicenseProduct);
        return true;
    }
    return false;
}

This method checks if the current machine has a license of ProductCode which is an enum with values like Server or Engine and if it does returns true. The usage looks like this:

private static bool _isStarted = false;

public static void Start()
{
    if (_isStarted)
        return;

    if (!Initialize(ProductCode.Server, esriLicenseProductCode.esriLicenseProductCodeArcServer))
    {
        if(!Initialize(ProductCode.Engine, esriLicenseProductCode.esriLicenseProductCodeEngineGeoDB))
        {
            throw new ApplicationException(
                "Unable to bind to ArcGIS license Server nor to Engine. Please check your licenses.");
        }
    }
    _isStarted = true;
}

Since the production machines are usually ArcGis Server and the development machines are Engine we usually check if the machine has Server license and if it doesn’t then we go to the fallback of Engine. Now the creation of PointClass will work.

 

That’s it.