Dust.js overwrites Knockout observables on render - javascript

I am using Dust.js and Knockout.js together on a project, using a module called Duster-KO to integrate the two. The problem happens when I am trying to render a dust template client-side: when I pass an observable, or any object containing an observable, to dust.render() in the Context parameter, Dust is actually setting the KO observable to be a "Chunk" object. I believe this is because Knockout observables are functions, and so Dust thinks that the function I am passing it is a callback instead of an observable, which it is then executing and somehow setting the observable that way.
Is there any way to avoid this issue, or otherwise prevent Dust from touching the observables?
Here is an example of a situation I have run into:
var guest = exports.guest = function(opts) {
this.first = ko.observable(opts.first||"")
this.last = ko.observable(opts.last||"")
// ... more model code here
}
var table = exports.table = function(opts) {
// This is an observable array of guest objects
this.guests = ko.observableArray(opts.guests||[])
this.template = "tableTemplate"
this.target = opts.target // This is whatever DOM element we are injecting the template into
// ... more model code here
var self = this
this.draw = function() {
// Before we render the Dust template, the guest's first and last name are as they should be
// this.ctx is a Context object inherited from another parent object, which has the current object pushed onto it
var rendered = dust.render(self.template, this.ctx)
// At this point in the code, the guest's first and last name have been set to Chunk objects, rather than their actual first and last names
self.target.appendChild(rendered)
}
}
In the above example, before I render the dust template each guest's first and last names are intact and as they should be. However, afterwards they are changed to Chunk objects.
And before someone suggests it, removing Dust and using only Knockout is not an option right now unfortunately.

