Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / Typescript

Knockout, Self, This, TypeScript. Are You Still Following?

Rate me:
Please Sign up or sign in to vote.
4.78/5 (2 votes)
12 Sep 2016CPOL4 min read 12.5K   1   1
Knockout, self, this, TypeScript. Are you still following?

Introduction

I’m working on an MVC application with simple CRUD operations. I want the following functionality (don’t mind the Flemish (Dutch) titles):

image

Remember, I’m “graphically handicapped”, so I’m afraid that my users will have to do with the standard Bootstrap lay-out for now.

The buttons are self-explanatory and always show the same dialog box. The blue button (Bewaren = Save) changes to Insert or Delete, depending on which action the user chooses.

I will need this simple functionality on some more pages, so I want to make it a bit more generic. I don’t want to use an existing grid because the functionality I need for now is so simple that any grid would be overkill. And of course I’m suffering the NIH syndrome. I will describe the generic solution in another post later.

Knockout and the “self” Thingy

If you have worked with Knockout before, then you know that it is advisable to do something like this (from http://learn.knockoutjs.com/#/?tutorial=loadingsaving):

JavaScript
function TaskListViewModel() {
    // Data
    var self = this;
    self.tasks = ko.observableArray([]);
    self.newTaskText = ko.observable();
    self.incompleteTasks = ko.computed(function () {
        return ko.utils.arrayFilter(self.tasks(), function (task) { return !task.isDone() 
               && !task._destroy });
        });

    // Operations
    self.addTask = function () {
        self.tasks.push(new Task({ title: this.newTaskText() }));
        self.newTaskText("");
    };

    // ...
}

TaskListViewModel is actually a function behaving like a class. As JavaScript doesn’t have classes (yet, wait for ES6), this is the only way to emulate classes. In every OO language, there is an implicit binding on “this”, referring to the object on which a method is called. As you may expect by now, this is different in JavaScript. “this” is referring to where the function is called from, and this is not necessarily the [emulated] class. This is one of the reasons that we all love JavaScript so much. </sarcasm>

There are some ways to tackle this problem, and in the Knockout library, they choose to use the pattern that you see in the code above. When the TaskListViewModel is created, this refers to itself. So we then assign this to a variable in the Model:

JavaScript
var self = this;

The nice thing is now that we can call the functions in TaskListViewModel from anywhere (that is, with possibly a different “this”) and that they will operate on the correct “self”.

Let’s Try This in TypeScript

In TypeScript, the problem remains the same but is even more tricky to detect. The code looks and feels like C# (thank you, Microsoft Glimlach) but eventually it is just JavaScript in disguise. So the “this” problem remains. And actually it gets worse, check out the following (incomplete) code:

JavaScript
class Color {
    ColorId: KnockoutObservable<number>;
    ShortDescription: KnockoutObservable<string>;
    Description: KnockoutObservable<string>;

    constructor(id: number, shortDescription: string, description: string) {
        this.ColorId = ko.observable(id);
        this.ShortDescription = ko.observable(shortDescription);
        this.Description = ko.observable(description);
    }
}</string></string></number>

In TypeScript, every member of a class must be prefixed by this. So that should take care of the scoping problem, not?

Let’s add the ColorsModel class to this and then investigate some behavior:

JavaScript
class ColorsModel { 
    Action: KnockoutObservable<actions> = ko.observable<actions>();
    CurrentItem: KnockoutObservable<color> = ko.observable<color>();
    Items: KnockoutObservableArray<t> = ko.observableArray<t>();

    Empty(): Color {
        return new Color(0, "", "");
    }

    Create(c: any): Color {
        return new Color(c.colorId, c.shortDescription, c.description);
    }

    InsertColor(): void {
        var newColor: Color = this.Empty();
        this.Action(Actions.Insert);
        this.CurrentItem(this.Empty());
        $("#updateColor").modal("show");
    }

    RemoveColor(Color: Color): void {
        this.Action(Actions.Delete);
        this.CurrentItem(Color);
        $("#updateColor").modal("show");
    }

    UpdateColor(Color: Color): void {
        this.Action(Actions.Update);
        this.CurrentItem(Color);
        $("#updateColor").modal("show");
    }
}

var model = new ColorsModel();
ko.applyBindings(model);</t></t></color></color></actions></actions>

In short, we create the ColorsModel class, which contains an array of colors (Items). This model is then bound to the page containing this script. For more information on this, check out http://knockoutjs.com/.

In the page, we have the following (partial!) html:

HTML
<form>
    <button class="btn btn-info" data-bind='click: $root.InsertColor'>
    <span class="glyphicon glyphicon-plus" aria-hidden="true">
    </span>  Kleur toevoegen</button>

    <table class="table table-striped">
        <thead>
            <tr>
                <th>Korte beschrijving</th>
                <th>Beschrijving</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: Items">
            <tr>
                <td data-bind='text: ShortDescription'></td>
                <td data-bind='text: Description'></td>
                <td>
                    <div class="btn-group" role="toolbar">
                        <button title="Update" type="button" class="btn btn-default" 
                        data-bind='click: $root.UpdateColor'><span class="glyphicon glyphicon-pencil" 
                        aria-hidden="true"></span></button>
                        <button title="Delete" type="button" class="btn btn-default" 
                        data-bind='click: $root.RemoveColor'><span class="glyphicon glyphicon-trash" 
                        aria-hidden="true"></span></button>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
</form>

As you can see on the <tbody> element, we bind the Items collection from the ColorModel to the rows. Each item in the collection will create a new <tr> with its values. We also create an update and a delete button, both bound to the $root methods UpdateColor (…) and RemoveColor(…).

The Problem

Running the application in the browser and clicking on the “update” button doesn’t seem to work. So using the debugger in the browser, we discover the following:

image

this” is not the $root (thus the ColorModel). In a real OO language, this would have been the case. Here “this” points to the current color, where we clicked on the “update” button. The debugging Console then rubs it in further:

SCRIPT438: Object doesn't support property or method 'Action'

As you can see in the RemoveColor(…) method, I found a quick workaround involving the use of the global variable model. Maybe that isn’t the right solution after all…

Next Attempt to Solve the Problem

First of all, this attempt didn’t last long, you’ll quickly notice why.

JavaScript
class ColorsModel {
    Self: ColorsModel = this;

    UpdateColor(Color: Color): void {
        this.Self.Action(Actions.Update);
        this.Self.CurrentItem(Color);
        $("#updateColor").modal("show");
    }
}

Remember that in TypeScript when you want to use a property, you need to prefix it with “this”? As we now know, “this” points to the wrong object, so it won’t have a property “this”. I feel a Catch 22 coming up.

A Clean Solution: Arrow Notation

JavaScript
 

When using the => to assign the function to the member UpdateColor, TypeScript will do the necessary to make this work. Looking in the debugger, we see this:

image

And yet, everything is working fine.

If You Can’t Beat Them, Confuse Them

So how is this possible? The bottom line: this is not this. Let’s look at the JavaScript that TypeScript generates for our arrow function:

JavaScript
var _this = this;
UpdateColor = function (item) {
    _this.Action(Actions.Update);
    _this.CurrentItem(item);
    $("#updateColor").modal("show");
};

So the TypeScript “this” is translated into “_this“, and is implemented in just the same way as “self” was before. That solved the scoping problem. The debugger doesn’t catch this subtlety and shows us the content of “this”, hence the confusion. But clearly everything works as it should and our problem is solved in an elegant way.

Conclusion

I’m sorry about this confusing post in which I tried to explain that this is not _this, but in plain JavaScript, this is self, but in TypeScript this is _this. If you understand this conclusion, then you have read the article quite well. Congratulations!

Do you know another solution to this problem? Feel free to share it in the comments!

References

Image 5 Image 6

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Faq.be bvba
Belgium Belgium
Gaston Verelst is the owner of Faq.be, an IT consultancy company based in Belgium (the land of beer and chocolate!) He went through a variety of projects during his career so far. Starting with Clipper - the Summer '87 edition, he moved on to C and mainly C++ during the first 15 years of his career.

He quickly realized that teaching others is very rewarding. In 1995, he became one of the first MCT's in Belgium. He teaches courses on various topics:
• C, C++, MFC, ATL, VB6, JavaScript
• SQL Server (he is also an MSDBA)
• Object Oriented Analysis and Development
• He created courses on OMT and UML and trained hundreds of students in OO
• C# (from the first beta versions)
• Web development (from ASP, ASP.NET, ASP.NET MVC)
• Windows development (WPF, Windows Forms, WCF, Entity Framework, …)
• Much more

Of course, this is only possible with hands-on experience. Gaston worked on many large scale projects for the biggest banks in Belgium, Automotive, Printing, Government, NGOs. His latest and greatest project is all about extending an IoT gateway built in MS Azure.

"Everything should be as simple as it can be but not simpler!" – Albert Einstein

Gaston applies this in all his projects. Using frameworks in the best ways possible he manages to make code shorter, more stable and much more elegant. Obviously, he refuses to be paid by lines of code!

This led to the blog at https://msdev.pro. The articles of this blog are also available on https://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=4423636, happy reading!

When he is not working or studying, Gaston can be found on the tatami in his dojo. He is the chief instructor of Ju-Jitsu club Zanshin near Antwerp and holds high degrees in many martial arts as well.

Gaston can best be reached via https://www.linkedin.com/in/gverelst/.


Comments and Discussions

 
GeneralMy vote of 3 Pin
Member 1236439014-Sep-16 22:16
Member 1236439014-Sep-16 22:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.