I've looked at this:
http://knockoutjs.com/documentation/extenders.html
The issue is that I'm using fromJs to create my view model, so my observerables already exist. I would think I could do the following to add an extender:
var data = result.Data;
if (!window.vmRealTimeActivity) {
window.vmRealTimeActivity = ko.mapping.fromJS(data, mappingKeys);
ko.applyBindings(vmRealTimeActivity, $('#second-btm')[0]);
} else {
ko.mapping.fromJS(data, vmRealTimeActivity);
}
vmRealTimeActivity.MyExistingObservable.extend({ numeric: null });
vmRealTimeActivity.MyExistingObservable(9999); // doesn't call numeric extender
My extender gets called the first time the extender is attached, but not after trying to change the value.
I read another SO post that stated that .extend() creates a new observerable so you have to do this, but this doesn't work either:
vmRealTimeActivity.MyExistingObservable = vmRealTimeActivity.MyExistingObservable.extend({ numeric: null });
In addition to not calling my formatter a second time, the value starts coming back NaN.
How do I attach an extender the proper way to an existing observable?
Since you are using the mapping plugin, you could specify a create callback. If you add the following to the existing mappingKeys, it would probably work (I don't know your exact mapping, so you might need to change bits here and there):
'MyExistingObservable': {
create: function(options) {
return new ko.observable(options.data).extend({ numeric: null });
}
}
This result in an extended observable upon mapping from yor data.
Here's a jsFiddle with a working example (vm1) and your current non-working example (vm2) for comparison
The above answer is correct, but for anyone interested, I found the simpler approach is to just create your view models client side and use fromJs to refresh them rather than both create and refresh them. You can then apply the answer here to support adding extend to both your parent and child view models: Map JSON data to Knockout observableArray with specific view model type
With either approach you will have to create additional mappings.
Related
What is the correct approach when working with an "new object" that is to be saved in a collection. Say I have a collection Cars. I have a /cars/new-car
url and then a form with:
name: __
parts: list of parts here
If I want to make this form "reactive" in the sense that if I add a new part in the parts array it shows a rerender is the best approach to make the whole "Car" a reactive object. Or should one just add a new row in the dom?
I dont want to automatically insert the whole thing into the "Cars" collection until It has a name and a list of parts.
Most examples shows very simple of adding to collection -> rerender of DOM which is very straightforward.
Edit: The same concept may apply to when editing a car. Fetching the car from a collection, setting up so the returned object is reactive(so I can add/remove parts) when done get all values and store the edited car information.
Start out by initializing an "empty" car as a reactive variable.
Template.cars.onCreated(function () {
this.car = new ReactiveVar({}); // empty car
});
Say your dom has some sort of attribute on each field describing which key it is:
<input data-key="name" placeholder="Car name"/>
Then you can bind an event that will use the data from this to update the reactive variable.
Template.cars.events({
'change input': function (e, template) {
template.car.set(_.extend(template.car.get(), {
[$(e.target).data('key')]: $(e.target).val()
}));
}
});
This will construct the object as you fill in your inputs.
Consider using Session for your /cars/new-car page
When the page first loads
Session.set('parts', []});
Session.set('name', '');
When the user saves a part
var addedPart = getPart();
var update = Session.get('parts').push(addedPart);
Session.set('parts', update);
Then your template helper functions can get everything it needs to render the view by calling Session.get().
Template.view.helpers({
currentParts: function() {
return Session.get('parts');
}
});
What do you think? I'm fairly new to Meteor myself, so there maybe even more clever ways to do batch updates on the session. But this is general gist.
I have some code that looks like:
var instance = new ModelA(element);
if(instance.isValid()){
CollectionA.add(instance);
}
Is there a better way to write this? Would prefer that either initializing ModelA or adding to CollectionA would fail or throw.
You have to override the constructor.
See here for more info.
Backbone has this baked right in. There is a validate method available in your model code which will get called before any save (You can also do it for set by passing {validate:true}
Here's a snippet from the backbone docs:
var Chapter = Backbone.Model.extend({
validate: function(attrs, options) {
if (attrs.end < attrs.start) {
return "can't end before it starts";
}
}
});
If validate returns anything, then the Backbone SAVE won't happen, but if it goes through your validate function cleanly without any returns, then it will go ahead with the save.
I've inherited a project which uses Knockout.JS to render a listing of posts. The client has asked that this listing be paginated and I'm wondering if this is possible and appropriate using Knockout.JS. I could easily achieve this in pure JavaScript but I'd like to use Knockout (if appropriate) for consistency.
From what I can tell, the page uses a Native Template in the HTML of the page. There is a ViweModel which stores the posts in a ko.ObservableArray() and a post model.
The data is loaded via a jQuery ajax call where the returned JSON is mapped to post model objects and then passed into the ObservableArray which takes care of the databinding.
Is it possible to amend the ViewModel to bind pagination links (including "previous" and "next" links when required) or would I be better off writing this in plain JS?
It should be easy enough to build a computed observable in knockout that shows a "window" of the full pagelist. For example add to the view model:
this.pageIndex = ko.observable(1);
this.pagedList = ko.computed(function() {
var startIndex = (this.pageIndex()-1) * PAGE_SIZE;
var endIndex = startIndex + PAGE_SIZE;
return this.fullList().slice(startIndex, endIndex);
}, this);
Then bind the "foreach" binding showing the record to pagedList instead of the full list, and in the forward and back links, simply change the value of pageIndex. Starting from there, you should be able to make it more robust/provide more functionality.
Also, this assumes you preload all data to the client anyway. It's also possible to make JSON calls on the previous and next link and update the model with the returned items. The "next" function (to be added to the view model prototype), could look like this:
ViewModel.prototype.next = function() {
var self = this;
this.pageIndex(this.pageIndex()+1);
$.ajax("dataurl/page/" + this.pageIndex(), {
success: function(data) {
self.dataList(data);
}
});
}
(using jQuery syntax for the ajax call for brevity, but any method is fine)
Writing features in KO always tend to generate less code and cleaner code than doing the same in "plain JS", jQuery or similar. So go for it!
I implemented a combobox with paging like this
https://github.com/AndersMalmgren/Knockout.Combobox/blob/master/src/knockout.combobox.js#L229
In my blog post, I have explained in very detail how to do it. you can find it (here. http://contractnamespace.blogspot.com/2014/02/pagination-with-knockout-jquery.html). It's very easy to implement and you can do it with a simple JQuery plugin.
Basically, I have used normal knockout data binding with AJAX and after data has been retrieved from the server, I call the plugin. You can find the plugin here. its called Simple Pagination.
I have a big problem with using Knockout JS. In my view model I have a field, called Method, that is actually an other view model.
This view model can be one of three different things (it is mapped to a polymorphic object in the domain model). To solve this I use templates that checks which type of Method that is selected withing the domain model and then shows the template that binds data for that type.
The function that checks the type of method looks like:
this.getTemplate = function (data) {
var method = data.original.get_Method();
if (method instanceof MyProj.MethodA)
return "methodA";
else if (method instanceof MyProj.MethodB)
return "methodB";
else if (method instanceof MyProj.MethodC)
return "methodC";
}
The markup where I bind the template looks like:
<div data-bind="template: {name: getTemplate($data), data: $data.Method}"></div>
This actually works very nice and when I change the type of method via an dropdown in the UI the domain model updates and the right template is shown. However here comes my problem. Each template contains a number of different fields that are specific for each method type. Whenever I change one of the values in the view model displayed by one of the templates the UI flashes and I think that happens because the template get selected again. This is quite irritating and looks extremly bad.
Any ideas on how to solve this problem? Any help would be greatly appreciated!
Thanks in advance
/Björn
Did you use any observable inside the getTemplate function. Updating the value of that observable makes the template rerender and you get your flash effect.
Checkout this link Part : "Note 5: Dynamically choosing which template is used".
I've got 2 fields in my model that have a master/slave type relationship.
If the master updates the slave should take the update too.
If the slave updates the master is unaffected.
I've managed to implement this with a manual subscription - http://jsfiddle.net/ProggerPete/XNUPj/
But I'm wondering if I could achieve the same result without the manual binding. The reason I'm wanting it is I'd prefer not to have to unbind my manual subscriptions when i'm destroying my view.
Cheers,
Peter
Generally, I would say that the manual subscription is the most straightforward approach to your question.
However, it is pretty easy to create your own custom observable that encapsulates this functionality and handles updating both the master and slave in a writeable dependentObservable. It might look something like this:
function customObservable(initialValue) {
var _source = ko.observable(initialValue),
_local = ko.observable(initialValue),
result = ko.dependentObservable({
read: _source,
write: function(newValue) {
_source(newValue);
_local(newValue);
}
});
result.local = _local;
return result;
}
and you would use it like:
var viewModel = {
source: customObservable("sourceValue")
};
The customObservable (call it whatever you want) returns a writeable dependentObservable that will update both values that you can bind against as source. The local value is also exposed as source.local.
So, you would use this in your scenario like: http://jsfiddle.net/rniemeyer/67pDS/
I am not sure how you want to use this functionality though. If you are looking for the ability to accept/cancel edits to an observable, then you might want to look at this custom observable.
Snippet to show disposal in custom binding:
var subscription = oComboBoxModel.value.subscribe(updateBestMatchFromValue, oComboBoxModel);
//handle disposal (if ko.cleanNode is called on the element)
ko.utils.domNodeDisposal.addDisposeCallback(element, function(){
subscription.dispose();
});