So with C# and other languages where you have a get and set assessor on properties, it's quite simple to build a lazy loading pattern.
I only recently started playing with Type Script and I'm trying to achieve something the same. I'm loading a Poco with most of it's properties via a Ajax Call. The problem can be described below:
export interface IDeferredObject<T> {
HasLoaded: boolean;
DeferredURI: string;
}
export class Library{
LibraryName: string;
Books: IDeferredObject<Book[]>;
}
export class Book {
Title: string;
UniqueNumber: number;
}
window.onload = () => {
//Instantiate new Library
var lib = new Library();
//Set some properties
lib.LibraryName = "Some Library";
//Set the Deferred URI Rest EndPoint
lib.Books.DeferredURI = "http://somerestendpoint";
lib.Books.HasLoaded;
//Something will trigger a GET to lib.Books which then needs to load using the Deferred Uri
};
Couple of questions: I
I know it's quite an open question, just looking for some guidance on how to build that pattern.
Thanks
Books
could be declared as a property(see get and set in TypeScript) and in its getter you can start an ajax call. The only issue is that you cannot know when the ajax call will return and you don't want to block the code execution while value for Books
is loading. You can use promise to solve your problem. The following code is using JQuery Deferred Object which is returned by $.ajax
call (since 1.5 version).
One change - HasLoaded
and DeferredURI
. They should reside in "loader class" i.e. Library
because in my example Library
class is responsible for data retrieval.
export class Library {
private _loadedBooks: Book[];
public get Books(): any {
var result: any = null;
// Check first if we've already loaded values from remote source and immediately resolve deferred object with a cached value as a result
if(this.HasLoaded) {
result = $.Deferred();
result.resolve(this._loadedBooks);
}
else {
// Initiate ajax call and when it is finished cache the value in a private field
result = $.ajax(this.DeferredURI)
.done((values) => {
this.HasLoaded = true;
this._loadedBooks = values;
});
}
return result;
}
}
Then code consumer would use Books
as following:
var library = new Library();
library.Books.done((books: Book[]) => {
// do whatever you need with books collection
});
For me this design is counter intuitive and user of this code would not expect the Books
field/property to return a deferred object. So I would suggest to change it to method, something like loadBooksAsync()
. This name would indicate that the code inside this method is asynchronous and hinting that return value is a deferred object which value would be available somewhere later.
Is an Interface even the right thing to use here?
Yes, using interfaces simplifies the coupling problems also in TypeScript
See also: Programmers: Why are interfaces useful?
I'd like to trigger a Load action when something accesses the Library Books Property. Is this even possible?
Yes, it is possible to implement any loading pattern you like. TypeScript
is just JavaScript
when it comes to code loading.
But, for small to medium sized applications the loading is usually not addressed at TypeScript
level but is rather handled by the module loader
and script bundler
.
Supporting statement:
https://github.com/SlexAxton/yepnope.js#deprecation-notice
Deprecation Notice
...The authors of yepnope feel that the front-end community now offers better software for loading scripts, as well as conditionally loading scripts. None of the APIs for these new tools are quite as easy as yepnope, but we assure you that it's probably worth your while. We don't officially endorse any replacement, however we strongly suggest you follow an approach using modules, and then package up your application using require.js, webpack, browserify, or one of the many other excellent dependency managed build tools...
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With