Project

tmpl

0.0
No commit activity in last 3 years
No release in over 3 years
An agnostic approach to handle dynamic forms in rails.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 3.9
~> 1.6.0
~> 3.2.4
~> 5.2
~> 4.0

Runtime

> 1.0.0
 Project Readme
= tmpl

* https://github.com/nearapogee/tmpl
* https://github.com/nearapogee/tmpl-example-app

== DESCRIPTION:

An agnostic approach to handle dynamic forms in rails.

== FEATURES/PROBLEMS:

* Simple: Build a template, multiply that template.
* Minimal view duplication.
* Works with deeply nested forms.
* Built minimal and light.
* Low coupling.
* No magic.
* Unobtrusive javascript.

There are no know problems. This is currently a pre-release. The following are
anticipated changes:

* The class names starting tmpl-container will become tmpl-ctn.
* The tmpl-remove class will become tmpl-rm (non user facing).
* JS will be refactored, for better naming.
* Support for <label for=""> renaming attributes to match field ids. (Soon!)

== SYNOPSIS:

Annotated deeply nested form example, taken from tmpl-example-app.
(https://github.com/nearapogee/tmpl-example-app) Everything is standard rails,
but it does need some integration with the model layer, so see the example to
if the documentation is not enough. All of the helper methods are documented
in Tmpl::ActionViewExtension.

[1]   - Create a form.
[2]   - Add regular model fields.
[3]   - Define a container that the templates will be appended to. This should
        have a class starting with tmpl-container and this should be unique on
        on the page even though it is a class. More on that further down.
        Sometimes this is optional, but it makes the logic explicit.
[4]   - Build a template in a block. This template is not printed to the
        screen where the template is built. Print it with [6] out of the way.
        The key attribute denotes the "key" of the indexed hash of attributes
        living in the template. It is sometimes optional with shallow nested
        forms (one level), but again it makes the logic explicit.
[5]   - Add a link to append a new template to the container defined in [3].
        Set the body of the link with the first argument or a block, like
        link_to allows. The next argument is the name of the template, matching
        what was passed to tmpl_build. The container option allows you to
        specify a css selector (usually always a class) of container to append
        to. This is not required if link it contained in the container and the
        container has a class name of tmpl-container.
[6]   - Print out the templates, with the name set in tmpl_build.
[7]   - Each template needs to be wrapped in a div with class tmpl.
[8]   - If users need to be able to destroy records add a :_destroy hidden
        and it will be set to true if the template is removed.
[9]   - This is the same as [3], except exhibits an additional feature. Again
        the class name of the container starts with tmpl-container, followed
        with -heading which is the name of the template, followed with a
        -index. Along with [10] and the container option, these indexes will
        be updated when new templates are appended with the add link. This
        allows even deeply nested forms to have their add links out side of
        the container they are appended to. The class naming convention is
        required to use this feature.
[10]  - Another add link, but using the index template name and index feature,
        to link it to the appropriate container, when there are many in a
        deeply nested form. Note that the index comes from standard ruby/rails
        iteration and is not part of this gem.
[11]  - Add a remove link. The link must be contained within the template
        (.tmpl) to be removed.

# app/views/book/_form.html.erb
<%= form_for(@book) do |f| %> [1]
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %> [2]
  </div>

  <div class="tmpl-container-chapter"> [3]
    <% f.object.chapters.each.with_index do |chapter, index| %>
      <%= f.fields_for :chapters, chapter do |chapter_fields| %>
        <%= render 'chapter', chapter_fields: chapter_fields, index: index %>
      <% end %>
    <% end %>
  </div>

  <% tmpl_build :chapter, key: 'chapters_attributes' do %> [4]
    <%= f.fields_for :chapters, Chapter.new do |chapter_fields| %>
      <%= render 'chapter', chapter_fields: chapter_fields, index: 0 %>
    <% end %>
  <% end %>

  <%= tmpl_add_link "Add Chapter", :chapter,
      container: '.tmpl-container-chapter' %> [5]

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<%= tmpl :chapter %> [6]
<%= tmpl :heading %> [6]

# app/views/book/_chapter.html.erb
<div class="tmpl"> [7]
  <div class="field">
    <%= chapter_fields.label :title %><br>
    <%= chapter_fields.text_field :title %> [2]
  </div>
  <%= chapter_fields.hidden_field :_destroy %> [8]

  <div class="tmpl-container-heading-<%= index %>"> [9]
    <%= chapter_fields.fields_for :headings do |heading_fields| %>
      <%= render 'heading', heading_fields: heading_fields %>
    <% end %>
  </div>

  <% tmpl_build :heading, key: 'headings_attributes' do %> [5]
    <%= chapter_fields.fields_for :headings, Heading.new do |heading_fields| %>
      <%= render 'heading', heading_fields: heading_fields %>
    <% end %>
  <% end %>

  <%= tmpl_add_link 'Add Heading', :heading,
      container: ".tmpl-container-heading-#{index}" %> [10]
  <%= tmpl_remove_link 'Remove Chapter' %> [11]
</div>

# app/views/book/_heading.html.erb
<div class="tmpl"> [7]
  <div class="field">
    <%= heading_fields.label :text %><br>
    <%= heading_fields.text_field :text %> [2]
  </div>
  <%= heading_fields.hidden_field :_destroy %> [8]

  <%= tmpl_remove_link 'Remove Heading' %> [11]
</div>

== WHY:

Seems like every app needs something like this. This methodology has been
adapted over countless projects until it was time to be extracted.

== REQUIREMENTS:

* rails
* (jquery needs to be loaded on the page)

== INSTALL:

* Add to Gemfile:
  gem 'tmpl'

* Run:
  bundle

* Add to application.js  manifest:
  //= require tmpl

== LICENSE:

(The MIT License)

Copyright (c) 2014 Matthew C. Smith

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.