Did u apply the hack mentioned in the Duster-Ko Readme file???
Why the Dust hack :(
Unpleasant business, that.
Dust expects any functional tags to accept a set of parameters (chunk,
context). We could build a Dust-friendly wrapper for every KO
Observer, and build Dust contexts out of these, but that seems like an
awful lot of unnecessary object creation.
Instead, we just hack Dust to not evaluate Observers like it normally
would, and handle the aftermath with a more stock standard
helper-filter.
The Source
These changes are done in whatever dust*js you're using.
Prime hackery:
Chunk.prototype.reference = function(elem, context, auto, filters) {
- if (typeof elem === "function") {
+ if (typeof elem === "function" && elem.name != "observable") {
elem = elem(this, context, null, {auto: auto, filters: filters});`
Oh, also, we're manually invoking some Dust templates & cough eval'ing
that. To manually invoke the template, we need to pass in a Dust Chunk
object, which normally we wouldn't be exposed to, so:
+dust.chunk= Chunk Tis all! Checkout lib/dust-patch.js for a patch
against unspecified Dust sources (for now, dust-core-0.3.0.js is the
intended target).

Related

Why is stack.head prefix required to access local context in Dust.js?

I have a strange issue with Dust.js contexts. Previously, I would pass the view model along as a plain old JavaScript object, e.g. res.render('page', { something: [1,2,3] } etc. When doing that, I could access something on the local context with {#something}...{/something} etc.
However, I changed the way I manage the context to use dust.makeBase, so that I can have globals and some sort of stack, rather than just an object. I'm using consolidate with express FWIW.
Now, I create the baseViewModel.
//
// at application init
//
app.baseViewModel = dust.makeBase({
someGlobal: 'example'
})
Later on, when rendering, I may extend it like so:
//
// in route handler
//
const viewModel = app.baseViewModel.push({
collection: someCollection
})
res.render('index', viewModel)
But then, the context stack looks like this:
{
"settings": {
// snip
},
"stack": {
"isObject": true,
"head": {
"collection": [
// snip
]
}
},
"global": {
"someGlobal": "example"
}
}
The problem is, now, to access collection, I must prefix the variables with stack.head.:
{#stack.head.collection}
<!-- etc -->
{/stack.head.collection}
Does anyone know why this is, and how I can get back to the simple way of just referring to {#collection} etc?
Thank you.
I managed to get this working, but it's a hack and should have to be done this way. There is a GitHub ticket now for this issue:
https://github.com/linkedin/dustjs/issues/743
Anyway, this is what I did:
dust.helpers.collection = (chunk, context, bodies, params) => {
return context.current().get('collection')
}
And then modified my markup to:
{#collection}
{.Name} etc
{/collection}
Strange, but I guess that's the way it works!
As per our comment chain, here's what I suspect to be the issue.
When you create a new Context object in Dust, it checks to see if you're passing in an existing Context to hydrate from. This is done using an instanceof check.
If the object does not seem to be an instance of a Context, it gets wrapped in a new Context, which would account for the behavior you're seeing here. This isn't great, so I'll go work on a PR to use a flag instead of an instanceof check.
You're using consolidate, which includes a dependency on dust, and I suspect that the version it includes is different than whatever you depend on in your package.json. I would check to see how many copies of dustjs-linkedin are in your tree (one in your root and one in consolidate's node_modules, perhaps). If you require dustjs-helpers it simply requires dustjs-linkedin itself, so the latter is what you really care about.

JavaScript: Is the nesting of constructor instances inside a constructed 'wrapper' problematic?

Hopefully this question won't be flagged as too subjective but I'm newish to OOP and struggling a bit when it come to sharing data between parts of my code that I think should be separated to some extent.
I'm building a (non-geo) map thing (using leaflet.js which is superduper) which has a map (duh) and a sidebar that basically contains a UI (toggling markers both individually and en masse, searching said marker toggles as well as other standard UI behaviour). Slightly confused about organisation too (how modular is too modular but I can stumble through that myself I guess). I am using a simple JSON file for my settings for the time being.
I started with static methods stored in objects which is essentially unusable or rather un-reusable so I went for nested constructors (kinda) so I could pass the parent scope around for easier access to my settings and states properties:
function MainThing(settings) {
this.settings = options;
this.states = {};
}
function SubthingMaker(parent) {
this.parent = parent;
}
SubthingMaker.prototype.method = function() {
var data = this.parent.settings.optionOne;
console.log(data);
this.parent.states.isVisible = true;
};
MainThing.prototype.init = function() {
this.subthing = new SubthingMaker(this);
// and some other fun stuff
};
And then I could just create and instance of MainThing and run MainThing.init() and it should all work lovely. Like so:
var options = {
"optionOne": "Hello",
"optionTwo": "Goodbye"
}
var test = new MainThing(options);
test.init();
test.subthing.method();
Should I really be nesting in this manner or will it cause me problems in some way? If this is indeed okay, should I keep going deeper if needed (maybe the search part of my ui wants its own section, maybe the map controls should be separate from DOM manipulation, I dunno) or should I stay at this depth? Should I just have separate constructors and store them in an object when I create an instance of them? Will that make it difficult to share/reference data stored elsewhere?
As regards my data storage, is this an okay way to handle it or should I be creating a controller for my data and sending requests and submissions to it when necessary, even if that data is then tucked away in simple JSON format? this.parent does really start to get annoying after a while, I suppose I should really be binding if I want to change my scope but it just doesn't seem to be an elegant way to access the overall state data of the application especially since the UI needs to check the state for almost everything it does.
Hope you can help and I hope I don't come across as a complete idiot, thanks!
P.S. I think the code I posted works but if it doesn't, its the general idea I was hoping to capture not this specific example. I created a much simpler version of my actual code because I don't want incur the wrath of the SO gods with my first post. (Yes, I did just use a postscript.)
An object may contain as many other objects as are appropriate for doing it's job. For example, an object may contain an Array as part of its instance data. Or, it may contain some other custom object. This is normal and common.
You can create/initialize these other objects that are part of your instance data in either your constructor or in some other method such as a .init() method whichever is more appropriate for your usage and design.
For example, you might have a Queue object:
function Queue() {
this.q = [];
}
Queue.prototype.add = function(item) {
this.q.push(item);
return this;
}
Queue.prototype.next = function() {
return this.q.shift();
}
var q = new Queue();
q.add(1);
q.add(2);
console.log(q.next()); // 1
This creates an Array object as part of its constructor and then uses that Array object in the performance of its function. There is no difference here whether this creates a built-in Array object or it calls new on some custom constructor. It's just another Javascript object that is being used by the host object to perform its function. This is normal and common.
One note is that what you are doing with your MainThing and SubthingMaker violates OOP principles, because they are too tightly coupled and have too wide access to each other internals:
SubthingMaker.prototype.method = function() {
// it reads something from parent's settings
var data = this.parent.settings.optionOne;
console.log(data);
// it changes parent state directly
this.parent.states.isVisible = true;
};
While better idea could be to make them less dependent.
It is probably OK for the MainThing to have several "subthings" as your main thing looks like a top-level object which will coordinate smaller things.
But it would be better to isolate these smaller things, ideally they should work even there is no MainThing or if you have some different main thing:
function SubthingMaker(options) {
// no 'parent' here, it just receives own options
this.options = options;
}
SubthingMaker.prototype.method = function() {
// use own options, instead of reading then through the MainThing
var data = this.options.optionOne;
console.log(data);
// return the data from the method instead of
// directly modifying something in MainThing
return true;
this.parent.states.isVisible = true;
};
MainThing.prototype.doSomething = function() {
// MainThing calls the subthing and modifies own data
this.parent.states.isVisible = this.subthing.method();
// and some other fun stuff
};
Also to avoid confusion, it is better not to use parent / child terms in this case. What you have here is aggregation or composition of objects, while parent / child are usually used to describe the inheritance.

What is the benefit of this pattern: loos augmentation

So, I have the need for a singleton. It really is a rather large "do something" object. Processes information etc.. it could be extended, and some methods could or might even be inherited, but overall, there doesn't need to exist more than one of them. So, I read a bit here which I love the concept: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
I am thinking more in terms of leveraging the sub module behavior.
But, I'd like to break my obj into sub-modules. But I am not seeing the need to pass in the parent sub-module as the "return" on that parent gives me access anyways. ala. Perhaps I am missing the "robustness" or real usage here.
For example.
var a = {};
a.m = function(){
var conf = {
a: 'aaa',
b: 'bbb'
}
var funcs = {
func1: function(){
console.log('a.m sub object func1');
}
}
return { // doing this gives me access
conf: conf,
funcs: funcs
};
}()
// this sub module obj WILL need some behaviors/methods/vals in a.m
a.anothersub = (function(m){
var anotherSub = m;
anotherSub.funcs.func1(); // access to a.m methods do I even need to pass it in?
a.m.funcs.func1(); // also access to a.m methods
}( a.m || {}))
// is a better approach to extend a.anothersub with a.m?
// jQuery.extend(a.anothersub, a.m);
If both "m" and "anothersub" are part of object 'a'. Is there a need for loose or tight augmentation here and for sake of keeping code compartmentalized and of same function behavior, I am creating these "sub objects".
I read that article and felt I could leverage its power. But not really sure this is the best approach here, or even needed. Your thoughts?
This all comes down to how tightly-coupled your modules/submodules actually are, and how much you can expect them to exist in all places around your application (ie: every page of a site, or at the global level of an application, et cetera).
It's also broaching a couple of different topics.
The first might be the separation of concerns, and another might be dependency-inversion, while another, tied to both, might be code organization/distribution.
Also, it depends on how cohesive two submodules might be...
If you had something like:
game.playerFactory = (function () {
return {
makePlayer : function () { /*...*/ }
};
}());
game.playerManager = (function (factory) { return {/*...*/}; }(game.playerFactory));
It might make sense to have the factory passed into the manager as an argument.
At that point, attaching both to game is really just a convenient place to make both accessible to the global scope.
Calling game from inside of one or the other, however, is problematic, in large systems, systems with lots of submodules, or systems where the interface is still in flux (when are they not?).
// input-manager.js
game.inputManager = (function () {
var jumpKey = game.playerManager.players.player1.inputConfig.jump;
}());
If all of your buttons are mapped out and bound to in that way, for every button for every player, then all of a sudden you've got 40 lines of code that are very tightly bound to:
The global name of game
The module name of playerManager
The module-interface for playerManager (playerManager.players.player1)
The module-interface for player (player.inputConfig.jump)
If any one of those things changes, then the whole submodule breaks.
The only one the input-manager should actually care about is the object that has the .inputConfig interface.
In this case, that's a player object... ...in another case, it might be completely decoupled or stuck on another interface.
You might be half-way through implementing one gigantic module, and realize that it should be six smaller ones.
If you've been passing in your objects, then you really only need to change what you're passing in:
game.inputManager = (function (hasInput) {
var jumpKey = hasInput.inputConfig.jump;
}(game.playerManager.players.player1));
Can easily become
game.inputManager = function (hasInput) {
/*...*/
}(game.playerManager.getPlayer("BobTheFantastic").config));
and only one line of code changed, rather than every line referencing game. ......
The same can be said for the actual global-reference:
// my-awesome-game.js
(function (ns, root) {
root[ns] = { };
}( "MyAwesomeGame", window ));
// player-factory.js
(function (ns, root) {
root[ns] = {
make : function () { /*...*/ }
};
}("playerFactory", MyAwesomeGame));
// player-manager.js
(function (ns, root, player) {
var manager = {
players : [],
addPlayer : function () { manager.players.push(player.make()); }
};
}("playerManager", MyAwesomeGame, MyAwesomeGame.playerManager));
Your code isn't impervious to change, but what you have done is minimize the amount of change that any one submodule needs to make, based on external changes.
This applies directly to augmentation, as well.
If you need to override some piece of some software, in a completely different file, 20,000 lines of code down the page, you don't want to have to suffer the same fate as changing interfaces elsewhere...
(function (override, character) {
override.jump = character.die;
}( MyAwesomeGame.playerManager.get(0), MyAwesomeGame.playerManager.get(1) ));
Now, every time player 1 tries to jump, player 2 dies.
Fantastic.
If the interface for the game changes in the future, only the actual external call has to change, for everything to keep working.
Even better.

RequireJs extend module initialize begin and end

I have created a JavaScript library which can be used for logging purposes.
I also want to support the logging of requirejs.
Which functions/events of requirejs can I prototype/wrap so I can log when a module is initialized and when it is done initializing and returns the initialized object.
For instance if I call require(["obj1","obj2", "obj3"], function(obj1, obj2, obj3){}
I would like to know when requirejs begins on initializing each of the object, and I would like to know when each object is completely initialized.
I looked into the documentation/code, but could not find any usefull functions I can access from the requirejs object or the require object.
Note: I do not want to change the existing code of requirejs I wish to append functionality from the outside by either prototyping or wrapping.
What I have tried (problem is that this only accesses the begin and end of the entire batch of modules):
var oldrequire = require;
require = function (deps, callback, errback, optional) {
console.log("start");
var callbackWrapper = callback;
callbackWrapper = function () {
console.log("end");
var args = new Array();
for(var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
callback.apply(this, args);
};
oldrequire.call(this, deps, callbackWrapper, errback, optional);
};
This is a "better than nothing answer", not a definitive answer, but it might help you look in another direction. Not sure if that's good or bad, certainly it's brainstorming.
I've looked into this recently for a single particular module I had to wrap. I ended up writing a second module ("module-wrapper") for which I added a path entry with the name of the original module ("module"). I then added a second entry ("module-actual") that references the actual module which I require() as a dependency in the wrapper.
I can then add code before and after initialization, and finally return the actual module. This is transparent to user modules as well as the actual module, and very clean and straightforward from a design standpoint.
However, it is obviously not practical to create a wrapper per module manually in your case, but you might be able to generate them dynamically with some trickery. Or somehow figure out what name was used to import the (unique) wrapper module from within it so that it can in turn dynamically import the associated actual module (with an async require, which wouldn't be transparent to user code).
Of course, it would be best if requirejs provided official hooks. I've never seen such hooks in the docs, but you might want to go through them again if you're not more certain than me.

Advantages & Disadvantages of Dependency-Injecting Non-Instantiable Objects

What, in your opinion, are the advantages and disadvantages of dependency-injecting non-instantiable objects in JavaScript?
Some context you may want to explore are:
Unit-testing
Is it worth dep-injecting it? After all, you can overwrite the non-instantiable "static" dependency to a fake object for each test even without dep-injection.
Refactoring
Will it become more difficult to locate & refactor the non-instantiable dependency?
Readability
Which implementation is easier to follow? Is the implicitness or explicitness important to you?
File-size
Code
Non-instantiable object:
WindowFactory = {
buildWindow: function() {
return {};
}
};
Dependency-Injected:
(House = function(windowFactory) {
this.windowFactory = windowFactory;
}).prototype = {
build: function() {
var window = this.windowFactory.buildWindow();
}
};
var house = new House(WindowFactory);
vs. The Non-Dependency-Injected variant:
(House = function() {
}).prototype = {
build: function() {
var window = WindowFactory.buildWindow();
}
};
var house = new House();
Context
My primary goal is to make the code above testable. I've gotten into a
habit of externalizing instantiable dependencies (e.g var window = new
Window(); var house = new House(window);). This helps when unit-
testing instantiable objects (e.g. House), since instead of the real
dependencies (Window) I can instantiate the object with a fake (var
fakeWindow = {}; var house = new House(fakeWindow);), and not have to
worry about redundantly testing the dependencies while testing my
object. (This form of dependency-injection is also useful when testing
objects that depend on some data being retrieved via XHR, DOM-events,
sessionStorage, or cookie.)
Now, when the dependency is an instantiable object itself, the
benefits are clear to me; but when the dependency is a non-
instantiable object (e.g. WindowFactory in the code above), I have
second thoughts about the usefulness.
If you get some gain in unit-testing, that might be more than enough for me. You'll be able to cleanly test the functionality without depending on global/external state. The added bonus then becomes the readability; you clearly show which global state/api you depend on in the arguments of your function.
Being able to change the static methods in the setup/teardown of the tests is a workaround for the problem, but I personally find it to be error-prone and a chore doing it.

Categories

Resources