I have a Rails 7 API coupled with a NuxtJS frontend. I have view and edit pages for various resources on the frontend. For a few resources, I have tagging.
I can handle both adding end removing associations (tags) one by one, however, I’d like to make it possible to edit them in a bundle. That is, on the relevant edit (and create) views I would have an interface to add and remove tags, then once the user hits the save button, the removed (if any) tags get removed, the newly added (if any) ones get added to the resource.
Currently, I’m using the following syntaxes: #article.tags << #tag and #article.tags.delete(#tag). This works as expected for both scenarios.
My thinking goes as follows: I should be able to pass an array of ids of tags, do something like #article.tags = #tags. However, this does not seem to delete the relevant rows in the join tables.
Is there a way to do this properly, “the Rails way”?
Related
I'm using acts-as-taggable-on to allow my NewsItem and MediaItem models to be tagged. By default, accessing the model's tags using either tag_list for an array of the tag names or directly via an association using tags for collection of ActsAsTaggableOn::Tags, results in the tags in an arbitrary order. I would like them to always be alphabetised. There is no mention in the documentation of setting up a default order, other than a way of maintaining the order the tags were created in (by using acts_as_ordered_taggable in the model).
Obviously I can order them every time I call tags using news_item.tags.order(:name), but I would like this to be the default behaviour throughout the application and don't want to duplicate the ordering wherever I need to use tags.
How can I set up my model so that its tags association always returns its tags in alphabetical order?
I'm afraid that this kind of ordering cannot be done in the current version of acts-as-taggable-on gem. It is apparent from the source code that this gem indeed only supports ordering by IDs (preserves the order of creation) or no ordering at all.
There is a pull request open that would allow ordering by name or any other attribute but the activity of the gem development seems to be quite stale right now (the pull request has been open for over a year already). Perhaps you might try to ask about the status of this pull request there.
I know this is probably an extremely basic level question, but I'm new to rails and can't seem to locate a clear answer in the Ruby Guides on my own; it's likely that I just don't know the term for this and can't figure it out.
I've got two models, documents and companies (companies is a table built by devise). Companies has_many :documents and documents belongs_to :companies. On my form there is a place for the company's name, address, etc., and I would like to populate the associated company on both on the _form.html.erb and the show.html.erb so that it's not necessary to input this information every time you fill out a form. It's not absolutely necessary that the information be present on the _form.html.erb, but it would be nice to go ahead and present this information so as to not confuse the user.
When I try calling #companies.company_name in my documents show view, I hit a nil class error. I've tried adding #companies = companies.all to my controller, but that doesn't work either. Like I said, I'm sure this is a simple problem, but I don't have much hair left and would like to preserve it for another problem.
I was able to find a different method that seems to be working well at the moment. Instead of adding #company = Company.find (params[:id]), I was able to call #document.company.company_name within the show action. I'll forgo to the new and edit for the moment since show was all that mattered.
I'm making an application that involves booking appointments for users. I have a User model and an AvailableDate model. The user has_many availble_dates and the AvailableDate belongs_to user.
I want to present a form for the user so that they can mark a couple of dates in a calendar and each of the dates they mark will become an AvailableDate object tied to that user.
At the moment my solution is to do all the work that a form_for helper would normally do manually. This involves a lot of javascript and is generally just getting far too messy.
I can't figure out how I should make a form_for tag work when I need to create potentially infinitely many dates. In theory a user could keep marking off dates in the future as available. If I knew how many dates I needed to create for a user, I could do user.available_dates.build, N times. But this doesn't work here.
Can anyone help? It like this problem should be pretty common. Am I designing my application wrong?
One technique is to render the fields for your association once, outside the form.
When the user performs whatever interaction that should create a new set of inputs you use javascript to clone the initial set of fields and insert them into the form. The one thing you need to do is change the name of these inputs so that they are unique. Usually people use the current time in milliseconds for this unique identifier.
Been there & have found several resources to help: Tutorial & Cocoon
The bottom line is you need to ensure child_index is unique for each field. The tutorial I use has child_index: Time.now.to_i to create a truly unique id, consequently allowing you to add as many fields as you want
The best way to do this:
Render fields_for as a partial (passing your form builder object)
When you want to add new field, create ajax_field action
Make ajax_field view have its own form_builder
Both your original & ajax_field forms will call the partial
On front-end, you can use JS to GET new form action & append field to page
I can give you code if you want
I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.
So far, I've not been able to find a solution that is able to allow a form to interact with two models that have a one-to-many relationship. Say for example, a customer has many items.
Currently, I've implemented solutions using Javascript that enables users to add/remove rows to the item list. Let's keep it simple, we're creating new records here.
In improving this to be degradable, I've instead created a 'Add Item' button that commits a different value, so that in my create action, I'm able to capture the new attributes and add a new Item to the customer object.
However, since item is new and has no id, I have no way of removing a particular item. I've thought of using indexes and added post buttons/links to remove, but this sort of makes everything messy and I can't use my partial for collections. So I feel like it's a hack; is there a better alternative?
You want to use accepts_nested_attributes_for. The Railscasts episode henrikloevborg mentions covers a method of doing this kind of thing that's been obsoleted by accepts_nested_attributes_for.
You can do it without accepts_nested_attributes_for. Which might be necessary if you don't want to upgrade to Rails 2.3.
In your javascript to remove an item, you should add a hidden field to the form called _delete with the value of true. Then in your controller. Ignore any new records with the _delete field, and destroy any existing ones.
All that's built in to accept_nested_attributes_for
Play around with the code in the complex-forms-exmample github repository. It's essentially the code from Railscast #75 updated to use the newer feature, accepts_nested_attributes_for.
You will notice that it doesn't use RJS to accomplish the dynamic addition/removal of records. Also compared to what you've been doing it creates on submit, not during a remote call. The automatic addition/deletion of items is handled using content_for tags to create a template stored in a javascript function and the Prototype library to modify a hidden field, which accepts_nested_attributes_for uses to destroy objects you want to remove.