{ height: 1%; } - Ruby on Rails and User Interface Design

CSS, UI Design, Ruby on Rails and cheese ... lots of cheese

AjaxScaffold 3.1.0 released

Posted by Richard White Tue, 18 Apr 2006 09:03:00 GMT

AjaxScaffold has been deprecated in favor of ActiveScaffold

Didn’t expect to see me again so soon… that makes two of us :)

  • Added support for defining your own scaffold columns in models by assigning an array of ScaffoldColumn’s to @scaffold_columns. If @scaffold_columns is not defined it will be created defaulting to columns for model.content_columns. I’ll explain this in more detail in the proceeding walkthrough.
  • Added cancel as an action. There was enough inline code in _new_edit.rhtml that I thought, for consistency, it should be refactored into a server-side method.
  • Refactoring: All common server side code has been moved into lib/ajax_scaffold.rb and namespaced more properly. include AjaxScaffoldUtil becomes include AjaxScaffold::Controller and include AjaxScaffold::Helper replaces include ajax_scaffold_helper
  • Changed CSS styling of sorted columns to a more subtle darker blue from the yellow.
  • Changed CSS and DOM structure (only slight changes) so support nested scaffolds
  • Fixed the fact that clicking on a page link wasn’t showing the loading indicator

The rest of this post will be a walkthrough a couple scenarios (Based on this demo) for customizing your scaffolds for model associations (belongs_to and has_many) and in the process explain how to customize the scaffold columns. At the end of this post is a fairly detailed description of how to migrate existing 3.0.x scaffolds.

I realize that a blog post isn’t always the best place to be reading reems of code: download the demo code

Handling associations from the belongs_to side of things

For the purpose of having a contrived scenario to base all this on I’m going to have two models: person.rb and pet.rb. A person has_many pets and a pet belongs_to a person (that’s right there are no strays in demoland). I’m going to assume that you know how to create a db migration file and generate scaffolds for pets and people and move straight into customization of these scaffolds. First thing’s first so lets define our associations in our models and also customize the scaffold columns (These are the columns you’ll see in the scaffold table, not to be confused with those on a create/edit form).

models/pet.rb

require 'ajax_scaffold'
class Pet < ActiveRecord::Base

  <strong>belongs_to :person, :foreign_key =&gt; "owner_id" 

  @scaffold_columns = [ 
      AjaxScaffold::ScaffoldColumn.new(self, { :name =&gt; "name" }),
      AjaxScaffold::ScaffoldColumn.new(self, { :name =&gt; "owner", 
        :eval =&gt; "pet.person.name", :sort_sql =&gt; "people.name" })
    ]</strong>

end    

models/person.rb

require 'ajax_scaffold'
class Person &lt; ActiveRecord::Base

  <strong>has_many :pets, :foreign_key =&gt; "owner_id" 

  @scaffold_columns = [ 
      AjaxScaffold::ScaffoldColumn.new(self, { :name =&gt; "name" }),
      AjaxScaffold::ScaffoldColumn.new(self, { :name =&gt; "pets", 
        :eval =&gt; "person.pets.collect{ |pet| pet.name }.join(', ')", :sortable => false })
    ]</strong>

end    

So besides adding the standard Rails belongs_to and has_many definitions I defined my custom column set by @scaffold_columns. ScaffoldColumn takes four possible options:

  • :name (required) : unique identifier for the column. ScaffoldColumn will attempt to figure out the other variables, if not defined explicitly by you, based on the assumption that this is the name of a content_column for this model. That is to say if you are doing a column for a basic attribute of your model you can just supply the :name and be done with it.
  • :eval : code that is eval’d to create the display value of the column. Eval isn’t the most performant call in Ruby but it is the most flexible since you can define whole blocks of code to be run to generate the display value for each column. You should note that I put pet.person.name not just person.name.
  • :label : the human readable label for the column (aka the header text).
  • :sort_sql : SQL statement to be used when this column is the sorted column. Note that you need to use the table name here, as opposed to the instance name you used with :eval, so people.name instead of person.name. If any of your sortable columns are from associations, such as with people.name in the above example, you need to add the model of the association to the :include option in the controller paginate call. Don’t worry if you don’t know what I’m talking about, I’ll do it just up ahead.
  • :sortable : speaks for itself. This is for those instances where there may not be any logically way to sort the column, such as with person.pets, or you just don’t want that column to be sortable.

