Is it possible to implement dynamic form input rows (adding new row by clicking on Add) with Handlebars and Meteor? - forms

I'm getting my hands dirty with Meteor, and I wanted to port this AngularJS app (http://simplydo.com/projector/) over as an exercise.
I'm having difficulty implementing adding dynamic input form rows to sections using Handlebars, and I've found no documentation anywhere that documents if this possible. It's a snap in Angular using ng-repeat, but I want to confirm if this is something that is possible in Handlebars or whether I need to use Jquery in order to achieve this.
Thanks in advance.

It's just as easy in Meteor
all you have to do is repeat the rows with {{#each rowData}} and have a button that adds a document to the collection. Here is an example:
In this template the rows for a page are shown. In order to add a row, the user has to click on the add image. The template is :
<template name="page">
{{#with page}}
<h3>{{title}}</h3>
{{#each rows}}
<div class="row-fluid page-row {{#if isSelected this}}selected-row{{/if}}">
... page content here
</div>
{{/each}}
{{/with}}
<div class="btn-toolbar">
<div class="pull-right">
<a id="add-row" href="#" data-toggle="tooltip" title="{{lbl 'add-page'}}">
<img src="/images/add.png" class="asset-icon"/>
</a>
<img src="/images/separator.png" class="asset-icon"/>
<a id="delete-row" href="#" data-toggle="tooltip" title="{{lbl 'delete-page'}}">
<img src="/images/delete.png" class="asset-icon"/>
</a>
</div>
</div>
</template>
the helpers for this template are:
Template.page.page = function () {
return Session.get("selected-page");
}
in order to add a page the click event for the add button is implemented:
"click #add-row": function (evt, template) {
var page = Session.get("selected-page");
Pages.update({_id: page._id}, ... add a new row here ...)
}
because the whole thing is reactive, the list of rows will redraw after the update on the Pages collection.

Related

Dynamically adding Semantic UI modals with SilverStripe

I'm having a hard time trying to connect SUI modal with SilverStripe to generate them dynamically.
I want to achieve something like this:
I have button (attach events) to trigger modal with some content. I wanted to loop that elements (GridField) to generate everything dynamically. But the only thing I can achieve is multiple modals with the same "trigger class" and it doesn't matter which button I have clicked (first, last or whatever). I only have one modal (the last in hierarchy). Without SUI the solution is easy - put "this" to fetch the closest element and I can have as many different modals as I want, but SUI seems to complicate things.
I think the best solution will be to generate a class/id with SilverStripe and pass it to the JavaScript to use with modal or use one class for every modal and to "somehow inform" that this button triggers this modal.
Basically I need one code snippet to handle many modals, not many snippets to handle many modals. I hope it's clear enough what the the problem is.
Here is my code:
HTML/SS
(without specific SilverStripe tags)
<% loop SomeName %>
<div class="job-offers">
<a class="ui right floated button job-application">Click here</a>
</div>
<div class="ui basic modal job-application">
<div class="job-application-sheet">
(...)
<div class="job-application-sheet-content">
<div class="ui grid">
(...)
<div class="ui center aligned container job-application-action">
<p>Lorem ipsum</p>
<button class="ui primary button">Click here</button>
</div>
</div>
</div>
</div>
</div>
<% end_loop %>
JavaScript
$('.ui.basic.modal.job-application')
.modal({
autofocus : false,
observeChanges: true
})
.modal('attach events', '.job-application', 'show');
As you can see "job-application" class is a trigger for modal, so is this possible to change it to "(this)" so I don't have to write "specific" class for each button/modal. Or maybe there is a different/easier solution?
first i generated a data-type attribute for each of my elements that are going to display a modal when they are clicked, and in send as a local the same index to the modal this way:
<% #relateds_array.each.with_index do |related,i| %>
<div class="card custom" data-id="<%=i%>">
<%= render partial: 'my_modal', locals: {index: i} %>
</div>
<% end %>
the i put the modal in the partial that i called my_modal for this example and used the index that i sent as a local to create an unique id for each my modals, like this:
<div class="ui modal" id="modal-<%=index%>">
<p>blabla things inside this modal.</p>
</div>
and then with javascript, simply get the data-id of the element clicked and select the element that contain the id for that data-id, like this:
$('.element_that_you_want_to_open_moda_on_click').click(function(event){
var card_clicked = $(this).attr('data-id');
$('#modal-' + card_clicked).modal('show');
});
Hope this was useful for you.
Note that i used Ruby on Rails for this example, but the behaviour that you should use should be something similar to this, no matter what framework you use.
Answer based on Sebastian's solution. I did some minor tweaks to meet my needs, like I've used ID variable to automatically get DataObject ID which is generated dynamically.
Basically to dynamically add Semantic UI (SUI) modal in SilverStripe, first you should add "data-id" in a modal trigger, for example:
Template.ss
<a class="ui button custom-trigger" data-id="my-item-$ID">Click here</a>
then inside modal container add an "id" tag, for example:
<div id="modal-my-item-$ID" class="ui basic modal">
(...)
</div>
Finally in JavaScript file:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('#modal-' + triggerItem).modal('show');
});
I meet problem with SUI autofocus, so after a modal opens, screen went to the bottom and button placed there was highlighted.
I tweaked original snippet adding SUI settings:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('.ui.modal')
.modal({
autofocus: false,
observeChanges: true
})
$('#modal-' + triggerItem).modal('show');
});
If someone wants to write "data-id trigger" manually using fields in CMS, then change $ID to $SomeField variable. Then normally add that field in .php file and in Page Controller add something like this:
public function init() {
parent::init();
Requirements::javascriptTemplate($this->ThemeDir().'/js/script.js', array(
'SomeField' => $this->SomeField
));
}
Hope this helps someone.

How to combine two collections objects to one table while looping over all objects in Meteor.js?

I'm working on the simple Meteor Chat application. I have two different collections, textMessages and FS.images. I need to display those element based on time in one flow. Now I submit them both apart from each other and can't figure the way, while looping over them using #each handler.
Template code :
<ul class="list-group">
{{#each messages}}
<li class="list-group-item">
<span class="badge">x</span>
UserN: {{text}}
</li>
{{/each}}
</ul>
<ul class="list-group">
{{#each showImages}}
{{#unless this.isUploaded}}
{{> FS.UploadProgressBar bootstrap=true}}
{{/unless}}
{{> imageItem}}
{{/each}}
</ul>
You can create a helper in the template that would combine both collections results and sort them by date if each user has set of images and text.
If each message has a set of images you can use this package for creating helper for the collection in such a way you can get the set of images for a certain message.
meteor-collection-helpers

how can I use the same template multiple times on meteor.js

I just started use meteor about a week ago, and I'm trying to write my first app for logging time for a project.
At the end of the day users can go in and log their hours with a single row consisting of 2 drop down select menus. First is the clients drop down. then based on that client (using Session) the 2nd drop down for client projects will auto-populate, and finally allowing you to enter your hours in a text input.
I have this working, but I also need to implement a button so you can add multiple rows at once. Sort of like jQuery Clone() in case the user worked on different clients or client projects.
I tried to re-render the row once newRow is clicked, but then the second row manipulates the first row because I'm assuming they're both referencing the same template.
To simplify? 1) How do I duplicate the below the last one, and 2) How do I use the same template for 1 or more rows and have them not affect each other?
Any thoughts/help is welcome
<form id="add_time">
<template name="row">
<ul>
{{> clientsDD}}
{{> projectsDD}}
{{> hoursAndTasks}}
</ul>
</template>
<p>Add another row
<input type="submit" id="submit_hours" value="Submit"/>
</form>
Template.clientsDD.clients = function() {
return Clients.find({});
}
Template.projectsDD.projects = function(event) {
return Projects.find({"client.clientId" : Session.get("clientSelected")});
}
Template.addHours.events({
'change select[name="clientsDD"]' : function(event){
newClient = $(event.target).val();
Session.set("clientSelected", newClient);
},
'change select[name="projectsDD"]' : function(event){
newProject = $(event.target).val();
}
});
There are two ways in my mind this can be done. As Meteor doesn't require page reloads to update your content. There is nothing to stop you have the row save on completion and then just having a loop which populates the page. Your form becomes something like this:
<form id="add_time">
{{#each rows}}
{{> row}}
{{/each}}
{{> row}}
<input type="submit" id="submit_hours" value="Save and Add Another"/>
<input type="button" id="submit_and_exit" value="Save and Exit"/>
</form>
<template name="row">
<ul>
{{> clientsDD _id}}
{{> projectsDD _id}}
{{> hoursAndTasks _id}}
</ul>
</template>
This would loop through a rows helper which contains your hours data displaying it for editing along with providing a new empty row. This would automatically update on save with no need for any DOM manipulation.
The other option is to duplicate the fields. I'm not aware of a method that allows you to duplicate a template but there's no reason why jQuery clone type function wouldn't work however you'd need to make sure that on saving they are saved separately. clientsDD[] as the input names should be okay as long as you give each group of fields a unique id.
I would personally use the first method thou as IMHO this is the way Meteor is designed to work so you don't have the need to manipulate the DOM as it will seamlessly update with the data store.

getting a collection back from a form in grails

I have an album view where a user can check which photos to delete. I am currently doing this:
<g:form action="delete">
<g:each in="${pictures}">
<div id="images2">
<img id="images2" src="${it.urlThumb}" alt="no Picture"/><br>
<g:checkBox name="myCheckbox"/>
</div>
</g:each>
<g:submitButton name="Submit"/>
</g:form>
The problem is this creates the form dynamically so each of the names are the same for the check boxes. I ideally want a collection returned to the controller that has each of the checked images.
Any ideas?
In the <g:checkBox> you need to specify a value. Each checkbox will be created with the same name but a different value. When the form is submitted with different checkboxes checked, you will get a list in the controller.
View:
<g:each in="${pictures}">
<div id="images2">
<img id="images2" src="${it.urlThumb}" alt="no Picture"/><br>
<g:checkBox name="myCheckbox" value="it.id"/>
</div>
</g:each>
Controller:
def checked = params.list('myCheckbox')

How to handle Multiple DOM elements with iScroll (while using jQTouch)

I've my markups as
<div id="home" class="current">
<div class="header">iScroll</div>
<div class="wrapper">
<div id="scroller">
<ul id="thelist" class="plastic"><!-- li items --></ul>
</div>
</div>
<div class="footer">Footer</div>
</div>
<!-- Events Details -->
<div id="events">
<div class="header">iScroll</div>
<div class="wrapper">
<div id="scroller"> <!-- stuffsss --></div>
</div>
<div class="footer">Footer</div>
</div>
For iScroll (http://cubiq.org/iscroll) to work, I need the #scroller as ID (as per the javascript Code I'm using to initialize iScroll.
//for iScroll
var myScroll = new iScroll('scroller', {desktopCompatibility:true});
// Load iScroll when DOM content is ready.
document.addEventListener('DOMContentLoaded', loaded, false);
But since I can't have two different elements with the same ID (please notice I've got two elements with same id scroller in my markup above), some conflicts are there and the iScroll isn't working properly.
I want to be able to implement the iScroll on the markup by changing the id as classes. I tried to change them into classes and see if it works but I couldnt get it right.
Can anyone help me change the codes so that it works by implementing classes instead of the the id??
Rob is right, but you can change your code to scroller classes as you said.
Then initialise your scrollers within unique wrappers like this:
var scroll1, scroll2;
function loaded() {
scroll1 = new iScroll('wrapper1');
scroll2 = new iScroll('wrapper2');
}
I'm not totally clear on what you are trying to achieve but if you want two parts of your page to scroll I would suggest changing the IDs to be unique and instantiate two iScrolls with the different IDs.
I am sure you have figured it out, but for other users still struggling with similar layout (multiple scrollers) and want to make them work. Here is the answer from other thread
https://stackoverflow.com/a/7584694/1232232
but for this to work you need to assign ID's to your classed (div containers)
like
<div id="home" class="current">
<div class="header">iScroll</div>
<div id="wrapper-1" class="scrollable">
<div class="scroller">
<ul class="thelist" class="plastic"><!-- li items --></ul>
</div>
</div>
<div class="footer">Footer</div>
</div>
<div id="home2" class="current">
<div class="header">iScroll</div>
<div id="wrapper-1" class="scrollable">
<div class="scroller">
<ul class="thelist" class="plastic"><!-- li items --></ul>
</div>
</div>
<div class="footer">Footer</div>
</div>
Note: Remember not to assign same ID to multiple elements, always use classes for that purpose.