Edit: I found out how to get around this problem. View this post.
My love of Knockout.js just took a big hit. I recently converted one of my old applications to TypeScript and in the process I was going to try to turn it into a Chrome web app.
Short story: Chrome web apps don't like Knockout.
Long story: I've created a couple other Chrome web apps in the past but they are the kind that are only links to the app on my website. There is another kind that is actually is able to run in its own window like a real application and can be run on other devices like Chromebooks. That's what O was trying to do.
So I got my app working and used Knockout to do some data binding. Since my app uses local storage I also had to convert it to use Chrome's version of local storage. If you're building one of these Chrome apps you must use their local storage, not the HTML5 kind. Since I wanted my app to run both in the browser and as a Chrome app I had to do a lot of work to make it use the correct storage based on the context.
After I got everything working I tried to test my Chrome app and that's when things went south. Apparently Chrome apps have a strict content security policy (CSP) which doesn't allow you to use things like eval() or new Function(). This is because a Chrome app has access to more resources than a normal web app running in your browser. Therefore someone could do potential harm to your computer if they were able to access it using one of those runtime evaluation methods.
I found out real quickly that Knockout doesn't work with CSP because it does have some runtime evaluation code. Ugh! A bunch of work down the drain. Well, I shouldn't say it was all a waste of time. I did learn to make my apps work in different contexts, how Chrome local storage works, and how to set up a Chrome web app.
I'm just disappointed that I can't use Knockout if I want to have my apps available in the Chrome web store. I've committed heavily to Knockout for my web apps. I think it has a great balance between enough functionality and being too opinionated, like say Angular is. I really enjoy using it.
I guess the search is on for a new MV* framework that isn't too hard to learn, isn't too opinionated, and works in Chrome web apps. Maybe it's back to using Angular, or maybe it's something else, I don't know. I'll keep you posted.
JM Gustafson's programming blog focusing on single page apps created with TypeScript
Thursday, February 25, 2016
Monday, February 8, 2016
Knockout with TypeScript - Embedded View Models
In my last post about Knockout and TypeScript I talked about interacting with observable arrays. This time we'll take a look at using embedded view models.
Knockout uses the concept of view models at its core. View models link to the view to provide data binding and functionality. There are times when you may want a more complicated structure for your view model. You may want to have child view models inside of view models so you can partition your application more effectively. So today I'll talk about how you do that.
What I sometimes like to do is create a master view model for my application in general, and then add view models underneath it for specific parts of the application. Say we have an application with two different views that have different data. In this example one view shows a list of favorite books and another shows favorite authors. You could have one view model with everything in it, but you can get a problem with namespacing if both views have similar properties.
In this case we need to keep track of the sort order for both lists. Since they are both in the same view model we are required to give them unique names. We could make this easier by using child view models. Let's make a view model for authors and one for books.
What we did was move the properties that were specific to each view into their own view model. Then we reference those view models inside of our main view model, FavoritesViewModel. This makes it a lot easier to determine what data goes with which view.
Now we need a way to use those view models in our HTML file. Knockout has a control flow binding that can help us out with that. It's the "with" binding. The "with" binding specifies a view model to use for a specific section of HTML.
If you look at the <div> for our favorite authors section it has a "with: authorsVM" binding. From that point down in the DOM the context for the data will be the AuthorsViewModel object. So now if you're in the authorsVM context and you click the sort order checkbox it's going to refer to the sortAscending property on the authorsVM model. Same for the favorite books section, it has a "with: booksVM" binding.
The "with" binding in Knockout makes it easy for us to separate our view models by view which make it easier to read and maintain our code when dealing with multiple views. We start with a primary application view model and add child view models to it then associate them with different views.
Knockout uses the concept of view models at its core. View models link to the view to provide data binding and functionality. There are times when you may want a more complicated structure for your view model. You may want to have child view models inside of view models so you can partition your application more effectively. So today I'll talk about how you do that.
What I sometimes like to do is create a master view model for my application in general, and then add view models underneath it for specific parts of the application. Say we have an application with two different views that have different data. In this example one view shows a list of favorite books and another shows favorite authors. You could have one view model with everything in it, but you can get a problem with namespacing if both views have similar properties.
class AuthorsViewModel1 { public appName = "Two views demo"; public currentView = ko.observable("authors"); public authors = ko.observableArray<string>(); public books = ko.observableArray<string>(); public sortAuthorsAscending = ko.observable(true); public sortBooksAscending = ko.observable(true); constructor() { this.sortAuthorsAscending.subscribe(() => this.authors.reverse()); this.sortBooksAscending.subscribe(() => this.books.reverse()); } }
In this case we need to keep track of the sort order for both lists. Since they are both in the same view model we are required to give them unique names. We could make this easier by using child view models. Let's make a view model for authors and one for books.
class AuthorsViewModel { public authors = ko.observableArray<string>(); public sortAscending = ko.observable(true); constructor() { this.sortAscending.subscribe(() => this.authors.reverse()); } } class BooksViewModel { public books = ko.observableArray<string>(); public sortAscending = ko.observable(true); constructor() { this.sortAscending.subscribe(() => this.books.reverse()); } } class FavoritesViewModel { public appName = "Two Views Demo"; public currentView = ko.observable("Authors"); public authorsVM = new AuthorsViewModel(); public booksVM = new BooksViewModel(); }
What we did was move the properties that were specific to each view into their own view model. Then we reference those view models inside of our main view model, FavoritesViewModel. This makes it a lot easier to determine what data goes with which view.
Now we need a way to use those view models in our HTML file. Knockout has a control flow binding that can help us out with that. It's the "with" binding. The "with" binding specifies a view model to use for a specific section of HTML.
<body> <h1 data-bind="text: appName"></h1> <select data-bind="value: currentView"> <option>Authors</option> <option>Books</option> </select> <div data-bind="visible: currentView() === 'Authors', with: authorsVM"> <h2>Favorite Authors</h2> <label><input type="checkbox" data-bind="checked: sortAscending" /> Ascending</label> <ul data-bind="foreach: authors"> <li data-bind="text: $data"></li> </ul> </div> <div data-bind="visible: currentView() === 'Books', with: booksVM"> <h2>Favorite Books</h2> <label><input type="checkbox" data-bind="checked: sortAscending" /> Ascending</label> <ul data-bind="foreach: books"> <li data-bind="text: $data"></li> </ul> </div> </body>
If you look at the <div> for our favorite authors section it has a "with: authorsVM" binding. From that point down in the DOM the context for the data will be the AuthorsViewModel object. So now if you're in the authorsVM context and you click the sort order checkbox it's going to refer to the sortAscending property on the authorsVM model. Same for the favorite books section, it has a "with: booksVM" binding.
The "with" binding in Knockout makes it easy for us to separate our view models by view which make it easier to read and maintain our code when dealing with multiple views. We start with a primary application view model and add child view models to it then associate them with different views.
TypeScript What/Why/Who/Where/When
Using TypeScript instead of JavaScript can really help to develop and maintain medium to large applications. In this post I'd like to go over some of the situations where I think TypeScript would be beneficial by answering the What/Why/Who/Where/When of TypeScript.
In addition, TypeScript also has support for many of the new features in the ECMAScript 6 specification such as modules, classes and arrow functions (modules are like namespaces or packages). These will get compiled down JS that is compatible with lesser versions of JS so you can use them in older browsers.
Well, the problem with JS is that it's very loosely typed (you could argue that's also one of it's strengths). For example, you can create a variable and set it to a string, then in the next line of code set it to a number, then set it to an object. Variables don't have types in JS.
Same with function parameters. In all actuality function parameters are just a convenience in JS. You can pass as many or as few parameters as you like to a function and JS won't care (the inside of the function may care, but not JS).
Same with objects. A JS object is just an associative array, or hash table, or dictionary, or whatever you want to call it. JS objects have no definite shape.
While the absence of typing can sometimes be a very powerful tool, it can also cause a lot of confusion. When writing large applications, using types gives you piece of mind. Once you get to the point where you can't keep the entire application in your head, typing provides protection and knowledge.
First of all it's like having a set of built in unit tests that get run every time you compile. We can all agree that it's better to find problems earlier rather than later at runtime. Would you rather have the compiler tell you exactly where the problem is, or step through your code in the debugger?
Secondly, all you have to do is look at a function's parameters and their types to know exactly what the function expects. Without it you have to look at the function body to know what the function is expecting, or write very good comments. There's no need to write comments if you can look at a variable or parameter and see what its type is. Its self documenting.
Lastly, TypeScript lets your IDE give you better tooling support. It's very hard for an IDE to give you auto complete and auto suggest information when using an untyped language such as JS. For example, it has no idea what types the parameters to a function are, therefore it can't tell you for certain what it expects. Same with objects; it has no way of knowing what fields an object contains. But once your IDE knows about the types of variables and parameters it can give you all kinds of help. It's another form of documentation. You also get better refactoring and usage finding support.
TypeScript also has what are known as type definition files. They provide type information about external JS libraries. That means you get all the tooling support you would expect for your favorite JS libraries including jQuery, Angular, Lodash, etc. (there a TD files available for nearly every popular library out there). No more going to look up documentation because you forgot the signature of a function call in one of these libraries, it's right there at your fingertips, including the types that the function parameters expect.
I think one of the biggest problems that programmers coming over to JS from languages like Java or C# have is that they just don't understand JS objects and how prototypal inheritance works. When I first started writing JS that was my biggest hurdle. I spent a lot of time and energy trying to make JS objects follow the classical OOP paradigms I was used to. With TypeScript you don't have to worry about that because it supports classical OOP.
Even if you are a JavaScript programmer you might want to consider using TypeScript for larger sized projects just to get better tooling in your IDE. It's also going to make it easier in a team environment for other team members to read and understand your code. Remember, TS is JS so you don't have to use every feature in TypeScript all the time. Just use the pieces you want and ease into it. I recently worked on a project with a JavaScript programmer and convinced him to use TypeScript just to get better documentation from type definitions. By the end of the project he also became a fan of static typing and plans to use TypeScript from now on.
Remember the golden rule of programming: Code is written for people to read, not computers.
What
Let's start with "what" since you need to know what TypeScript is before we can start. TypeScript is a typed superset of JavaScript that compiles down to plain old JavaScript. What this means is that TypeScript is JavaScript with a few features added, such as static typing. This means that you can specify what type a variable or parameter to a function is. TypeScript has a number of built in types: string, number, boolean, array, enum, any, void. In addition you can create your own types using interfaces, classes and enums.The benefits of static typing are numerous and will be discussed in the "Why" section below.In addition, TypeScript also has support for many of the new features in the ECMAScript 6 specification such as modules, classes and arrow functions (modules are like namespaces or packages). These will get compiled down JS that is compatible with lesser versions of JS so you can use them in older browsers.
Why
This is the most important question. Why use TypeScript at all. JavaScript is just fine, right?Well, the problem with JS is that it's very loosely typed (you could argue that's also one of it's strengths). For example, you can create a variable and set it to a string, then in the next line of code set it to a number, then set it to an object. Variables don't have types in JS.
Same with function parameters. In all actuality function parameters are just a convenience in JS. You can pass as many or as few parameters as you like to a function and JS won't care (the inside of the function may care, but not JS).
Same with objects. A JS object is just an associative array, or hash table, or dictionary, or whatever you want to call it. JS objects have no definite shape.
While the absence of typing can sometimes be a very powerful tool, it can also cause a lot of confusion. When writing large applications, using types gives you piece of mind. Once you get to the point where you can't keep the entire application in your head, typing provides protection and knowledge.
First of all it's like having a set of built in unit tests that get run every time you compile. We can all agree that it's better to find problems earlier rather than later at runtime. Would you rather have the compiler tell you exactly where the problem is, or step through your code in the debugger?
Secondly, all you have to do is look at a function's parameters and their types to know exactly what the function expects. Without it you have to look at the function body to know what the function is expecting, or write very good comments. There's no need to write comments if you can look at a variable or parameter and see what its type is. Its self documenting.
Lastly, TypeScript lets your IDE give you better tooling support. It's very hard for an IDE to give you auto complete and auto suggest information when using an untyped language such as JS. For example, it has no idea what types the parameters to a function are, therefore it can't tell you for certain what it expects. Same with objects; it has no way of knowing what fields an object contains. But once your IDE knows about the types of variables and parameters it can give you all kinds of help. It's another form of documentation. You also get better refactoring and usage finding support.
TypeScript also has what are known as type definition files. They provide type information about external JS libraries. That means you get all the tooling support you would expect for your favorite JS libraries including jQuery, Angular, Lodash, etc. (there a TD files available for nearly every popular library out there). No more going to look up documentation because you forgot the signature of a function call in one of these libraries, it's right there at your fingertips, including the types that the function parameters expect.
Who
Now that we know what TypeScript is and why you should use it, let's answer the question of who TypeScript is for. Anyone can use TypeScript of course. But if you are a Java or C# programmer you will feel right at home with TypeScript. That's because TypeScript bridges the gap between what classical OOP programmers expect and how JS does prototypal inheritance. Behind the scenes TypeScript does all the work of converting classes and modules into JS objects.I think one of the biggest problems that programmers coming over to JS from languages like Java or C# have is that they just don't understand JS objects and how prototypal inheritance works. When I first started writing JS that was my biggest hurdle. I spent a lot of time and energy trying to make JS objects follow the classical OOP paradigms I was used to. With TypeScript you don't have to worry about that because it supports classical OOP.
Even if you are a JavaScript programmer you might want to consider using TypeScript for larger sized projects just to get better tooling in your IDE. It's also going to make it easier in a team environment for other team members to read and understand your code. Remember, TS is JS so you don't have to use every feature in TypeScript all the time. Just use the pieces you want and ease into it. I recently worked on a project with a JavaScript programmer and convinced him to use TypeScript just to get better documentation from type definitions. By the end of the project he also became a fan of static typing and plans to use TypeScript from now on.
Where
You can use TypeScript anywhere that you use JS. That includes single page applications (SPAs) in the browser or Node.js applications. Since TypeScript compiles to plain JS it can be used in any browser that supports JS. You can even start using the ES6 features that TypeScript supports now rather than waiting until all of the browsers get enough support that you can specifically target ES6.When
Should one use TypeScript all the time? I don't think so. Yes, you heard me right. I like writing TypeScript but there are cases when it's overkill, I'll be the first to admit that. My recommendation, and the rule of thumb I use, is when I can't keep everything in my head anymore then I switch to TypeScript. For me that's when the code starts to take up more than a page of code or I start breaking my code into multiple files. If I can't see it all then I have to spend time moving around looking for things which slows me down. It's also going to make it harder for someone else to read your code.Remember the golden rule of programming: Code is written for people to read, not computers.
Subscribe to:
Posts (Atom)