- Read Tutorial
- Watch Guide Video
We're moving right along in our forms section and in this guide and probably in the next one because I think this is going take to long for one single video but we are going to start to integrate some javascript form helpers. And specifically, we are going to integrate the cocoon gem. Cocoon is an incredibly powerful and very popular gem so there is a good chance that you will be using cocoon not just in this app but along your rails development journey. You're going to be using this one quite a bit and it's because it is very helpful if you read the description you can see that it is an unobtrusive nested forms handling using jQuery. Use this to discover cocoon heaven. So that is one way of saying it. Another way of saying it is you can use get cocoon to have some very cool form helpers so that you can have javascript elements in your form. So I'm going to start the rails server.
rails s
Now if I come to our app which is right here now there are a few issues with our current portfolio form. So if I hit new you can see that we have three items and these are all functional and this is for our nested technologies. So the way we have it is that a portfolio item can have as many nested technologies as we want which is the right behavior. But right now we are hard coding this what I want is the ability to simply have one additional item here one form element or a little link that says add a new technology and then for it without refreshing the page just adding dynamically a new form filled here. And the way that you do that is with javascript because javascript will allow us to create these without having to go communicate with the server it can all happen on the client side. And then when we hit save portfolio item then it's going to wrap all of the technologies up that we create and it will be saving it for us in the database. So that is what we're looking to do now. Also, I found a little bug if you notice you click on the show page for my new portfolio and look at the image here you can see that we have a little bug here where our thumbnail is being shown as the main image.
So we're also going to fix this so I'm going to change thumb_image
to main_image
(inside of portfolios/show.html.erb
and have I told you how many bad things can happen when you copy and paste. It is because it's very true. If you hit refresh. Now you can see this is working. So we're good on that side. I just wanted to implement that before we did anything else.
Now to install this come into the cocoon gem page which I'll include in the show notes and come to sublime and let's open up the Gemfile
coming all the way down the bottom put cocoon there
gem 'cocoon', '~> 1.2', '>= 1.2.9'
and hit save. Now we can run
bundle install
While that's installing. Let's open up the documentation. So right here the github page has all of the necessary information that we're going to need the installation for cocoon is shockingly simple. So all we have to do is actually once we have installed the gem we simply call //= require cocoon
from our javascript file. Make sure that installed it all did. So now I can say
rails s
to start the server up. And now let's come to our application.js
file and place it right underneath //= require html.sortable
, hit save and that is literally all we have to do to install cocoon.
Now obviously we have to implement some things inside of the HTML pages but that is the basic integration in terms of having access to the libraries. So the first thing we're going to do is let's open up our portfolios/_form.html.erb
. So this has our title, subtitle, main image, thumb image and then down below it has all of the technologies used. Now, this also has our fields_for
and has all of this good stuff here. Now the first thing we need to do is we need to make sure that we're not getting that weird hard-coded behavior. Where we have three technologies created for each item. So I'm going to open up the portfolios_controller.rb
and we can simply remove this call for 3.times { @portfolio_itm.technologies.build }
(on line 24) because we're now going to be using javascript in order to accomplish this for us I'm going to keep this open because there is some more work we have to do on it. But now what we can do is we can update this form so we're not going to get rid of it but we are going to refactor it so it can be used and we're going to use a partial that's going to manage this entire process for us. So first I'm going to rip out all of this code here.
And also let's see right here we're going to keep this
and we're going to put a partial call inside of it. So if I put some embedded Ruby I am going to say <%= render 'technology_fields', f: technology_form %>
and then technology fields and the naming is important here. So technology fields and then we're going to pass in because this is a partial pass in a local variable off:
for form. But we need to let it know which value it's going to use so it's going to use the technology_form
value.
So just to kind of reiterate what's going on right here we have our fields for technologies and inside of us. We have our technology form so this is a ruby block and this is a block variable that gives the form the data it needs to have some knowledge about what fields it should have and some knowledge about the model it's associated with which is the technologies model and then it's going to render this partial and it's going to make the variable f
available. So with all of that in place I'm going to create a new file inside the portfolios
directory, hit save and this is going to be _technology_fields.html.erb
and now I can just paste in what I ripped out.
<div class="form-group"> <%= technology_form.label :name %> <%= technology_form.text_field :name, class: 'form-control' %> </div>
So inside of this what we're going to have are a few things we already have our technology_form.label
but remember that I got rid of technology form and now we have this f
variable so we need to pass that in. So instead of technology_form
here it's going to be f.label
and f.text_field
and everything else is going to be able to stay the same.
<div class="form-group"> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> </div>
Now the next thing we need to do is we need to update and add a link that is going to allow us to create a new one so I'm going to just create a new div. I'm not gonna give it a class or anything like that I'm going to say <%= link_to_add_association 'Add Technology', f
and this is a special method provided from cocoon and one quick thing, f
is not related to the f:
variable passed to the partial that one is local to the block. This one is related to the entire form. So we're taking that f
and then we're going to say :technologies
as a symbol and all this is doing is it's taking the technologies and it's mapping between these two things. So when you say link_to_add_association
it's going to say bring me a new technology field.
<div> <%= link_to_add_association 'Add Technology', f, :technologies %> </div>
OK, I'm not sure if this is everything we need but let's start it up and let's see if this gives us at least something. So let's go click on one of these (portfolio items). And this is one that has no technologies that's good and let's say edit. Ok, good. So no errors. So far so good. Now if I click on Add a technology, look at that. Now I can say "Rails"
and if I click it again I have another one. So that's pretty cool. I can say "Ruby"
. Now if I click on Save Portfolio Item
and come back here. Let's see if this is working or not. So if I click on this (Jordan is referring to the portfolio item that he added the technologies to). There we go, Rails and Ruby. So this is working kind of, but not totally yet. So this is giving us kind of what we need but if I come back here(to the portfolio edit page). One thing that is definitely missing is the fact that if I want to remove one of these items I don't have the ability to do that. So how exactly can we do that part of it.
Well, that starts to get into a little bit trickier territory. Nothing too crazy and that's what we're going to walk through now. And that's also why I kept the portfolios_controller
and we need to also open up our portfolio.rb
model. So first we need to update the model and right under technologies we need to add a new item here and we're going to say allow_destroy: true
which essentially is saying that we want to give permission to our portfolio to delete the nested attributes or to delete technologies. And that's all we need to do for that portfolio item.
Now if you come all the way down to the portfolios_controller.rb
right now all we have is the :name
. But we also need to pass some things in such as the :id
and then a very special little name here and this one's kind of a weird one but this is specific to the attributes that are needed by cocoon. And this is going to be a :_destroy
.
So what this is going to allow us to do is to actually remove items and also pass those in as attributes. So now that we have those we can come to _technology_fields.html.erb
and add one more field and it is a very special field here and it's a method that's called <%= link_to_remove_association 'Remove Technology
'. And then we have to pass in the f
for form.
And now let's see if this is working. So if I come (portfolio edit page) here hit refresh. Now you can see we have these little remove technology items so let's test this out. If I click that nothing is working. OK, so what gives right here? A few ways to debug this. You're not going to see any action here in the terminal and there's a very good reason it's because everything that we're doing right here is based in javascript so this is all on the client side. Now we know that Add Technology
works but Remove Technology
doesn't. It's kind of interesting. Now if I hit refresh you can see that it clears that off. Also a good idea is always to open up the console. So if I check this if I click Remove Technology
we don't have any errors. OK. So let's come back here and let's see exactly what is going on. So we have a link_to_remove_association
. It says Remove Technology
we pass the f
here, we have f.label
and there's one thing we're missing now. This is something that is very important and it may not seem like a big deal but if you understand the way jQuery works then this is a good lesson form_group
is not the only class this needs. We also need a class called nested-fields
.
And if you're wondering how we know that all we have to do is look at the documentation coming down. And this is not going to look completely clear this is using haml which is another way of writing instead of using erb but we can kind of decipher what this is doing.
So right here in our projects form partial we have all of these items and this is our set tasks, for us, this is our technologies. We have fields_for which we've done our render to our partial passing in the form variable to the partial. Now here's the partial Now do you notice something this .nested-fields
and with the way haml works this means that this is a CSS class. And so in order for the delete to work it needs to have this class because the way that the jQuery is written is it needs to know where on the page to go and remove those items. So now that I've added that now this should work.
So if I come back here hit refresh if I click on remove technology. Look at that it's removed. Click Save. And if we come back here (to the portfolio show page) you can see that now that has been removed. So we are good to go. Everything here is working beautifully. Now one thing I do want to add for admins is I want to add the ability to edit the portfolio item because right now we're having to come here and that's just really not a great way of doing it. So let's come and do that really quick. So on the portfolios/show.html.erb
page we have all of these items. Let's also add a new little div. Let's see where would we want this. We could put a just right underneath technologies used so we can put it right here. Create a new div and we can say col-md-12
and I'm going to wrap all of this so this col-md-12
doesn't even show up to non-admin users so I'm going to say <% if logged_in?(:site_admin) %>
. So in other words if a site admin is logged in then they will see this. And if not they won't see anything at all. And inside of this let's put a button so I'm going to say <%= link_to "Edit Item", edit_portfolio_path(@portfolio_item), class: 'btn btn-warning' %>
.
<% if logged_in?(:site_admin) %> <div class="col-md-12"> <%= link_to "Edit Item", edit_portfoio_path(@portfolio_item), class: 'btn btn-warning' %> </div> <% end %>
There you go. We have an item right there and that is looking OK. I'm not sure. Maybe you don't even want this column thing because that's a reason why we're getting the indentation. (Jordan removed the class of col-md-12
from line 25). There we go. Perfect, so now if I click on this, this takes us to the edit and if I open this up as a non-admin user. You can see that it doesn't even show up at all which is exactly what we're looking for. Ok cool. And I'll leave that open. I think we have everything we need. And shockingly we did it in just one take. So that's always cool.
git status
I'm going to separate these commits out because remember how we've made a couple of changes to the show page. And those are completely unrelated to our form. So it's bad practice to merge multiple commits into one. Not to say that I don't do it occasionally on accident or when I'm in a rush. But whenever you can it's really a good idea to follow best practices.
git add app/views/portfolios/show.html.erb
So now if I say
git status
you can see that we have the show page isolated. So now I can say
git commit -m "Updated fix for show page image and added edit button"
git push origin forms
and technically I didn't have to push and you can create as many commits as you want without having to push and they're still separated. And then when you push them up all the commits get pushed up now you can say
git add .
and
git commit -m "Integrated cocoon for portfolio items nested form component"
git push origin forms
and that's everything we need. There let's come to pivotal tracker as a big one to cross off the list to have javascript form integrations done in the next guide. We are going to take a look to see how we can add custom validations for our forms and we're also going to start adding notifications on the site if you've noticed we don't really have any notifications which is not the best user experience for people so what we're going to walk through is how we can add some custom validations to the forms and then look at what we need to do to add alert messages all throughout the site as well. So I will see in that guide.