Next we add the :include to the @paginate call in def component ...

controllers/pet_controller.rb

...

@paginator, @pets = paginate(:pets, :order_by =&gt; @sort_by, <strong>:include =&gt; 

:person</strong>, :per_page =&gt; default_per_page)

...

... add the drop down to the form …

views/pets/_form.rhtml

...

&lt;div class="form-element"&gt;
  &lt;label for="pet_person"&gt;Owner%lt;/label&gt;
  &lt;%= select 'pet', 'owner_id' , Person.find_all.collect {|p| [ p.name, p.id ] } &&gt;
&lt;/div&gt;

...

... and Viola!

Handling associations from the has_many side of things

For the other side of the coin, I’m going to generate a seperate controller (ruby script\generate ajax_scaffold Pet person_pets) for the pet model to use as the scaffold I’m going to embed into the person scaffold. You could say that this violates the DRY principle, and you’d be right, but there a number of small changes that need to be made to the embedded scaffold and I’d rather do it this way than using if/else on a parameter everywhere. Obviously I have the luxury in this contrived example of not having a lot of other code besides what was generated that will be repeated, if you actually have a fairly customized scaffold you might want to go with the latter route.

Create a link to editing pets…

views/person/_person.rhtml

...  

  &lt;td class="indicator-container"&gt;
    &lt;%= loading_indicator_tag(@options) %&gt;
  &lt;/td&gt;
  <strong>&lt;td&gt; 
    &lt;% pets_options = @options.merge(:action =&gt; 'pets') %&gt;
       &lt;%= link_to_remote "Pets", 
            { :url =&gt; pets_options, 
              :loading =&gt; "Element.show('#{loading_indicator_id(@options)}');" },
            { :href =&gt; url_for(pets_options) } %&gt;
  &lt;/td&gt;</strong>
  &lt;td&gt; 
    &lt;% edit_options = @options.merge(:action =&gt; 'edit') %&gt;

...

... create a view in which to embed the other scaffold. This will look a lot like _new_edit.rhtml so I’ll highlight the important differences …

views/person/_pets.rhtml

&lt;% if not request.xhr? %&gt;
&lt;table class="ajax-scaffold" cellpadding="0" cellspacing="0"&gt;
  &lt;tbody&gt;
&lt;% end %&gt;
&lt;tr id="&lt;%= element_row_id(@options) %&gt;" &lt;%= "style=\"display:none;\"" if 

request.xhr? %&gt;&gt;
  &lt;td id="&lt;%= element_cell_id(@options) %&gt;" class="update" colspan="&lt;%= num_columns 

%&gt;"&gt;

      <strong>&lt;h4&gt;Pets for &lt;%= @person.name %&gt;&lt;/h4&gt;</strong>

      &lt;% if request.xhr? %&gt;
        &lt;div id="&lt;%= element_messages_id(@options) %&gt;" 

class="messages-container"&gt;&lt;/div&gt;
      &lt;% else %&gt;
        &lt;%= render :partial =&gt; 'form_messages' %&gt;
      &lt;% end %&gt;

      <strong>&lt;% child_component_params = params.merge(:scaffold_id =&gt; [ 

@options[:scaffold_id], @options[:id], "pets"].join("-"), :parent_id 

=&gt; 
@options[:id]) %&gt;
      &lt;div id="&lt;%= child_component_params[:scaffold_id] %&gt;" class="ajax-scaffold"&gt;
        &lt;div id="&lt;%= scaffold_content_id(child_component_params) %&gt;"&gt;
          &lt;%= render_component :controller =&gt; '/demo/person_pets', :action =&gt; 

