Accessing subfields in template - meteor

I'm using autoform with simple schema and collection2 and I've created a schema with subfields. I'm having trouble accessing the subfields in my template. I seem to just get [object object]. The subfields are arrays. Can someone tell me what I'm missing.
Path: template.html
{{#with currentUser}}
{{#with profile}}
{{#each CV}}
{{languages}}
{{/each}}
{{/with}}
{{/with}}
Path: schema.js
Schema.Language = new SimpleSchema({
language: {
type: String,
optional: true
},
proficiency: {
type: String,
optional: true
}
});
Schema.CV = new SimpleSchema({
languages: {
type: [Schema.Language],
optional: true
}
});
Schema.UserProfile = new SimpleSchema({
CV: {
type: Schema.CV,
optional: true,
},
});
Schema.User = new SimpleSchema({
profile: {
type: Schema.UserProfile,
optional: true
}
});

Schema.Language has several properties, which means it's an object. Try this:
{{#with currentUser}}
{{#with profile}}
{{#each CV}}
{{#each languages}}
{{language}}
{{/each}}
{{/each}}
{{/with}}
{{/with}}
You also could replace the #each CV with a #with operator, as CV is not an array in your schema.

Related

Get all current values in a collection when defining its simples chema

So I have a semantic type collection here to define my type. It has a field arrayValue. The functionality is the new semantic type here can be an array of existing semantic types, either predefined or defined by the user. The problem is that all the semantic types are stored in the collection itself. So is it possible to get the values in collection (something like SemanticTypes.findOne().semanticType) when defined the schema? Here's the code:
collection:
SemanticTypes = new Meteor.Collection('semanticTypes');
Schemas.SemanticTypes = new SimpleSchema({
semanticType: {
type: String,
regEx: /^[a-zA-Z_][a-zA-Z0-9_]*$/,
unique: true
},
baseType:{
type: String,
autoform: {
options: [
{label: "int", value: "int"},
{label: "float", value: "float"},
{label: "string", value: "string"},
{label: "bool", value: "bool"},
{label: "array", value: "array"}
]
}
},
arrayValue: {
type: String,
optional: true,
autoform: {
options:
// I want to get all current semantic types in the collection now
}
}
});
SemanticTypes.attachSchema(Schemas.SemanticTypes);
html:
<template name="addSemanticTypeForm">
{{#autoForm collection="SemanticTypes" id="insertSemanticTypeForm" type="insert"}}
<fieldset>
{{> afQuickField name='semanticType'}}
{{> afQuickField name='baseType' }}
{{#if isArraySelected}}
{{> afQuickField name='arrayValue'}}
{{/if}}
</fieldset>
<button type="submit" class="btn btn-primary">Add</button>
{{/autoForm}}
</template>
js:
Template.addSemanticTypeForm.onCreated(function() {
Session.set("isArraySelected", false);
});
Template.addSemanticTypeForm.helpers({
isArraySelected: function() {
return Session.get("isArraySelected");
}
});
Template.addSemanticTypeForm.events({
'change select[name=baseType]': function(evt) {
if ($(evt.currentTarget).val() == "array"){
Session.set("isArraySelected", true);
}
else {
Session.set("isArraySelected", false);
}
}
});
I actually found a way to do it by defining an options variable in the helper to get all values in the collection with collection.find(), and use {{> afQuickField name="arrayType" options=arrayOptions}} in html. Here's my code:
js:
Template.addSemanticTypeForm.onCreated(function() {
Session.set("isArraySelected", false);
});
Template.addSemanticTypeForm.helpers({
isArraySelected: function() {
return Session.get("isArraySelected");
},
arrayOptions: function(){
var options = [];
SemanticTypes.find().forEach(function(type){
options.push({label: type.semanticType, value: type.semanticType});
});
return options;
}
});
Template.addSemanticTypeForm.events({
'change select[name=baseType]': function(evt) {
if ($(evt.currentTarget).val() == "array"){
Session.set("isArraySelected", true);
}
else {
Session.set("isArraySelected", false);
}
}
});
html:
<template name="addSemanticTypeForm">
{{#autoForm collection="SemanticTypes" id="insertSemanticTypeForm" type="insert"}}
<fieldset>
{{> afQuickField name='semanticType'}}
{{> afQuickField name='baseType' }}
{{#if isArraySelected}}
{{> afQuickField name='arrayValue' options=arrayOptions}}
{{/if}}
</fieldset>
<button type="submit" class="btn btn-primary">Add</button>
{{/autoForm}}
</template>

Returning incorrect data

The autoform is only recording one accept/reject which it applies to all jobOffers. Doesn't doc=this refer to the individual jobOffer when inside an {{#each}} statement.
Path: Schema.js
Schemas.Offers = new SimpleSchema({
offer: {
type: String,
optional: true,
allowedValues: ['Accept', 'Reject'],
autoform: {
type: "select-radio",
options: function () {
return [
{label: "Accept", value: 'Accept' },
{label: "Reject", value: 'Reject' },
];
}
}
}
});
Path: template.html
{{#each jobOffers}}
{{#autoForm collection="Offers" id="offerForm" doc=this type="update" autosave=true}}
{{> afQuickField name="offer" type="select-radio" template="buttonGroup" label=false}}
{{/autoForm}}
{{/each}}
All forms in your loop uses the same id='offerForm' which is what is used to determine the destination.
Fix this by adding a dynamic id to your Autoform.
If jobOffers is a cursor from Mongo, it will have a unique _id you can use to prefix/postfix on your form id with something like id='offerForm{{_id}}'
The solution was to create a helper.
Path: helper.js
Template.Offer.helpers({
jobOffers: function () {
return JobOffers.find({candidateUserId: Meteor.userId()});
},
makeUniqueID: function () {
return this._id;
}
});
Path: template.html
{{#each jobOffers}}
{{#autoForm collection="JobOffers" id=makeUniqueID doc=this type="update"}}
{{> afQuickField name='offer'}}
<button type="submit" class="btn btn-primary submit">Update</button>
{{/autoForm}}
{{/each}}

Autoform users profile page

I try create user profile page with autoform, but autorofm tell me "Error: AutoForm: You must specify a collection when form type is insert."
I have only one way through the methods?
Please help me to solve the problem.
Helper
Template.accountForm.helpers({
userSchema: function () {
return Schema.User;
}
});
Template
<template name="accountForm">
<div class="panel-body">
{{#autoForm schema=userSchema collection=Users id="accountForm" type="insert"}}
<fieldset>
{{> afObjectField name='profile'}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
</div>
</template>
Schema
Schema = {};
Schema.UserProfile = new SimpleSchema({
lastname: {
type: String
}
});
Schema.User = new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
},
createdAt: {
type: Date
},
profile: {
type: Schema.UserProfile,
},
services: {
type: Object,
optional: true,
blackbox: false
}
});
Meteor.users.attachSchema(Schema.User);
You don't need the helper function, just use the Meteor.users object.
{{#autoForm collection='Meteor.users' doc=currentUser type='update' id='accountForm'}}
{{> afQuickField name='profile'}}
<button type='submit' class="btn btn-primary">Save profile</button>
{{/autoForm}}

How to add/edit users with meteor accounts and autoform

I am building part of an admin system in Meteor that lets admins add/edit other admins. I am using Meteor Accounts and Autoform, but I can't figure out how to handle it so the users are validated with Autoform and saved properly. From what i've found it looks like I need to use the Accounts.createUser method and make the form a type="method" or something, but I'm not sure how to handle that or if that is even the correct way.
Here is my code right now:
Schema:
Schema = {};
Schema.UserProfile = new SimpleSchema({
name: {
type: String,
label: "Name"
}
});
Schema.User = new SimpleSchema({
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
},
password: {
type: String,
label: "Password",
min: 6
},
passwordConfirmation: {
type: String,
min: 6,
label: "Password Confirmation",
custom: function() {
if (this.value !== this.field('password').value) {
return "passwordMissmatch";
}
}
},
createdAt: {
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
} else {
this.unset();
}
}
},
profile: {
type: Schema.UserProfile
},
services: {
type: Object,
optional: true,
blackbox: false
}
});
Meteor.users.attachSchema(Schema.User);
Routes:
Router.route('/admin/admins', {
controller: 'AdminController',
name: 'adminAdmins',
title: 'Admins',
parent: 'adminHome',
});
Router.route('/admin/admins/new', {
controller: 'AdminController',
name: 'adminAdminNew',
title: 'New Admin',
parent: 'adminAdmins',
});
Router.route('/admin/admins/:_id/edit', {
controller: 'AdminController',
name: 'adminAdminEdit',
title: 'Edit Admin',
parent: 'adminAdmins',
data: function() {
return Meteor.users.findOne(this.params._id);
}
});
Admin Form:
{{#autoForm collection="Meteor.users" doc=this id="adminAdminForm" type=formType}}
{{> afQuickField name='profile.name'}}
{{> afQuickField name='email'}}
{{> afQuickField name='password'}}
{{> afQuickField name='passwordConfirmation'}}
<button type="submit" class="btn btn-block btn-secondary">Save Changes</button>
{{/autoForm}}
You should add Hooks to be able to modify the collection
Something that should look like this
AutoForm.hooks({
adminAdminForm: {
onSubmit: function (doc) {
schemas.User.clean(doc);
this.done();
return false;
},
onSuccess:function(operation, result, template){
Router.go('users.show',{'username':template.data.doc.username});
},
onError: function(operation, error, template) {
console.log(operation,error)
}
}
});
You can find more details on the dedicated documentation https://github.com/aldeed/meteor-autoform#callbackshooks

iron:router will not re-render after route change with same template

How can I make Iron:router re-render a template?
I have this html:
<head>
</head>
<body>
</body>
<template name="mainLayout">
list
find
{{> yield}}
</template>
<template name="listTemplate">
<p>list</p>
</template>
and this js:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate'
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function () {
return this.params.query;
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function () {
if (this.data)
console.log('find ' + this.data.q);
else
console.log('list all');
};
}
When I click on the links to switch views (simulated here with console.log), the route does change, but the template is not re-rendered.
Is there a way to force iron:router to re-render?
Setting the router controller state did not work for me. The answer Antônio Augusto Morais gave in this related github issue worked. Using the Session to store the reactive var to trigger the autorun reactiveness. It's a hack, but it works.
## router.coffee
Router.route '/profile/:_id',
name: 'profile'
action: ->
Session.set 'profileId', #params._id
#render 'profile'
## profile.coffee
Template.profile.onCreated ->
#user = new ReactiveVar
template = #
#autorun ->
template.subscription = template.subscribe 'profile', Session.get 'profileId'
if template.subscription.ready()
template.user.set Meteor.users.findOne _id: Session.get 'profileId'
else
console.log 'Profile subscription is not ready'
Template.profile.helpers
user: -> Template.instance().user.get()
## profile.html
<template name="profile">
{{#if user}}
{{#with user.profile}}
<span class="first-name">{{firstName}}</span>
<span class="last-name">{{lastName}}</span>
{{/with}}
{{else}}
<span class="warning">User not found.</span>
{{/if}}
</template>
You can try something like this:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate',
action: function() {
this.state.set('query', this.params.query);
}
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function() {
return this.params.query;
},
action: function() {
this.state.set('query', this.params.query);
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function() {
this.autorun(
function() {
if (this.state.get('query'))
console.log('find ' + this.data.q);
else
console.log('list all');
}
);
};
}
The rendered method isn't reactive, that's why you need an autorun.
The template "this.data" isn't reactive so you're gonna need a reactive var to do that, either a Session variable, a controller state, or some kind of reactive var.
You may need to add the reactive-var package depending on what approach you take.

Resources