Adding properties to all Knockout models - javascript

I have several knockout models, that I use on my website. Let's say for a given model I have a property
function modelA {
this.doSomething = function () { ... };
}
Now i want to use the doSomething() function in a different model. I would like to do this, such that I am not obligated to rewrite doSomething() in every single model that needs it. Ideally i would like it if this function becomes available as soon as ko.applyBindings(new modelX()) is called.
I know that i can have the functions inherit from a prototype, but that also leads repeated code in every single model. Is there a way to alter knockout.js to inject this property into all model objects?
EDIT:
In case there's a different approach that would achieve my desired goal, i'll elaborate on that desired goal. We use knockout-validation.js which allows me to call isValid() on every property in a model that has been extended with validation rules. I want to append a function to every model that will inspect the other properties in the model, and call isValid() on them. That way i can write a generic validation function that can be used with every model, without having to explicitly add it to the model.

You can add a custom function onto knockout's core datatypes. Alternatively you could look at extenders, and inside the extension you could add the function to an observable. Note, however, that both of these techniques address cross-cutting concerns at the "observable" level, not at the ViewModel level.
I would be curious to understand what doSomething is doing. Assuming that it has to access some observable(s) inside the ViewModel you should be able to reframe the problem so that the behaviour is attached to an observable.

Related

Is it possible put two ko.applyBindings() in the same function?

I've to put two ko.applyBindings(); in the same function but the system turn me back a run-time error.
Is it possible to do that?
Usually, you create a viewModel object and then call ko.applyBindings(viewModel) once, like this:
var viewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable(123)
};
ko.applyBindings(myViewModel);
Maybe this is what you're looking?
Optionally, you can pass a second parameter to define which part of the document you want to search for data-bind attributes. For example, ko.applyBindings(myViewModel, document.getElementById('someElementId')). This restricts the activation to the element with ID someElementId and its descendants, which is useful if you want to have multiple view models and associate each with a different region of the page.
From the documentation here.
As for your comment about managing a list and an array, I'm not sure what you mean. They sound like the same thing to me. Knockout has observable arrays that you can use.

Howto: generic test to see if widgets call this.inherited succesfully?

I maintain a custom library consisting of many dijit widgets at the company I work at.
Many of the defects/bugs I have had to deal with were the result of this.inherited(arguments) calls missing from overriden methods such as destroy startup and postCreate.
Some of these go unnoticed easily and are not always discovered until much later.
I suspect I can use dojo\aspect.after to hook onto the 'base' implementation, but I am not sure how to acquire a handle to the _widgetBase method itself.
Merely using .after on the method of my own widget would be pointless, since that wouldn't check whether this.inherited(..) was inded called.
How can I write a generic test function that can be passed any dijit/_WidgetBase instance and checks whether the _widgetBase's methods mentioned above are called from the widget when the same method is called on the subclassing widget itself?
Bottom-line is how do I acquire a reference to the base-implementation of the functions mentioned above?
After reading through dojo's documentation, declare.js code, debugging, googling, debugging and hacking I end up with this piece of code to acquire a handle to a base method of the last inherited class/mix-in, but I am not entirely happy with the hackiness involved in calling getInherited:
Edit 2 I substituted the second param of getInherited with an empty array. While I actually get a reference to the method of the baseclass using aspect doesn't work. It appears this approach is a bust.
require(['dijit/registry','dojo/_base/declare','mycompany/widgets/widgetToTest'],
function(registry,declare,widgetToTest)
{
var widget = registry.byId('widgetToTestId');
var baseStartup = getBaseMethod(widget,'startup');
function getBaseMethod(widget,methodName){
return widget.getInherited(methodName,[]);
}
//This is the method body I want to use .after on to see if it was called, it returns the last overriden class in the array of inherited classes. (a mixin in this case, good enough for me!)
alert(baseStartup);
});
I have given up trying to use dojo/aspect.
I have instead opted to modify the code of our custom base widget to incorporate snippets such as the one below. They are automatically removed when creating a release-build in which console-calls and their content are removed:
console.log(
function(){
(this._debugInfo = this._debugInfo|| {}).postCreate=true;
}.call(this)
);
A simple method in boilerplate code I added near the unittests is available so that I can call it on all mycompany.widgets.basewidget instances in their respective unittests.

