Hey guys,
I’m having a little problem on extending the onRendered function of my ViewModel. For example, I’ve extended my model like this:
Template.videos_card.onCreated(function() {
this.vm = new ViewModel(CardViewModel(this.data));
this.vm.extend({
changeListener: function () {
//this is a new function, not defined in the original object
},
onRendered: function() {
//this method is also defined in the original object, I want to extend
it but it should still execute the onRendered of the original object
}
});
});
Now I want extend the “onRendered” method of the original one, meaning I want to execute the original onRendered function and also the extended onRendered. How could I do this?
This is convoluted way to go about things but let’s roll with it…
You can define multiple onRendered functions on the template. In your case you can move the new onRendered out of the view model.
Template.videos_card.onCreated(function() {
this.vm = new ViewModel(CardViewModel(this.data));
});
Template.videos_card.onRendered(function() {
// Do your stuff
} );
You can wrap the original onRendered function:
Template.videos_card.onCreated(function() {
this.vm = new ViewModel(CardViewModel(this.data));
var originalOnRendered = this.vm.onRendered;
this.vm.extend({
onRendered: function() {
originalOnRendered();
// Do your stuff
}
});
});
Thank you for the answer, but the second example is breaking the function, I’m just getting $ is not defined, this is the part of the original onRendered method:
onRendered: function () {
var instance = this.templateInstance;
instance.$(".card").css("opacity", 1);
instance.$(".card").addClass("animated slideInUp fadeIn");
}
if (this.isPicture()) {
///....
}
Seems like templateInstance isn’t defined anymore if I use the manual definition way like it is posted in the docs. If I comment it out, the next error is that this.isPicture() isn’t defined.
The error throws if I call originalRender() within the extended version.
this.vm = new ViewModel(CardViewModel(this.data));
var originalRendered = this.vm.onRendered;
//Edit: This one here is working - defined in the extended ViewModel:
Template.videos_card.onCreated(function() {
this.vm = new ViewModel(CardViewModel(this.data));
console.log(this.vm);
});
…the function isPicture() is there. Seems like im running again into a “this” issue, or? Because with the “original” function within the ViewModel I change’s originalRendered context and then your second way is also working.
CardViewModel = function (data) {
isPicture: function () {
if (data.pictureId) return true; else return false;
},
onRendered: function () {
var instance = Template.instance(); //normally, this.templateInstance works...
instance.$(".card").css("opacity", 1);
instance.$(".card").addClass("animated slideInUp fadeIn");
if (this.isPicture()) {
......
});
}
},
});
Then I add this model to my normal “card”
Template.card.viewmodel(CardViewModel);
I also have a “video_card” which should extend the original ViewModel:
Template.videos_card.onCreated(function() {
this.vm = new ViewModel(CardViewModel(this.data));
var originalRendered = this.vm.onRendered;
this.vm.extend({
onRendered: function() {
originalRendered(); //doesn't work
StatusBar.backgroundColorByHexString("#000000");
}
});
});
After that, I’m binding it to the Template:
Template.videos_card.onRendered( function() {
this.vm.extend(this.data); //I need it because of the with context, otherwise my document properties are not available
this.vm.bind(this);
this.vm.onRendered();
});
Hey,
thanks, that really helped me. I’m just remembering that I run also in that “this” issue on my transformation / helper classes. Think we’ve discussed it here ViewModel 2 - A new level of simplicity
Is this problem more complex? I’m just wondering how I can “force” the context of “this”, so that it doesn’t get “overwritten” by ViewModel. Sorry for that beginner questions, but I moved from PHP to JS and never had problems with data contexts
I don’t know if this is even a problem. ViewModel is designed so this refers to the current view model under normal situations. That solves a lot of context/scope issues. That said, it’s still JavaScript so you can run into problems with this but that’s not a ViewModel issue, it’s just the way JS works.
In your case you were calling a function (A) that used this from inside another function (B). In that case the context for A is the function B unless you bind it to a different context.
@manuel Now I’ve the case, that some properties of my MongoDB document could be added while the ViewModel has been already generated. If I call my single card again with
{{#with posting}}
{{>videos_card}}
{{/with}}
and I add the property isRecommended:true to the MongoDB posting object, this.isRecommended() isn’t defined in my ViewModel videos_card. It works when I refresh the page or I add the property direct into my Blaze template ({{isRecommended}}). Is there an option to recreate the ViewModel when the data context get’s new properties?
If I understand you correctly, you’re trying to use a property that isn’t there (on the Mongo document). If that’s the case you need to be explicit about it and define the property on the view model for videos_card. That way you can still access the property with the default value of your choosing if the document doesn’t have that property.
Yeah, for the moment it isn’t there, but it is possible that it will be available when a user clicks on “Recommended”. So in that case, the Mongo object get’s updated and now has the property “isRecommended”. So the ViewModel should also get the property when the Mongo object has changed. If I define a default value isRecommended:false in my ViewModel, will it be overwritten when I use {{#with posting}}?
Both of them have ViewModels. Now I get all the time errors like “Property ‘XXXX’ not found on view model”. The problem seems to be, that my template “commentItem” get’s all properties of the parent ViewModel and looses it’s own. So in my example, “commentText” isn’t available anymore, but “expandAll” for a commentItem.
The really annoying thing is, that it works with the account of User 1, but not when I’ve logged in with the account of User 2. I can see all comments with both accounts, but on User 2 all properties of my ViewModel are lost without any error message, except the “property missing” one.
When I remove the ViewModel of “Comments”, it works fine on both accounts.
//Edit: Okay, this behavior happens if we don’t check if our subscriptions already loaded. So this code fixed the issue:
Didn’t get exactly the same reproduction because my project uses also the package publish-composite to return multiple collections with one subscription, but in the repo above I get also an error like this. I’m having 20 entries in the collection “test”, all of them have a firstname and a lastname (checked via Mongo GUI).
This is what I get on my site:
It is John Doe Only for a test
It is John Doe Only for a test
It is John Doe Only for a test
It is John Doe Only for a test
It is John Doe Only for a test
[...]
I’m getting this error (only once per page reload):
binds.coffee:61 Property 'firstname' not found on view model:
binds.coffee:61 Property 'lastname' not found on view model:
binds.coffee:61 Property 'someText' not found on view model:
The error disappears if I change my “main” template to:
I’ll later extend this repo, because on my real project it doesn’t show the binding values, in the example above it works. The only difference on my real project between my 2 users is, that the first one is already subscribed to it’s own comments (during startup), while the second one subscribes when he opens the comments page - and there it is failing without subscriptionsReady.