I'd like a good source on how to set up controller actions and forms for creating a resource inside the view of another resource that it belongs_to...
Set up your controllers as you would normally. You'll need to use the nested attributes feature of Rails. This enables you to create children objects at the same time as creating their parent using one form.
This is my go-to link for nested attributes. The only change you will need to make if you are running Ruby 1.9.2 is in the setup_person helper. returning has been deprecated so you can change it to:
def setup_person(person)
person.tap do |p|
p.children.build if p.children.empty?
end
end
In typical Rails style, this will just work using standard controllers for each of your resources.
Other links
http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
http://jeffperrin.com/2009/06/04/rails-nested-forms-and-collection_select/
I don't have a web source that documents what I usually do, but I created a gist that documents what I do most often here: https://gist.github.com/900241
The premise of the gist is that you have a project model with many project roles, and you want to edit many project roles in the project form. This is pretty much the classic accepts_nested_attributes_for scenario, and just about any page that talks about it will give you a decent writeup. The problem is, the solutions I've seen have always involved some seriously messy obtrusive JavaScript that escaped your entire form view and threw it in the onClick method of a link. I recently came up with a cleaner unobtrusive approach using jQuery templates.
You don't have to do a thing to your ProjectsController when you move to a nested model. Everything Just Works at the controller level, and you don't even need a ProjectRolesController. (This is why I didn't bother including them in the gist.) At the model level, it's just standard accepts_nested_attributes_for. Where it gets interesting is in the view.
The project form has two form_for blocks: one rendering a jQuery template, and another rendering the project roles form. The jQuery template in turn just renders the project roles form (mmm DRY!), but from within a <script> tag, and with a blank project role. Because the form is within a script tag, it won't get submitted along with the project form, and because the script type is "text/x-jquery-tmpl", this is completely valid markup.
When the user clicks on "Add a Project Role", it fires some jQuery that takes the form within the template, replaces the index with the current date (this is all so this project role can be uniquely identified), and appends it to the end of the project roles section of the form.
When the user clicks on "Delete" next to a project role, it checks to see if this project role is a new record, and if not, it appends a "_delete" hidden field to the end of the form. In either case, it removes the project role div from the DOM.
Related
I want to create several instances of same model form a single form. And more importantly, the number of instances aren't known before form rendering.
I've seen several tutorials of this kind, but unfortunately those didn't suit my need. I've seen Ryan bate's nested form tutorial. But I'm not creating nested form. I've also seen some tutorials, which do create multiple objects, but the number of object's are all known in those cases. One of the tutorial is here - http://archive.railsforum.com/viewtopic.php?id=717
User will click a button and a new set of fields for a new object will be inserted just like the nested form demo from ryanb.
Here is a mockup of what I want. It's basically a very small form fit into a single line.
As i understand you need cocoon gem it allows to add form fields
It sounds like you may need to reach beyond Rails views and utilize javascript to dynamically render more "partials" when the user decides to add more fields. Something like this: Adding input elements dynamically to form
If you want to keep your view rendering logic in rails, you could make an AJAX request to your application, have it return just a partial's worth of html back, and insert the response html into your dom.
I'm trying to make a drop down menu that has a bunch of items (Amelia, Cerulean, Cosmo, Cyborg, Flatly, Journal). Each of these items represent a css file.
When one of them is selected I want my website to take this selected css file and apply it to the website.
I would like the drop down menu to interact with jquery, meaning when a item is selected jquery takes over and makes a asyn/ajax call to some mvc actionresult.
By the way I'm using MVC 5.
I hope someone can help me sketch the initial groundwork.
I've implemented this in my application.
I'm not sure what to tell you though. It's easier when we have an attempted solution to fix.
Here's an overview of how mine works:
I have created a controller called SharedController. The purpose of it is to contain various actions that render common actions. All of the actions are considered ChildActionOnly.
My _Layout uses RenderAction to render the action NavbarPartial which is in my SharedController.
More importantly the Navbar partial then uses RenderAction to render the action ThemeListPartial. This action is responsible for getting a list of available themes. The list of available themes is determined at applications startup. I've created a ThemeFinder class and ThemeRepository class that are responsible for finding and storing themes. The ThemeFinder finds themes by expressions that you give it. In a new class called App_Start/ThemeConfig I've given it only one expression - "~/Content/themes/{name}.bootstrap.css". This will find all themes with that naming convention in that location.
My razor code will take the ViewModel and display a dropdown menu in the navbar.
To get the themes to change my dropdown menu contains an AJAX link to an action called SaveTheme in ThemeController. This action takes a theme name as a string and tries to save it in a cookie for the user.
If the theme is found and saved successfully, the action responds with a success message.
jQuery then changes the theme by finding the associated link attribute and changing the HREF contents to the new theme. It knows the new theme relative URL because I have it stored in data attributes.
I completed this before I made the switch to AngularJs. The one thing I plan to go back and change is to cut out as much (maybe all) jQuery as possible and replace it with better code.
I'm writing a rails app that allows users to delete records of various sorts. After pressing a delete button, I'd like to show a confirm dialog using bootstrap. I'd like to use this same dialog in several of my views, so I'll need to include the same HTML snippet in most of my pages.
I'm new to rails and I'm still learning the conventions. Can anyone suggest the best (or standard) place to put the dialog code? Should it be a partial in views/layouts/_confirm_delete_dialog.html.erb, should it go inside application.html.erb, or should I put it somewhere else?
Thanks in advance for your advice,
D.
Within your Views folder, you can create a general folder (called whatever you want). If you have a variable that you need to pass through to the general layout, you can definitely do so, but you will want to make sure that the information that you're passing through doesn't cause conflicts with the fields pulled from the model. For example, if you have two models and one has a public field but the other doesn't then you will not want to have a generic message that is using the public field. However, something like the created_at or updated_at would be okay.
You would use a code similar to,
<%= render 'general/simple_message', :f => f %>
In your views folder, you would have a directory called general and a file called _simple_message.html.erb.
I put the begin/end form statement in the layout page so that I don't have to repeat it on several pages. Below's a simplified version of the code.
#using(Html.BeginForm())
{
#RenderBody()
<input type = "submit" name = "nextButton" value = "Next-->" />
}
Things are working well. Unfortunately, I have a page that has several "Delete" buttons. I want to generate a form for each delete button so that it can send the id of the item to delete back to the controller.
Can I do that knowing that there's already another form on top of that?
Thanks for helping
As Mrchief says, the the HTML specs forbid nested forms. Since MVC just generates standard HTML, you have to work withinn the framework of the spec.
Why not just create two master layouts, and use the form based one most of the time, but use one without a form when you need more control over the embedded forms.
This is the why you should really only use forms exactly where they are needed, not just everywhere.
Do note that nesting of forms is not allowed as per the W3 specs
Every form must be enclosed within a FORM element. There can be
several forms in a single document, but the FORM element can't be
nested.
There is an interseting article about caveats of nesting forms here.
In this case, it is better to generate a form for each button instead of having a single form.
ASP.Net web forms restricted you from having multiple forms on the page by using runat=server attribute (and the framework ensuring that only one per page was allowed). MVC forms are pure HTML so you can have multiple of them.
Before starting, I do have a very particular question and if you want to answer it go straight to the end. But I do welcome comments and advices hence the lengthy post.
OK, we deal with a lot of forms and some of these forms are quite lengthy and have many fields. We also have a requirement - in addition to top level fields - to be able to have variable number of repating rows - as we call them. For example, let's think of a customer which has name, surname and age while it can have zero or many addresses (say 0 to 10) so the user must be able to add or remove contacts from the form while filling it in. So typically user gets and "Add" button to add more addresses and next to each address, a delete button. Potentially there could be more than one repeating section in the same form but I am not going there. The point is, because of legal and historical reasons, all the forms must be saved at once so while the forms can be edited, we cannot accept a half-filled form and have another page for users to add and remove addresses, e.g.
I am using ASP NET MVC 2 (strongly typed views with a single generic controller) with client side validation and heavy jquery scripting for flashy features. We are probably going to migrate to ASP NET MVC 3 very soon and I am already playing with 3 for finding a good solution. These addresses are defined on the Model as List<Address>, e.g.
I currently have a working solution for this issue but I am not satisfied with it: I have an HTML Helper that names the add or delete buttons and a bit of JavaScript to disable validation and allow the form to be posted back (even invalid) and since I can find out the name of the button that was clicked, I have all the necessary logic to handle add or delete and works really well.
But I am posting back and the form is reloaded and I am looking for an aletrnative solution. Here are what I can do:
Do everything in the client side. "Add" button will clone one of such addresses and "Delete" button will remove() the element. I only have to rename the indexes which I have done. We were using jquery calendar and it was breaking on the new elements which I have also fixed. But the validation is not working which can probably work with ASP NET MVC but this solution looks like a very brittle one - a house of card which looks great before you add another card.
Post the whole page usin Ajax and then load it back again: This is probably better than my current solution but only slightly.
Use ajax to post the form and get back JSON and use the data to build the elements or remove them: Again a house of card because of extensive client side scripting
Serialize the form and post using Ajax to a particular action and get back only the repating section (as a partial view). The action on the controller can be reused and called from the view itself to return the partial view
OK last one is the one I am working on but there is an issue. ASP NET MVC 3 with unobtrusive validation works only if the form is engulfed in a BeginForm() while my top level view has a BeginForm() but not my partial view. It works well when I call it from the view but not on the ajax call to get just the repeating section.
(Question)
So is there a way to tell ASP NET MVC 3 to spit out validation data atttributes regardless being in a BeginForm() block?? To be honest if this is not a bug, this is definitely an important feature request. I have in fact used reflector to disassemble the code and the condition seems to be there.
Short Answer:
Add this to the partial view:
if (ViewContext.FormContext == null)
{
ViewContext.FormContext = new FormContext();
}
I don't think it is possible using the default unobtrusive libraries supplied. If you look at jquery.validate.js and jquery.validate.unobtrusive.js it looks like it only validates what is inside the form.
There's a few posts about it if Googled and a few work arounds.
I had a similar issue (although much simpler) where I had a validation summary at the top of the page and multiple forms but the unobtrusive javascript would only populate the view summary if its inside the form (jquery.validate.unobtrusive.js line 39 if interested...).
I'm not sure if the validation library is extendible but most things in jquery are so that might be an option if you want to go down that road.
As far a possible solution to your problem I'll put in my 2 cents for whats its worth.
You could have two actions that are posted to. The first action is you post your model with no js validation and all validation is handled in the code - this will catch all user with javascript turned off.
Your second action is you serialized the model. In mvc 3 using the Ajax.BeginForm has an AjaxOption for Url where you can specify an action for the jquery to call (where it serializes the form form you and you can decorate your action with your strongly typed model). Here you can check the model and return a json result and handle this in the javascript.