React component with dynamically created function

I have a React component that includes an input element. When the value of the input changes the handler function is supposed to validate the input and act accordingly. The actual validator is dynamically created (via new Function(code)) with the actual code passed as a string prop to the element. The validation code never changes for a given component.
Right now I am doing the function construction in the actual onChange handler which seems unnecessary. It does not belong in state either. I would like to create the function once, store it somewhere and use it on demand. My question is, where should it be stored? Should I make it an attribute of the actual component object? The statics object seems also reasonable, but can one pass properties dynamically (like the code string above) and if yes, how?
Recomputing the validator inside the onchange is not that bad from a reproducibility point of view. It only affects performance and if that is an issue one possibility would be to use the caching mechanism of your choice:
handleOnChange: function(){
if(this.cachedValidatorString !== this.props.validatorString){
this.cachedValidatorString = this.props.validatorString;
this.cachedValidator = new Function(...);
}
// ...
}
Another, perhaps cleaner, approach would be to update the validatorFunction field inside the render method. That is the earliest you can update it and it guarantees that it will always correspond to your current props.
render: function(){
this.validatorFunction = new Function(this.props.validatorString);
return <input onChange={...} />;
}
As for the other possibilities you mentioned:
I agree that this.state is not the best place to put the validator function because in general you want to avoid putting things that can be computed from the props in the state. See props in getInitialState as an anti pattern
Finally, I don't think statics would make much sense here. Static properties are shared by all instances of your class but the validator functions need to be different for each instance of the component.

Am I overusing the Knockout mapping plugin by always using it to do my viewmodel?