'component', :params =&gt; child_component_params  %&gt;
        &lt;/div&gt;
      &lt;/div&gt;</strong>

      &lt;p class="form-footer"&gt;
          <strong>&lt;% done_params = @options.merge(:controller =&gt; '/demo/person', :action 

=&gt; 'close_pets') %&gt;</strong>
          &lt;%= link_to_remote "Done",
            { :url =&gt; done_params,
              :loading =&gt; "Element.show('#{loading_indicator_id(@options)}');" },
            { :href =&gt; url_for(done_params) } %&gt;                                  
          &lt;%= loading_indicator_tag @options %&gt;
        &lt;/p&gt;  

        <strong>&lt;script type="text/javascript"&gt;
        Rico.Corner.round('&lt;%= child_component_params[:scaffold_id] %&gt;', {color: 

'#005CB8', bgColor: '#fff', compact: true});
      &lt;/script&gt;</strong>
  &lt;/td&gt;
&lt;/tr&gt;
&lt;% if not request.xhr? %&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;% end %&gt;

... create pets action (parallel in function to edit) and close_pets (indentical to pets) in the controller …

controllers/person_controller.rb

  def pets
    @person = Person.find(params[:id])
    @successful = true

    return if request.xhr?

    # Javascript disabled fallback
    if @successful
      <strong>@options = { :action =&gt; "pets", :id =&gt; params[:id] }
      render :partial =&gt; "pets", :layout =&gt; true</strong>
    else 
      return_to_main
    end 
  end

  def close_pets
    @person = Person.find(params[:id])
    @successful = true

    return if request.xhr?

    return_to_main    
  end

... create pets.rjs (parallel to edit.rjs) ...

views/person/pets.rjs

@options = { :scaffold_id =&gt; params[:scaffold_id], <strong>:action =&gt; "pets"</strong>, :id 

=&gt; params[:id] }
@view_options = @options.merge(:action =&gt; "view")

if @successful
  page.hide element_row_id(@view_options)
  page.insert_html :bottom, scaffold_tbody_id(@options), <strong>:partial =&gt; 'pets'</strong>, 

:locals =&gt; { :hidden =&gt; true }
  page &lt;&lt; "new TableRow.MoveAfter('#{element_row_id(@view_options)}', 

'#{element_row_id(@options)}');" 
  page.show element_row_id(@options)
  page.replace_html element_messages_id(@options), :partial =&gt; 'form_messages'
else
  page.replace_html scaffold_messages_id(@options), :partial =&gt; 'messages'
end

page.hide loading_indicator_id(@view_options)

... create close_pets.rjs (parallel to cancel.rjs) ...

views/person/close_pets.rjs

@options = { :scaffold_id => params[:scaffold_id], :action => "view", :id => params[:id] }
@pets_options = @options.merge(:action => "pets")

if @successful
  page.remove element_row_id(@options)
  page.insert_html :bottom, scaffold_tbody_id(@options), :partial =&gt; 'person', :locals =&gt; 

{ :hidden => true }
  page &lt;&lt; "new TableRow.MoveAfter('#{element_row_id(@pets_options)}', 

'#{element_row_id(@options)}');" 
  page.remove element_row_id(@pets_options)
  page.show element_row_id(@options)
  page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');" 
  page.replace_html scaffold_messages_id(@options), :partial =&gt; 'messages'
else
  page.replace_html element_messages_id(@pets_options), :partial =&gt; 'form_messages'
  page.hide loading_indicator_id(@pets_options)
end

Now we’ve finished modifying the parent person controller and assorted actions we’ll move on to making our adjustments to the person_pets controller and actions. First change the helper for the embedded scaffold to override showing all defined @scaffold_columns and show only the name column …

helpers/person_pets_helper.rb

...

  def scaffold_columns
    [ Pet.scaffold_columns_hash["name"] ]
  end

...

... add the :conditions to @paginate in def component of the person_pets_controller so that it only displays those pets associationed with the parent_id passed in …

