rwz / nestive

A Better Nested Inheritable Layouts Plugin for Rails
MIT License
236 stars 31 forks source link

Extending Partials #11

Closed CyborgMaster closed 11 years ago

CyborgMaster commented 11 years ago

I love Nestive and how it allows me to really make my html templates DRY. I wanted to apply the same modularity to partials. I have a few partials that are extremely similar (Content types that each need a slightly different editor) and so I wanted to leverage nestive inheritance.

However it doesn't work. It appears that there is only one nestive context per view render or something, because both partials end up with the same content.

I have a setup similar to this:

Parent partial: _parent.html.erb
<div>
  Shared Content...
  <%= yield %>
</div>

Child 1: _child1.html.erb
<%= extends "_parent" do %>
  Child 1 Content
<% end %>

Child 2: _child2.html.erb
<%= extends "_parent" do %>
  Child 2 Content
<% end %>

Main view: show.html.erb
<%= render 'child1' %>
<%= render 'child2' %>

The partials work, but I get the same content out of both of them as follows:

<div>
  Shared Content...
  Child 1 Content
</div>
<div>
  Shared Content...
  Child 1 Content
</div>

I haven't delved into the nestive code to try and figure out why yet (although I hope to find to do that today) but I thought I would ask for help here first.

Is this a use case that nestive can handle? If not can we extend it somehow to make it work?

CyborgMaster commented 11 years ago

So I think I actually figured it out (and have therefore turned this issue into a pull request). I'm simply saving and restoring the area context each time a template extends another. This allows for branching extend trees (as opposed to a single linear extend) and allows the same base template to be used by two children (partials) in the same view render. Awesome!!

Caveat: I'm still a little fuzzy on how rails view rending internals works. I think I've got it mostly groked, but if there is something I missed or a better way to do this, let me know and I'll fix it.

rwz commented 11 years ago

I'm really not very comfortable with the idea of extending partials. This can become super messy and confusing.

Let's sit on this one. You can try using your branch for some time and see if it actually works and delivers any value. And then maybe update the pull request with real-live (relatively) examples.

rwz commented 11 years ago

I'll close this for now, but feel free to comment on this.

CyborgMaster commented 11 years ago

I have been using this for the past month and getting great use out of it. I'll put together a comment explaining how I'm using it.

CyborgMaster commented 11 years ago

I'm have a settings page that has several similar sections to it. We have different communication types, email and text message, and they have similar, but slightly different partials. Basically everything is the same except text messages uses a text area as the editor while email uses a rich text editor (tinyMCE). So I end up with a parent partial and two child partials.

_medium_section.html.erb:

<div id="<%= area :id %>">
  <%= yield %>

  <div class="sub-title space-vertical">Delivery Schedule</div>
  <form class="form-inline">
    <div class="space-vertical" ng-repeat="delivery in medium.deliveries">
      <%= select_tag :offset, options_for_select(0..100),
          'ng-model' => 'delivery.positiveOffset' %>
      <%= select_tag :unit, options_for_select(Delivery.unit.values),
          'ng-model' => 'delivery.unit' %>
      <%= select_tag :direction, options_for_select(['before','after']),
          'ng-model' => 'delivery.direction' %>
      <button class="btn" ng-click="medium.deliveries.splice($index, 1)">
        <i class="icon-trash"></i>
      </button>
    </div>
    <div>
      <button class="btn" ng-click="medium.addDelivery()">Add delivery</button>
    </div>
  </form>
</div>

_sms_medium_section.html.erb:

<%= extends "communication_settings/_medium_section" do %>
  <% replace :id, 'smsMediumSection' %>
  <div>
    <textarea id="sms-medium-editor"
              rows="4"
              class="input-block-level"
              ng-model="medium.contents[0].content">
    </textarea>
  </div>
<% end %>

_email_medium_section.html.erb:

<%= extends "communication_settings/_medium_section" do %>
  <% replace :id, 'emailMediumSection' %>
  <label>Subject</label>
  <input type="text" ng-model="medium.contents[0].subject">
  <label>Body</label>
  <textarea id="email-medium-editor"
            ui-tinymce="tinymceSettings"
            ng-model="medium.contents[0].content"></textarea>
<% end %>

And then _email_medium_section and _sms_medium_section are included directly in the settings page. This allows me to keep the shared parts of the partials in a parent file, keeping my html DRY.

What do you think?

CyborgMaster commented 10 years ago

I left a very detailed example of how I am using extended partials to great benefit in my project. What do you think?

rwz commented 10 years ago

Honestly, I still don't like the idea. I think partials should be minimal atomic blocks you use to build the page. I prefer to keep things simple, and extending partials is anything but simple.

And if you really need this level of complexity, you could easily make it possible using couple of custom helpers.

CyborgMaster commented 10 years ago

Fair point. I'm all for keeping things as simple as possible, which is why ActiveRecord probably only supports one layer of layouts out of the box. In my opinion, people use nestive because they need more complexity than the out-of-the-box solution provides, so we are already in the business of "giving people enough rope to hang themselves with."

If extending partials was more any more than this 2-line change that I have in the pull request, I would be inclined to agree, but it's simple enough that it won't complicate the code base and the usage is literally identical to extending views or layouts.

I would love to see this feature included in nestive, but you are the author and therefore have creative authority over the gem, so if our views are irreconcilable than that's ok. If that does turn out to be the case, how do you recommend I proceed? I love this feature and we use it heavily in our project (It makes creating inherited angular-js templates a breeze), so I would like to keep it available to our team. Is there a way I can somehow make this an add on gem that plugs into yours, or do you recommend I maintain a fork of your gem (which I would rather not do).

Either way thanks for the awesome work, I love nestive and can't imagine my rails projects without it.

justinfrench commented 10 years ago

You could create a wrapper gem with the add-on functionality you need. It would have nestle as a dependency, and you'd install your gem in the Gemfile instead.

gem 'nestive-with-partials', :git => '…'
CyborgMaster commented 10 years ago

If @rwz does decide that this feature should not be included (I'm still holding out that he might change his mind), then that is the path I will most likely go down.

If I were to do that. How do you recommend I go about doing it? Would I simply require nestive and then monkey patch the extend method? Is there a better way to hook in?

CyborgMaster commented 10 years ago

@rwz, is there any way I could restructure this feature to make it fit better with your vision for nestive? Or barring that, do you have any suggestions on how I could factor out this functionality into an add-on gem?

rwz commented 10 years ago

Still very skeptical about extending partials....

I guess the best way to go for now would be to create a wrapper gem that depends on original nestive, requires it, and redefines extends method.

CyborgMaster commented 8 years ago

@rwz. I will take your advice and create a simple wrapper gem that patches the extends method.