I'm still learning the proper usage of Knockout and I've found myself quickly getting away from ever typing ko.observable when setting up my viewmodel and instead just defining an object literal and passing it through the mapping plugin with something like
var viewModel = ko.mapping.fromJS(data);
or at the very least, something along the lines of stuffing all of my data into an attribute on the viewModel like so
var viewModel = {
... events etc ... ,
"data": ko.mapping.fromJS(data)
}
To be honest, the main reason I've been doing this is to get around having to type ko.observable and ko.observableArray repetitively. I'm just trying to figure out if this is a good approach and if there are any downsides to dropping the specific var x = ko.observable() declaration all together. Also, I'm doing this all on load, not in response to any ajax call etc, which from what I can tell, is what the mapping plugin was designed for.
In your work with knockout, do you still declare the observables manually, one by one, or have you gone with the mapping.fromJS method that I use? Are there any specific downsides to using the mapping plugin so frequently like this?
Edit:
Specific Example
In this article, Steve sets up his viewModel by doing
var initialData = [ { ... } , { ... } ]; // json from the serializer
var viewModel = {
gifts : ko.observableArray(initialData)
};
Normally, I'd just use ko.mapping.fromJS for this situation as well, specifically to make sure the objects within the array are turned into observables as well. Looking at what he did, my approach seems like its overkill and adds a bit of unnecessary overhead.
After using Knockout for a little longer, I've noticed that the mapping plugin has some additional options that give you much more fine grained control over the mapping process.
Control type and amount of properties generated
There are several ways to accomplish this, and I'll go over some, but the end result is that you end up with a lighter result from the mapping plugin because everything isn't observable.
Basically you leave everything that you don't think will change, as a normal property and only make observables out of the specific items that you want to observe.
Make mapping omit certain properties
You can make the mapping plugin omit properties entirely from the end result by specifying things like ignore or include. Both of these accomplish the same thing, just in opposite ways.
Note: Samples are from the knockout.js mapping plugin documentation, comments added by me
Mapping Plugin Argument: include
The following snippet will omit all properties from the source object other than those passed in via the include argument.
// specify the specific properties to include as observables in the end result
var mapping = {
// only include these two properties
'include': ["propertyToInclude", "alsoIncludeThis"]
}
// viewModel will now only contain the two properties listed above,
// and they will be observable
var viewModel = ko.mapping.fromJS(data, mapping);
Mapping Plugin Argument: ignore
If you want to only omit certain properties from the source object, use the ignore argument as shown below. It will make observables from all properties in the source object except for the specified properties.
// specify the specific properties to omit from the result,
// all others will be made observable
var mapping = {
// only ignore these two properties
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
// viewModel will now omit the two properties listed above,
// everything else will be included and they will be an observable
var viewModel = ko.mapping.fromJS(data, mapping);
Control what properties are or are not made observable
If you need to include properties but you don't think that they will need to be made observable (for whatever reason), the mapping plugin has something that can help.
Mapping Plugin Argument: copy
If you want the mapping plugin to simply copy the plain properties and not make them observable, use this argument, as shown below.
// tell the mapping plugin to handle all other properties normally,
// but to simply copy this property instead of making it observable
var mapping = {
'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);
Gain complete control over the mapping process
If you want to have 100% control over what is created in the mapping process, including the ability to put closures and subscriptions in your objects, then you want to use the "create" option.
plain result with calculated properties
Here is an example where I was mapping data from an ajax call to an object with a results property. I didn't want anything observable and I just wanted a simple generated property that would be made of the other simple properties on the object. Maybe not the most compelling example but it demonstrates the functionality.
var searchMappingConfig = {
// specific configuration for mapping the results property
"results": {
// specific function to use to create the items in the results array
"create": function (options) {
// return a new function so we can have the proper scope/value for "this", below
return new function () {
// instead of mapping like we normally would: ko.mapping.fromJS(options.data, {}, this);
// map via extend, this will just copy the properties from the returned json element to "this"
// we'll do this for a more light weight vm since every last property will just be a plain old property instead of observable
$.extend(this, options.data);
// all this to add a vehicle title to each item
this.vehicleTitle = this.Year + "<br />" + this.Make + " " + this.Model;
}, this);
};
}
}
}
subscriptions and closures and mapping, oh my
Another situation is if you want closures and subscriptions in your result. This example is too long to be included in its entirety but its for a vehicle make/model hierarchy. I wanted all the models (children) for a given make (parent) to be un-enabled if the model was un-enabled and I wanted this to be done with a subscription.
// here we are specifying the way that items in the make array are created,
// since makes has a child array (Models), we will specify the way that
// items are created for that as well
var makesModelsMappingConfig = {
// function that has the configuration for creating makes
"create": function (options) {
// return a new function so we can have the proper
// scope/value for "this", below
return new function () {
// Note: we have a parent / child relationship here, makes have models. In the
// UI we are selecting makes and then using that to allow the user to select
// models. Because of this, there is going to be some special logic in here
// so that all the child models under a given make, will automatically
// unselect if the user unselects the parent make.
// make the selected property a private variable so it can be closure'd over
var makeIsSelected = ko.protectedComputed(false);
// expose our property so we can bind in the UI
this.isSelected = makeIsSelected;
// ... misc other properties and events ...
// now that we've described/configured how to create the makes,
// describe/configure how to create the models under the makes
ko.mapping.fromJS(options.data, {
// specific configuration for the "Models" property
"Models": {
// function that has the configuration for creating items
// under the Models property
"create": function (model) {
// we'll create the isSelected as a local variable so
// that we can flip it in the subscription below,
// otherwise we wouldnt have access to flip it
var isSelected = ko.protectedComputed(false);
// subscribe to the parents "IsSelected" property so
// the models can select/unselect themselves
parentIsSelected.current.subscribe(function (value) {
// set the protected computed to the same
// value as its parent, note that this
// is just protected, not the actual value
isSelected(value);
});
// this object literal is what makes up each item
// in the Models observable array
return {
// here we're returning our local variable so
// we can easily modify it in our subscription
"isSelected": isSelected,
// ... misc properties to expose
// under the item in the Model array ...
};
}
}
}, this);
};
}
};
All in all, what I've found is that you rarely need 100% of an object that you'd pass to the plugin and you rarely need 100% of it to be observable. Dig in with the mapping configuration options and create all sorts of complex and simple objects. The idea is to only get everything you need, nothing more or less.
My suggestion to you would the same another questioned I just answered at https://stackoverflow.com/questions/7499133/mapping-deeply-hierarchical-objects-to-custom-classes-using-knockout-mapping-plug.
Your reasoning for using mapping plug-in is reasonable and the one that I use. Why type more code than you have to?
In my experience with knockout (all of 4 months), I've found that the less I do manually and let the knockout routines do their thing, the better my apps seem to run. My suggestion is try the simplest approach first. If it doesn't meet your needs, look at how the simple approach is doing it's "thing" and determine what has to change to meet your needs.
Allen, my recent learning experience with Knockout.js has been similar to yours. We work with a deep hierarchical object graph from the server and I have defined explicit instantiable view model functions which preserve the basic structure of it.
I began by defining each property explicitly as an observable on the relevant view model, but that quickly got out of hand. Also, a major reason for switching to using the mapping plugin was that we have to do frequent Ajax posts of the graph back to the server where it is merged with the persisted version, then validated on the server in such a way that numerous properties can change and collections be modified, and a new instance returned as the Ajax result where it has to be re-merged with the client representation. That became seriously difficult, and the mapping plugin helped big time by allowing the specification of identifiers for resolving adds / deletes / updates and to remap an updated graph onto the original.
It also helped in the original graph creation through the use of the "create" option for sub view models. In each view model constructor I receive a reference to the parent view model plus the data with which to construct the child view model, then create further mapping options to create grandchildren from the passed-in child data.
The only (slight) downside I recently found, as detailed in this question, is that when doing ko.mapping.toJSON it doesn't hook into any toJSON overrides you may have defined on the prototypes of your view models in order to exclude properties from serialization. I have been able to get around that by specifying ignore options in the unmapping, as recommended by Ryan Niemeyer in that post.
So in summary, I'll definitely be sticking with the mapping plugin. Knockout.js rules.
A simpler but help-full add-on could be knockout-data-projections
Currently, it does not handle js to viewmodel mappings, but it handles quite well view model to JS mappings.