controllers/person_pets_controller.rb

...

    @paginator, @pets = paginate(:pets, 
      <strong>:conditions =&gt; [ "owner_id = ?", params[:parent_id] ],</strong>
      :order_by =&gt; @sort_by, :per_page =&gt; default_per_page)

...

... add the parent_id when creating a new pet …

controllers/person_pets_controller.rb

...

  def new
    @pet = Pet.new
    <strong>@pet.owner_id = params[:parent_id]</strong>
    @successful = true

...

... add the owner_id as a hidden tag into _new_edit.rhtml …

views/person_pets/_new_edit.rhtml

...

 &lt;%= hidden_field_tag "scaffold_id", @options[:scaffold_id] %&gt;
 <strong>&lt;%= hidden_field "pet", "owner_id" %&gt;</strong>

...

... and Viola!

Migrating

Here are a bunch of quick notes I made to help those of you upgrading your current 3.x scaffolds to 3.1. Go ahead and generate a 3.1 scaffold in a new directory to use as a template for copying code into older scaffolds.

Delete the following:

  • helpers/ajax_scaffold_helper.rb. Any customization you have made to this or the ajax_scaffold_lib can be migrated into ajax_scaffold.rb under the proper modules (should be fairly straightforward which ones)
  • lib/ajax_scaffold_lib.rb
  • lib/content_column_patch.rb. Remember to take the require ‘content_column_patch’ out of your environment.rb if you were using this

Changes to controllers:

  • Change include AjaxScaffoldUtil into AjaxScaffold::Controller
  • You can add in the code for making cancel a server side action, but since that’s more of a lateral refactoring move there is no real reason you need to. If you want though just copy in the controller cancel method and the cancel.rjs from a new scaffold and modify _new_edit.rhtml so that the cancel link looks like that in a new scaffold as well.
  • Your :default_sort values in component_update and can no longer be “id”. It either should be nil or the name of either a content_column or a column you’ve defined in @scaffold_columns
  • Change the @sort_by line in def component to:
    @sort_by = current_sort(params).nil? ? "people.id asc" : 
    
    Person.scaffold_columns_hash[current_sort(params)].sort_sql  + " " +   
    
    current_sort_direction(params)
    

Changes to helpers:

  • Change include AjaxScaffoldHelper into include AjaxScaffold::Helper
  • Put these two methods in your helper (num_columns should already be in there just overwrite it) making sure to change Customer out with whatever the name of your model is:
      def num_columns
        scaffold_columns.length + 1 
      end  
    
      def scaffold_columns
        Customer.scaffold_columns
      end
    

Changes to models:

  • Add require ‘ajax_scaffold’ to your existings models

Changes to views:

  • Move the class attribute that was on the <TR> tag in _new_edit.rhtml onto the <TD> tag instead.
  • Add class ‘form-footer’ to the <p> at the bottom of _new_edit.rhtml that contains the submit button and cancel link
  • Swap out _column_headings.rhtml with that from a new scaffold. Be sure to set the :controller parameter correctly though
  • Swap out lines 6-10 of _widget.rhtml with code from a new scaffold

Closing Thoughts

I hope this update helps those that were struggling with modifying 3.0 scaffolds to work with associations. I’ll have another writeup later on today about some other useful code/modifications you can do to your scaffolds. Thanks for the patience and feedback, keep it coming.

