Closed frankapimenta closed 8 years ago
Did you try the following:
onCreated(template) {
template.subscribe(contact, FlowRouter.getParam('_id'), () => {
let contact = Contacts.findOne();
this.load({contact: contact})
});
},
Thank you for replying that fast.
Yes, the callback in the subscribe! I don't know how I did not remember that.
By using your suggestion...
I can log this.contact() in the autorun() without errors.
If I try to log this.contact() in onRendered() and hasErrors() it throws error:
Exception from Tracker afterFlush function: debug.js:41 TypeError: this.contact is not a function
Exception in setTimeout callback: TypeError: this.contact is not a function at ViewModel.onRendered
And now I'm blocked 👎
Just an extra:
-> ahead (id comes from FlowRouter)
if I try do this.load({_id: id}) instead then I can do this._id() in place of this.contact(). So I think it blows up when the object is from Astronomy.
Another extra.
Considering I have a nested element Address in contact and I load it (as in your suggestion) as:
onCreated(template) { template.subscribe(contact, FlowRouter.getParam('_id'), () => { let contact = Contacts.findOne(); this.load({address: contact.raw().address}) });
it only throws viewmodel errors ( Not meteor errors) when I tried to use address().url in the template.
vm error: can't access url of undefined.
Should not the full vm instance be build before rendering the template in the browser? Is there a way to assure this.load is finished before other methods are defined in the vm instance?
Mhhh...let's do it this way:
Template.test.viewmodel({
doc: null,
onRendered(t) {
t.subscribe("yourSub",() => {
var contact = Contacts.findOne();
this.doc(contact);
});
},
autorun: [
function() {
if(this.doc()) alert('Data loaded');
}
]
});
Thank you for your help!
It works.
However when:
hasErrors() { return this.doc().getValidationErrors(); } sometimes this.doc() is null and it will fail again so I worked around by:
hasErrors() { if(!this.doc()) return new Contact(); return this.doc().getValidationErrors(); }
Would be nice if the viewmodel could have a subject (here the doc) so that the functions defined by the developer (no callbacks (autoruns and so)) would only be called in the view if subject was already loaded.
A bit like: Template.subscriptionsReady().
Is it a good idea?
Funny thing is happening.
Remember:
Template.editContact.viewmodel({
doc: null,
onRendered(t) {
const id = FlowRouter.getParam('_id');
t.subscribe("contact", id,() => {
var contact = Contacts.findOne();
this.load(_.pick(contact.raw(), 'value'));
this.doc(contact);
});
},
editContact() {
if(!this.doc()) {
return new Contact(); // to avoid to have the null issue
}
let contact = this.doc();
contact.set('value', this.value());
return contact;
},
getErrors() {
var contact = this.editContact();
contact.validate(false);
console.log(contact.getValidationErrors); // put this line in mind
return contact.getValidationErrors();
},
hasErrors() {
return !! this.getErrors();
},
getErrorValues() {
return _.values(this.getErrors());
}
});
<template name="editContact">
<form {{b "class: {error: hasErrors}"}}>
{{#each getErrorValues}}
<div class="error" {{b "text: this"}}></div>
{{/each}}
<input type=text {{b "value: value"}} >
</form>
After having this.doc() when rendering the template:
Moreover it runs every time the input field value of the form changes. So if the input on load show "street X" and one starts removing character by character the log runs 16 times for every character deleted.
Somehow the set of new value in this.doc()
contact.set('value', this.value());
triggers the computations and hasErrors() which will run getErrors() runs again but stops after 16 times (if validation does not fail);
When the validation fails with errors it runs forever.
Ideas? :)
Yeah, it is normally that the methods triggered if you change the value, this is how reactivity in Meteor works. Try this code:
Template.editContact.viewmodel({
doc: null,
value:null,
onRendered(t) {
const id = FlowRouter.getParam('_id');
t.subscribe("contact", id,() => {
var contact = Contacts.findOne();
this.doc(contact);
this.value(_.pick(this.doc().raw(), 'value'));
});
},
editContact() {
if(!this.doc()) {
this.doc(new Contact());
}
this.doc().set('value', this.value());
return this.doc();
},
getErrors() {
if(this.doc().validate()) {
return {}; //Empty object, no errors...
} else {
return this.doc().getValidationErrors();
}
},
hasErrors() {
return Object.keys(this.getErrors()).length > 0;
},
getErrorValues() {
return _.values(this.getErrors());
}
});
Thank you for the code. Yes that is how meteor works.
In your code you are not calling editContact anywhere so this.doc() is not going to ever change. I do this:
getErrors() {
if(this. editContact().validate()) {
return {}; //Empty object, no errors...
} else {
return this. editContact().getValidationErrors();
}
and I have the same problem I explained before.
What I did was to get rid of the doc field solution.
Template.editContact.viewmodel({
value:null,
onRendered(t) {
const id = FlowRouter.getParam('_id');
t.subscribe("contact", id,() => {
// wait for this.ready();
});
},
autorun() { // LOOK HERE
// I wanted for this.ready() in subscribe so contact is present for sure.
var contact = Contacts.findOne(); // now I have reactivity
this.value(_.pick(contact.raw(), 'value'));
},
editContact() {
var contact = Contacts.findOne()
if(!contact) {
return new Contact();
}
contact.set('value', this.value());
return contact;
},
getErrors() {
var contact = this.editContact();
if(contact.validate()) {
return {}; //Empty object, no errors...
}
return contact.getValidationErrors();
},
hasErrors() {
return Object.keys(this.getErrors()).length > 0;
},
getErrorValues() {
return _.values(this.getErrors());
}
});
Now reactivity works everytime I change a value via meteor mongo.
Everything now works fine with validations.
If you have any further questions do not hesitate to ask me.
Closing this one.
Hi Manuel,
first and foremost. Great work here. Thank you for making it available.
When looking at your contacts app code you have this:
..... if (this._id()) { this.load( Contacts.findOne(this._id()) ); } else { this.categoryId( this.selectedCategory() ); } ....
this works fine.
... but this (using Astronomy for Contacts):
Template.contact.viewmodel({ onCreated(template) { template.subscribe(contact, FlowRouter.getParam('_id')); }, onRendered(template) { let contact = Contacts.findOne(); this.load({contact: contact}) }, hasErrors() { return this.contact().getValidationErrors(); // blows up here with this.contact() undefined. } });
Do you have an idea how to make this work? I know we should wait for object subscription but in your documentation I could not find a way.