Wrapping a DOM element inside a JavaScript object

I've noticed a common pattern in the JavaScript I've been writing and was wondering if there is already a pattern out there that defines something similar as best practice? Essentially, it's how to get a DOM element and wrap it inside / associate it with a JavaScript object. Take this example, where you need a filter in your web app. Your page looks like this:
<html>
<head></head>
<body>
<div id="filter"></div>
</body>
</html>
You'd then wrap the element like so:
var myFilter = new Filter({
elem: document.getElementById('filter'),
prop: 'stacks-test',
someCallback: function() {
// specify a callback
}
});
And the JavaScript (where spec is an object passed to the constructor):
var Filter = function(spec) {
this.elem = spec.elem;
this.prop = spec.prop;
this.callback = spec.someCallback;
this.bindEvents();
};
Filter.prototype.bindEvents = function() {
var self = this;
$(this.elem).click(function(e) {
self.updateFeed();
};
};
Filter.prototype.updateFeed = function() {
this.prop; // 'stacks-test'
this.callback();
// ...
// code to interact with other JavaScript objects
// who in turn, update the document
};
What is this kind of approach called, and what are the best practices and caveats?
You might be interested in Dojo's widget library, Dijit - if I'm understanding your question correctly, it essentially does what you're asking, and a whole lot more.
In Dijit, a widget essentially encapsulates a DOM node, its contents, any JavaScript that defines its behavior, and (imported separately) CSS to style its appearance.
Widgets have their own lifecycle, registry, and events (including many which simply map to DOM events on a node within the widget, e.g. myWidget.onClick could effectively call myWidget.domNode.onclick).
Widgets can (but don't have to) have their initial contents defined in a separate HTML template file, through which it's also possible to bind events on nodes within the template to widget methods, as well as set properties on the widget that reference particular nodes in the template.
I'm barely scratching the surface here. If you want to read more on this, you can start with these reference pages:
http://dojotoolkit.org/reference-guide/dijit/info.html
http://dojotoolkit.org/reference-guide/dijit/_Widget.html (the base that all widgets extend)
http://dojotoolkit.org/reference-guide/dijit/_Templated.html (RE the HTML templating)
http://dojotoolkit.org/reference-guide/quickstart/writingWidgets.html (useful information when starting to write your own widgets)
http://dojotoolkit.org/reference-guide/dijit/ (for a bunch more info)
All said, I don't know what you're ultimately aiming for, and maybe this is all a bit much for your purposes (considering I'm throwing an entire other library at you), but figured it might pique your interest at least.
Continuing from my comment on the question, jQuery is a potential tool for the job, as it already provides some of the foundations for what you're after. However, having said that, it does introduce complexities of its own, and further, not all "jQuery ways" are equal. I'll suggest one way of using jQuery as your "object model", but it may or may not suit your needs.
First things first. The philosophy of jQuery is that you start everything by selecting the element first, using $(), or equivalently jQuery(). All operations conceptually begin with this. This is a slightly different way of thinking compared to creating an object that wraps an element and keeping a reference to that wrapper, but essentially this is what jQuery does for you. A call to $('#some-id') grabs the element with id of "some-id" and wraps it in a jQuery object.
One way: Write "Filter" plugins.
Replace your constructor with a initFilter() jQuery method. You can do this by modifying the jQuery prototype and using the jQuery object as your wrapper. jQuery's prototype is referenced by jQuery.fn, so:
jQuery.fn.initFilter = function (prop, callback) {
// Save prop and callback
this.data('filter-prop', prop);
this.data('filter-callback', callback);
// Bind events (makes sense to do this during init)
this.click(function () {
$(this).updateFeed();
});
};
Then do a similar thing for updateFeed():
jQuery.fn.updateFeed = function () {
this.data('filter-prop');
this.data('filter-callback')();
});
And use it like this:
$('#filter').initFilter(prop, callback);
Note that updateFeed can simply be in-lined into the click handler to prevent unnecessary pollution of the jQuery namespace. However, one advantage of using jQuery like this is that you do not need to keep a reference to the object if you need to invoke some function on it later, since jQuery ties all references to actual elements. If you'd like to call updateFeed programmatically, then:
$('#filter').updateFeed();
will then be invoked on the correct object.
Some things to consider
There are certainly downsides to this method. One is that all properties, which we've saved against the element using .data(), are shared between all jQuery functions that act on that element. I've attempted to alleviate this by prefixing the property names with "filter-", but depending on the complexity of your object(s), this may not be suitable.
Further, this exact method may not be so suitable for objects that require a lot of manipulation (i.e. objects with many functions) since all of these functions become common to all jQuery objects. There are ways to encapsulate all this which I won't go into here, but jQuery-ui does this with their widgets, and I'm experimenting with yet another alternative in a library I'm creating.
However, pulling back a bit, the only reason I suggested using jQuery in the first place is that your Filter object appears to be heavily tied to the DOM. It binds events to the DOM, it modifies the DOM based on user interaction, basically it appears to live in the DOM, so use something DOM-based, i.e. jQuery.

Categories