Comments

  1. Phil Wilson said about 3 hours later:

    You are the hardest working man in rock n’roll today, as the saying goes. From the quick look, it’s golden. The examples are welcome additions as well. Excellent stuff.

  2. Saboteur said about 4 hours later:

    That is, without doubt, the best think since sliced bread. :)

    One question : on the demo if you update a person, and change their name, is it possible to update the owner in the pets without a refresh ??

    Excellent work – just mad my life a whole heap easier.

    A donation will be comming your way.

  3. Peter said about 4 hours later:

    Richard, you are amazing. I have been putting off my rails project until 3.1, now I am really looking forward to getting going armed with this release. May I ask, is it safe to assume that near future releases will be bugfixes rather than major rewrites… ? I just sent you a small donation, if I actually finish this project with the scaffolding I’ll send ya some more. keep up the good work!

  4. Dean said about 5 hours later:

    Holy Cow MAN!!!! You definately are working hard on this. I hope that you are starting to see some real compensation for this… Both with your donations and other ways (Personally I’d love for you to be able to do presentations/etc too and get paid for those. You definately are what rails is about) As for me.. I’ll be on that list of – I’ll be dropping you a donation when i can take this from playing to being paid for it.

  5. Geoff said about 5 hours later:

    Wow, amazing work. I had moved some of my scaffolds to a different with 3.0.4 on it after having built them on 3.0.2 and some bits didnt seem to work. Wasn’t overly looking forward to rebuilding the scaffolds especially my customised row formats, but my god the @scaffold_columns bit is a fantastic piece of work. I rebuilt the scaffolds just how I had them before in about 3 minutes where it took me 10 minutes to get them how I wanted them when I did them previously.

    Very nice work.

  6. Eden said about 5 hours later:

    Richard: This looks amazing! You always end up delivering way more than you talk about. I will be busy today getting ready for a demo, but I will be sure to post (perhaps in the forum) about my experiences with 3.1 in the next day or two. Excellent work and thank you again.

  7. Pat said about 6 hours later:

    I’ve been following the project for a couple of weeks. I decided to learn ror before using this. I finally decided to work on this yesterday and what do I see today? holly cow :) you’re the man! I agree with Saboteur, it would be great if you could give us a hint on how to update the owner in the pets without a refresh. thank you again and very nice work

  8. Pat said about 7 hours later:

    excuse my ignorance, but could you please give me the details about the sql table to create? is it: “people”: id,name,pet_id “pets”: id,name,people_id ? or id,name,owner_id?

    thanx in advance

  9. Stephen Carr said about 7 hours later:

    You really are the man, release after release, fixing stuff, making it better. You are a hero, thanks so much for sharing.

  10. Pat said about 8 hours later:

    I think you meant views/_form.rb instead of views/pet_controller.rb for adding the drop down to the form :) by the way I think I found out, it’s: “people”: id,name and “pets”: id,name,owner_id right? :)

  11. Richard White said about 8 hours later:

    Saboteur: No its not possible to update the other scaffold, at least not out of the box obviously. I’m going to put up some code later on today that explains how to make your scaffolds refresh in the background. But managing dependencies between scaffolds is a nasty web that you would be best to avoid.

  12. Richard White said about 8 hours later:

    Pat: For the pet SQL table it was id, name, owner_id. That’s why you see the foreign_key defined in the belongs_to and has_many relationships.

  13. Richard White said about 8 hours later:

    Peter: I am going to address that issue later on today but yes everything from here on out should be bug fixes and how you can add onto the scaffolds and customize the generator yourself. Thanks for the donation btw, its greatly appreciated.

  14. Eden said about 9 hours later:

    A couple of points from my experience so far:

    1. “generate a seperate controller (person_pets) for the pet model ” literally means “ruby script\generate ajax_scaffold Pet PersonPet”.

    2. The section that says ”... create close_pets.rjs (parallel to cancel.rjs) ... views/person/pets.rjs” should actually say ”... create close_pets.rjs (parallel to cancel.rjs) ... views/person/close_pets.rjs”

    Once I get more familiar with the whole embedding process, I am going to try to figure out a way to extend the generator so that embedding can be done at runtime with an option of some kind.

  15. Eden said about 9 hours later:

    Whoops, on second thought, point 1. above should be:

    1. “ruby script\generate ajax_scaffold Pet person_pets”

  16. Richard White said about 10 hours later:

    Eden: That’s what I get for doing this at 5 in the morning. I’ve updated the article to fix these inaccuracies. Thanks for pointing them out.

  17. Eden said about 11 hours later:

    Richard: At least you don’t make the sheer number of mistakes as I do during the day!!

    “embedding can be done at runtime with an option of some kind.”

    Should have been “embedding can be done at scaffold generation with an option of some kind.

  18. Kevin said about 11 hours later:

    Have a couple questions about the id field. The id column isnt showing up in my ajax scaffolds. First, is it possible to show and be able to modify the “id” column as well in the ajax scaffolding Second, what if my “id” column isnt an autonumber column? I noticed in generating a scaffold for one of my tables when I add entries it inserts the number 0 for new records in the id field, when I actually use it to store unique employeeid numbers… One more question, In another table, my id field is a varchar and it causes an RJS error when I try to create a new entry or edit an entry.

    I’m fairly new to ruby on rails so maybe I just dont have some things set up right as well.. Thanks for any help.

  19. Eden said about 12 hours later:

    Richard: Let me know if you prefer to get this feedback in the bug tracker or the forum.

    I think the javascript disabled code in the pets method in the people_controller should change from:

    # Javascript disabled fallback if @successful @options = { :action => "pets", :id => params[:id] } render :partial => "pets", :layout => true else return_to_main end

    To:
    # Javascript disabled fallback if @successful @options = params render :partial => "pets", :layout => true else return_to_main end

  20. RobY said about 12 hours later:

    Richard, Once again you’ve blown me away. I just plugged in the new version and it looks fantastic! And future migrations should be a bit easier as well.

  21. RIch said about 12 hours later:

    Nice job. Plus the example really helps. One small issue, If I click on the pets button on the right multiple times before it has a chance to load I can get multiple subgrids to showup.

  22. Kevin: said about 12 hours later:

    Kevin: Are you overriding the defaults in your models for compatibility with a legacy DB? If so, it may take a lot of work to get any sort of scaffolding (built in Rails or ajax_scaffold) to work since they depend on a Rails schema.

    If you are using a new database and build the schema how ever you like, you should consider implementing it the “Rails way”. If you aren’t sure what I mean, check out the resources here: http://www.rubyonrails.org/docs. The Agile Web Development book is a great resource when you are just starting out (actually, I still use it almost every day myself).

  23. Eden: said about 12 hours later:

    Kevin: Not sure how my response to you ended up with your name, but the response above this message is from me.

  24. RIch said about 12 hours later:

    Kevin, I’d second what Eden said. Especially if you are new to rails, try to stick to creating databases the ‘rails’ way. We have an existing application with a different database and made the choice that it will be easier to change the database and take advantage of many of the things rails can do easily rather than continue to code in PHP.

  25. Stephen Carr said about 13 hours later:

    In a dropdown list how do I get it to sort in alphabetical order? I am sure this is 101 stuff but I have to start somewhere. Here is my select code:

    <%= select ‘journey’, ‘sub_station’ , Station.find_all.collect {|p| [ p.name, p.id ] } %>

  26. Richard White said about 13 hours later:

    Stephen: You would want to insert a sort() call in that method change before the collect. At that point you probable want to refactor that code into a method in your object (ex: Station.options) and just call that instead.

  27. Pat said about 13 hours later:

    just to be sure, the right way to generate the scaffold for people is like this: “script/generate ajax_scaffold Person person” and for pets “script/generate ajax_scaffold Pet pet”? thanx in advance Pat

  28. Richard White said about 13 hours later:

    Eden: Actually it explicitly sets the action to “pets” since the same method is called from close_pets but we still want to render the pets partial.

  29. Richard White said about 13 hours later:

    Rich: Yeah your right it would cause multiple subcomponents to popup… probably should put a check for an existing pets subcomponent before the page.insert in the RJS

  30. Eden said about 13 hours later:

    Richard: Do you have any input into nesting components two levels deep?

    Example:
    Suppliers
    -> Manufacturers
    —>Facilities


    I seem to have it working, but my “done” link isn’t working just right and I get some cool RJS popup errors.

  31. Pat said about 14 hours later:

    ok I’m getting desperate, I tried for hours now and I always get the same error. I follow all the step and everything works fine except for when I click on Pets (next to Edit Delete on the people box). I enter an infinite loop and nothing happens. if one of you would be kind enough to have a look at my code http://p80.free.fr/peeps.zip I would really apreciate. Thanx in advance. Pat

  32. Stephen Carr said about 14 hours later:

    Thanks Richard, one more question, can you quickly just put the code into context? I am trying this and for reasons that are probably obvious to you (and others, feel free to jump in) it is not working: <%= select ‘journey’, ‘station_id’ , Station.find_all.sort.collect {|p| [ p.name, p.id ] } %>

  33. Richard White said about 17 hours later:

    Stephen & Pat: Can you guys start threads on the scaffold forum for these issues and we’ll go from there.

  34. xurde said 1 day later:

    OMG! Associations are gorgeous! I was thinking about it since i saw 1.0 version. Congratulations!

  35. Sergej Müller said 1 day later:

    Hi Richard, this is definitely a masterpiece of work. Although I love this scaffold, there is a little thing that I dislike. According to MVC the model should not know anything about the kind of views which observe it. So the configuration of the columns inside the model is a litte bit unclean. I’m very new to Rails, so I don’t have a suggestion yet but maybe someone out there has a good idea. Nevertheless great job. Greetings from Germany.

  36. Richard White said 1 day later:

    Sergej: I would agree with you and I actually tried to come up with a way around that. My main problem is that while I only needed access to the column array from the views I need access to a hash of those column values in the controller. To the best of my knowledge the only thing in Rails that has visibility in both is a model. The one scenario where it becomes actually more of a limitation, than just being simply “unclean”, is when you have multiple controllers for the same model and you don’t want to have the same displays for all controllers. My concession to this problem was to move the columns displayed into a helper which could then be overrode. So if it helps you sleep better think of the code in the model as being a definition of possibilities and the actual display is driven by a helper method :)

  37. Daniel said 1 day later:

    Richard: I think that what you have done is absolutely superb. Reading Sergej and your comment made me wonder and analyze a bit more my particular needs. I did come to realize that it would be great if the dependency on the model for specifying the scaffold columns would be moved to a controller specific or helper module. The reason being is that I do (and I’m sure others) have a need to reuse the model on different views and specify different columns. I guess you could always get the same thing from the controller by calling Model.content_columns or something like that. I guess somehow let AjaxScaffold::... know what the model is :). Also, the same way that you specify the scaffold columns for the list, it would be nice to specify them for the new/edit forms. Lastly, my typical applications use large tables and the has_many/belongs_to associations you have here are good. However, if I use your belongs_to drop-down approach, my forms would be too heavy and hard to search for the right value. What I would like to see is a text_field_with_auto_complete instead of the drop down. Would that be feasible? I’m very impressed to how this generator is maturing and how quickly it is doing so. I once thought that rails was evolving very quickly, but now, this is just blowing my mind. Great work!!!

  38. Daniel said 2 days later:

    Richard, what I meant by “improving” the belongs_to association for larger look up data sets is perfectly seen in SugarCRM. If you wish, please login as the demo user (will/will) at http://demo.sugarondemand.com/sugarcrm_ent/index.php?action=Login&module=Users then click on Create Account on the left hand side shortcut. Then click on any of the Select buttons on the form. You will see that it pops up a “search”/list form where you can filter by criteria and then select the record you want and simply puts that in the “parent” form. I did something similar a long time ago with a lot of ugly JS code. Maybe there is a cleaner and more elegant way of doing that. What I had actually done was the form would query the related lookup table and if the number of records exceeded a threshold, it would pop up the window, otherwise, it would put a dropdown. That way for smaller data sets you can simply select from a dropdown

  39. Daniel said 2 days later:

    Just one more comment :). If you notice, these fields with the Select button also act as text_field_with_auto_complete, which is pretty cool. That’s exactly what I was looking for. The question I have is (sorry if that doesn’t belong here), how can you adjust the size of the auto_complete drop down. In Rails, it seems to default to the size of the actual text field. In theirs, it’s bigger than the text field.

  40. conradwt@gmail.com said 4 days later:

    Hi, I was wondering, should there be more directories for the demo source? Or is there some steps that I should perform prior to executing the source?

    Thanks,

    -Conrad

  41. Richard White said 4 days later:

    Conrad: All that is included in the demo are the relavent files that were created from the scaffolding. It assumes you would drop it into an existing Rails application (or just the skeleton of one).

  42. Richard White said 4 days later:

    Daniel: Sorry for the very tardy response, I think the size of your comments overloaded my buffer or something :) If you read my response to sergej I didn’t really see an alternative to putting the column definitions in the model given my knowledge of how Rails works. You can, as I mentioned, pick and choose the columns you want in a specific view by overriding scaffold_columns in the helper (I did this in the 3.1.1 writeup). Also, I hate dropdowns as well. My original roadmap for the scaffolding had me, probably at about this point in development, turned inward and focused on building better forms. Text autocomplete instead of drop downs, use the Datebox engine (or some less bloated version thereof) instead of the horride default rails date pickers and so on. I wonder what the community as a whole would think of adding those improvements.

  43. Kaveh said 4 days later:

    Richard: I see a couple of typos in your demo writeup (I know you wrote it 5 am, and I’m not complaining) Right above the Voila! and the screenshots you mention that you need to add a hidden tag to the _new_edit.rhtml file but then for the accompanying code cites “views/person_pets/_form.rhtml” which I’m pretty sure isn’t where it goes. Also as for the hidden field itself, you left off “_tag” from the “hidden_field_tag” Rails method. Just above that whole section where you mention that you need to add parent_id to the person_pets controller, your instructions read “add the parent_id to the ??? when creating a new pet”. I think you are missing something where I inserted the question marks. —Thanks again for all your hard work on this

  44. Kaveh said 4 days later:

    On a related note, I’m having some issues with the whole person_pets side of things where the I get a RJS object error and the form doesn’t seem to successfully update or create a pet. Anyway, I’ll post the details over on the forum, I just hope its not a result of misinterpreting your instructions.

  45. Richard White said 4 days later:

    Kaveh: Well you got 1 out of 3 on calling me out :) 1) hidden_field is another rails helper method similiar to BUT not the same as hidden_field_tag, so that was on purpose 2) The hidden field does actually go into the form of for the embedded scaffold 3) Yep, wh00ps… that has been fixed. Thanks

  46. Kaveh said 4 days later:

    Richard: Thanks for the quick updates. As for issue #2 I’m still a bit confused. You mention that it should go in the file _new_edit.rhtml which is where the code snippet comes from (I recognize that scaffold_id hidden field) but the code is label (with the grey background) indicates its in the file “views/person_pets/_form.rhtml”. Ironically enough, I don’t think it matters as long as you put it in one of those two locations since it ends up in the “form” either way. Sorry for splitting hairs :-)

  47. Richard White said 5 days later:

    Kaveh: I stand corrected, I apologize for ever doubting you :) Thanks.

  48. Doug @ Straw Dogs said 18 days later:

    This has becoming increasingly fantastic. It was good when it was first released, then it had improvements and for a while I never checked back. Now I’ve come to update and start using it for another project and its grown to be an epic piece of work.

    Really impressive.

  49. Ketan Pandya said 26 days later:

    Richard, New to this work but looks great. Question on the demo – if I add a new pet to a person how would I refresh my pet list to show a new has been added and similar fashion if I remove a pet how can I dynamically refresh the persons pets to reflect it no longer exists?

  50. Richard White said 28 days later:

    Ketan: Post that question over on the forums, I’m working on the answer.

Trackbacks

Use the following link to trackback from your own site:
http://height1percent.com/articles/trackback/35

(leave url/email »)

   Comment Markup Help Preview comment