Meteor Autoform Custom Templates - templates

This is a niche one - hopefully someone has had some experience with this before! Any pointers would be greatly appreciated.
Problem
I'm trying to use Autoform together with Autoform-Semantic-UI and nested custom templates. In the interface, the outer form loads, but when my custom templates load I get console errors referencing that the template cannot access any data, such as:
debug.js:41 Exception in template helper: TypeError: Cannot read property 'type' of undefined
at afArrayTracker.atInitField [as initField]
Exception in template helper: TypeError: Cannot read property 'slice' of null
at Object.autoFormGetLabelForField [as getLabelForField]
Exception in template helper: Error: Invalid field name: (not a string)
at Object.getDefs
This makes my form only partially rendered!
Code
client.js has the schema design:
Schemas = {};
Template.registerHelper("Schemas", Schemas);
Schemas.MarkingScheme = new SimpleSchema({
name: {
type: String,
optional: false
},
description: {
type: String,
optional: true
},
aspects: {
type: Array
},
'aspects.$': {
type: Object
},
'aspects.$.focus': {
type: String
},
'aspects.$.rubric': {
type: Array,
minCount: 1
},
'aspects.$.rubric.$': {
type: Object
},
'aspects.$.rubric.$.mark': {
type: Number
},
'aspects.$.rubric.$.criteria': {
type: String,
optional: true
},
comments: {
type: [String],
optional: true
}
});
Here's the template for the form, which works fine:
<template name="insertScheme">
<div class="ui large header">New Marking Scheme</div>
{{#autoForm schema=Schemas.MarkingScheme id="insertScheme" type="insert"}}
{{> afQuickField name="name"}}
{{> afQuickField name="description"}}
{{> afArrayField name="aspects" template="rubric"}}
{{> afQuickField name="comments"}}
<div class="row">
<div class="centered column">
<div class="two large ui buttons">
<button type="reset" class="ui button">Clear</button>
<div class="or"></div>
<button type="submit" class="positive ui button">Submit</button>
</div>
</div>
</div>
{{/autoForm}}
</template>
Now is where things go south, even though I've reverted to using a near carbon copy of the template used for the semantic-ui autoform package:
<template name="afArrayField_rubric">
<h4 class="ui top attached block header">
{{afFieldLabelText name=this.atts.name}}
</h4>
<div class="ui secondary bottom attached segment">
{{#if afFieldIsInvalid name=this.atts.name}}
<div class="ui pointing red basic label">
{{{afFieldMessage name=this.atts.name}}}
</div>
{{/if}}
{{#afEachArrayItem name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
<div class="field autoform-array-item">
{{#if afArrayFieldHasMoreThanMinimum name=../atts.name minCount=../atts.minCount maxCount=../atts.maxCount}}
<div class="ui mini red corner label autoform-remove-item">
<i class="icon minus"></i>
</div>
{{/if}}
{{> afArrayField name=this.name label=false options=afOptionsFromSchema template="aspects"}}
</div>
{{/afEachArrayItem}}
{{#if afArrayFieldHasLessThanMaximum name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
<div class="field">
<div class="ui small green icon button autoform-add-item" data-autoform-field="{{this.atts.name}}" data-autoform-minCount="{{this.atts.minCount}}" data-autoform-maxCount="{{this.atts.maxCount}}">
<i class="icon plus"></i>
</div>
</div>
{{/if}}
</div>
</template>
The idea is that it goes on deeper; but this is as far as we get.
Many thanks for any ideas of things to try!

Related

MeteorJS get data passed to the template

I am a newbie in MeteorJS. I tried to do list with download and report buttons:
List template:
<template name="myBooks">
<div class="container">
<h2>My books</h2>
<div class="list-group">
{{#each myBooks}} {{> myBookListItem}} {{/each}}
</div>
</div>
</template>
myBookListItem Template:
<template name="myBookListItem">
<a class="list-group-item">
<span class="badge badge-success">{{downloads}}</span>
<span class="badge badge-error">{{warnings}}</span>
<h4 class="list-group-item-heading "><i>{{title}}</i> - {{author}}</h4>
<p class="list-group-item-text ">{{description}}</p>
<button type="submit" id="download" class="btn btn-success">Download</button>
<button type="submit" id="report" class="btn btn-danger">Report</button>
</a>
</template>
My book collection item contains also "link" property which is url to downloadable pdf - how can I access it on #download button click?
if (Meteor.isClient) {
Template.myBookListItem.events({
'click #download': function (event) {
//What_kind_of_magic_shoud_i_use_here()
alert();
},
'click #report': function (event) {
alert();
},
});
}
Anybody can help me:)?
I just needed to use this.link.

Template for modal from remote page

I am implementing modals as an ember component and I was wondering if and how could this be accomplished: If the content of the modal is coming from a different page, how would it be rendered as a template:
Example:
http://jsfiddle.net/koala_dev/NUCgp/918/
The script above opens a modal with this content:
http://fiddle.jshell.net/bHmRB/51/show/
But what if I wanted some extra data that depends on specific users, coming from the routes model? How would I insert something like
{{#each link in links}}
{{#link-to 'link.url'}}{{link.title}}{{/link-to}}
{{/each}}
into the remote page, so it would be rendered as a template, and not regular text?
Here is a very quick and easy example just to give you a right direction. You can play with it and parse the html to extract content (which is not relevant to the question).
You can setup a component for modal element. I've setup a component property (attribute) contentURL where you can set the url of the content.
Component handlebars
Notice the {{content}} which we used later in the action to save the ajax result. Action showModal is added on the link to execute the ajax call.
<script type="text/x-handlebars" id="components/bootstrap-modal">
<a data-toggle="modal" data-target="#myModal" {{action "showModal"}}>Click me !</a>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">{{content}}</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
</script>
Component JS
App.BootstrapModalComponent = Ember.Component.extend({
/*initModal: function(){
}.on('didInsertElement'),*/
contentURL: null,
content: 'Content is loading...',
actions: {
showModal: function(){
var self = this;
var contentURL = this.get('contentURL');
if(contentURL == null){
this.set('content', '<p>No content url is given</p>');
}
console.log('get content from url > ' + contentURL);
$.ajax({
url: contentURL,
dataType: "html",
beforeSend: function(){console.log('before send');},
error: function(xhr, status, message){
console.log(message);
},
success: function (content) {
// parse and get the right html for the content
self.set('content', content);
}
});
}
}
});
Now you can call the component bootstrap-modal on your template like {{bootstrap-modal}}
For example:
<script type="text/x-handlebars" id="index">
{{bootstrap-modal contentURL="http://fiddle.jshell.net/bHmRB/51/show/"}}
</script>
jsFiddle example http://jsfiddle.net/sisir/NUCgp/1713/

Ember Object, with a nested array

To learn Ember, I've been trying to make a simple app that computes timezones.
When a person enters their city, and the other person's city, I make a GET request to my API, which returns the dates like so --
great_times: [array]
good_for_me: [array]
good_for_them: [array]
In handlebars, I have
<div class="container">
<div class="col-md-6 col-md-offset-3">
<header>
<h2>We found <span class="twenty-four-header">{{totalTimes}}</span>
great meeting {{pluralize totalTimes 'time' 'times'}} for you!</h2>
</header>
<div class="main-content">
<div class="row">
<div class="col-md-6">
<div class="form-group">
{{view RightTime.AutoCompleteAddressView value=myCity placeholder="What's your city?"
class="form-control input-lg" latLng=myLatLng}}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
{{view RightTime.AutoCompleteAddressView value=theirCity placeholder="What their city?"
class="form-control input-lg" latLng=theirLatLng}}
</div>
</div>
</div>
{{#each meetingTime in greatTimes}}
{{render 'meetingTime' meetingTime }}
{{/each}}
</div><!--main-content-->
</div>
</div>
This works, but what happens is that when I update the city, It no longer updates this each loop.
I do know however that the model was updated, because the {{totalTimes}} computed property does update.
This is what my meeting Object looks like:
RightTime.Meeting = Ember.Object.extend({
meetingTimes: null,
myCity: null,
theirCity: null,
myLatLng: null,
theirLatLng: null,
totalTimes: function() {
if (this.meetingTimes) {
return this.meetingTimes.great_times.length;
}
}.property('meetingTimes'),
locationsData: function() {
return {
myLatLng: [this.myLatLng.k, this.myLatLng.A],
theirLatLng: [this.theirLatLng.k, this.theirLatLng.A]
}
}.property('myLatLng', 'theirLatLng'),
findTimes: function() {
var meeting = this;
if (this.myLatLng && this.theirLatLng) {
$.ajax({
url: 'api/meetings',
type: 'GET',
data: meeting.get('locationsData')
}).done(function(response){
meeting.set('meetingTimes', Ember.A(response));
});
}
}.property('myLatLng', 'theirLatLng')
});
I have a feeling that the problem lies in
.done(function(response){
meeting.set('meetingTimes', Ember.A(response));
});
I'm resetting the whole meetingtimes array, which may be the wrong way to go about it.
How would you go about making the meetingTimes arrray update and reflect in handlebars?
I'd probably just move great_times into a computed property that depends on meetingTimes and isn't chained.
something like
greatTimes: function() {
return Em.get(this, 'meetingTimes.great_times') || [];
}.property('meetingTimes'),
With a template like this
{{#each meetingTime in greatTimes}}
{{render 'meetingTime' meetingTime }}
{{/each}}

Why would a newly created record not show up in a list?

I have a form that creates a record, then transitions to the list of resources for a particular object. However once the record is created, it is not reflected in the list of resources. If I refresh the page, the record is saved in the correct place. I have the ember chrome extension installed and if I look under Resources, then the resource is there pointing to the correct Badge. But if I go to badge first, and look for resources, it is not listed. Any ideas? I would be happy to provide any more information necessary to clarify. Thank you in advance
Create Resource Form Controller and Route
Controller
App.ResourcesCreateController = Ember.ObjectController.extend({
resourceTypes: ["link","file","video"],
needs: ['badge','resourcesIndex'],
actions: {
save: function() {
//Gather the info from the form
var description = this.get('description');
var url = this.get('url');
var type = this.get('type');
var text = this.get('text');
var badge = this.get('controllers.badge').get('model');
//set the data to the model of the route (ResourceCreateRoute)
var resource = this.get('model');
console.log(resource);
resource.set('description',description);
resource.set('url',url);
resource.set('type',type);
resource.set('text',text);
resource.set('badge',badge);
var self = this;
//save the route
var a = resource.save().then(function() {
//if success
//this.get('store').reload();
console.log('%c that resource saved rather nicely','color:green;');
self.transitionToRoute('resources.index',self.badge);
}, function() {
//if failure
console.log('%c Yea boss...that didnt go so hot', 'color:red;');
self.set('isError',true);
});
},
reset: function() {
this.transitionToRoute('resources.index');
}
}
});
Route
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource');
}
})
List Resources Route
App.ResourcesRoute = Ember.Route.extend({
model: function(){
return this.modelFor('badge').get('resources');
}
});
Models
Resource Model
App.Resource = DS.Model.extend({
'badge': DS.belongsTo('badge'),
'text': attr('string'),
'url': attr('string'),
'description': attr('string'),
'type': attr('string')
});
Badge Model
App.Badge = DS.Model.extend({
'category': DS.belongsTo('category'),
'title': attr('string'),
'type': attr('string'),
'ord': attr('number'),
'short_description': attr('string'),
'full_description': attr('string'),
'mentor': DS.belongsTo('employee'),
'parent':DS.belongsTo('badge'),
'icon': attr('string'),
'required': attr('boolean'),
'signoff_role': attr('string'),
'created_at': attr('string'),
'updated_at': attr('string'),
'resources': DS.hasMany('resource', { async: true } ),
'quiz': DS.belongsTo('quiz', { async: true } )
});
Templates
List of Resources
{{#link-to "resources.create" class="btn btn-primary btn-xs pull-right"}} Create Resource {{icon "plus"}}{{/link-to}}
<h3>Resources</h3>
<dl>
{{#each resource in controller}}
{{render resources/resource resource}}
{{else}}
<p class="lead text-muted">There are no resources</p>
{{/each}}
</dl>
Resource Item Template
{{#if isEditing}}
<div {{bindAttr class="controller.isError:alert-danger:alert-info :alert"}}>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div class="btn-group">
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
{{#if confirmDelete}}
<div {{action "delete"}} class="btn btn-danger">{{icon "trash"}} Are You sure?</div>
{{else}}
<div {{action "confirm"}} class="btn btn-danger">{{icon "trash"}} Delete</div>
{{/if}}
</div>
<div {{action "reset"}} class="btn btn-default"> {{icon "ban-circle"}} Cancel</div>
</div>
{{else}}
<div class="btn-group pull-right btn-group-xs">
{{#if view.hover }}
<div {{action "edit"}} class="btn btn-default">{{icon "cog"}}</div>
{{/if}}
</div>
<dt>
<span class="text-muted">{{resource_icon type}}</span> {{text}}
</dt>
{{#if description}}
<dd class="text-muted" style="margin-bottom:1em">
{{markdown description}}
</dd>
{{/if}}
<hr>
{{/if}}
Create Resource Template
<h3>Create Resource</h3>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
<div {{action "test"}} class="btn btn">Test</div>
{{#link-to "resources.index" class="btn btn-default" }} {{icon "ban-circle"}} Cancel {{/link-to}}
<br><br>
</div>
Just some general notes first.
With this much code, everyone's going to have a much easier time helping you if you provide a JSBin or something. It's a bit of extra work for you, but you're asking for help, and this is a lot to just mentally parse and run. Personally, it was some extra overhead for me because you didn't include your router, so I had to do a pass just to try to figure out how badge and resource were related.
When you're using an ObjectController with the route model set to a new record, with input helpers, you shouldn't need to do all of that setting. That's why you specified those value bindings on the helpers. But when you do need to set a bunch of properties, you can just do that all at once with something like record.setProperties({prop1: prop1Value, prop2: prop2Value ...}); and save yourself a lot of typing.
I don't understand why you're using resourcesIndex as a ResourcesCreateController need. To actually answer your question, it might work to specify just 'resources' as a need
then use something like
resource.save().then(function(record){
self.get("controllers.resources").pushObject(record);
self.transitionToRoute("resources.index", badge); // I don't know if this makes any sense because you didn't show your router, but at the very least, don't use self.badge, or even self.get("badge"), because badge is already accessible in this scope.
}
It'd be nice to see your Model definitions, and even better if you had a jsbin setup showing the problem.
You could always try hooking it up on createRecord.
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource', {badge: this.modelFor('badge')});
}
})
It seems like when you create the new resource, you aren't putting it in the store. You should try something like this.store.createRecord(resource) then instead of your resource.save, do a this.store.commit.
I'm not entirely sure I'm right. But it may be a possibility.
Fixed. I am not sure if this is the correct way to handle this but basically the parent model needed to be reloaded once the child model was created and saved. So like this
save: function() {
var self = this;
var resource = this.store.createRecord('resource',{
property: 'property',
relatedProperty: this.get('model'),
//etc
});
resource.save().then(function(){
self.get('model').reload();
},function(){
//do stuff because the resource didnt save
});
}

In EmberJS my event triggers all the sub-views instead of just the targeted one

i'm learning EmberJS and building a comment section that allows 1 level of sub comments. I have an Ember View listing all the comments, when you click "reply" on a particular comment, it should display a textarea input for a user to write a sub-comment.
In my EmberJS code when you click "reply" it shows the textarea input for all the comments not just the specific one. Any advice would be appreciated :)
// View
App.commentsView = Em.View.create({
templateName: 'commentsTmpl',
showReply: false,
reply: function(e) {
e.view.set('showReply', true);
e.preventDefault();
}
});
App.replyCommentsView = Em.View.extend({
showReplyBinding: 'App.commentsView.showReply'
});
// Template
<script data-template-name="commentsTmpl" type="text/x-handlebars">
</h2>comment</h2>
{{#each App.commentsController}}
<div class="comment-group clearfix">
<div class="comment">
<img class="comment-pic" {{bindAttr src="userPic"}} alt="user pic">
<div class="comment-content">
{{userName}}
<span class="comment-body">{{text}}</span>
<div class="comment-actions clearfix">
<a href="#" {{action "reply"}}>Reply</a>
</div>
</div>
</div>
{{#view App.replyCommentsView}}
{{#if showReply}}
<div class="comment-reply">
<h2>sub-comment</h2>
<textarea class="txt-comment-reply" rows="2" cols="65"></textarea>
</div>
{{/if}}
{{/view}}
</div>
{{/each}}
</script>
Currently you are binding the showReply to App.commentsView which is the whole container. To be make it easy activate single comments, I'd suggest looking into a CollectionView, this way each of your comments will have their own view and you can toggle showReply on an individual comment's view.
Something like this: (Sorry, I haven't tested it)
App.commentsView = Em.View.create({
templateName: 'commentsTmpl'
});
App.CommentView = Em.View.extend({
classNames: "comment-group clearfix".w(),
showReply: false,
reply: function(e) {
e.preventDefault()
this.set("showReply", true)
}
})
// Template
<script data-template-name="commentsTmpl" type="text/x-handlebars">
</h2>comment</h2>
{{#collection contentBinding="App.commentsController" itemViewClass="App.CommentView"}}
<div class="comment">
<img class="comment-pic" {{bindAttr src="content.userPic"}} alt="user pic">
<div class="comment-content">
{{content.userName}}
<span class="comment-body">{{content.text}}</span>
<div class="comment-actions clearfix">
<a href="#" {{action "reply"}}>Reply</a>
</div>
</div>
</div>
{{#if showReply}}
<div class="comment-reply">
<h2>sub-comment</h2>
<textarea class="txt-comment-reply" rows="2" cols="65"></textarea>
</div>
{{/if}}
{{/each}}
</script>