Migrating From jQuery to Vanilla Javascript Discussion
`document.querySelectorAll` looks like return an array, but in fact it is an object. It's important to keep this in mind in order to avoid some problems iterating the dom elements.
Hey Felipe,
Yeah, I think I know what you mean. querySelectorAll returns an object but since you can do something like object[0] to get the first element in the object, it looks like you might be working with an array. Great point.
This is a great video.
Is it possible to remove jQuery if you are using Bootstrap 4? I know it's a dependency but maybe if you are not using all aspects of Bootstrap you can remove it.
Anyway, removing jQuery is a good way to remove 250kb (30kb gzipped) from your website's download which is not bad at all.
Is it possible to change the default render for other one?
When Trix render I would like to use a different partial. For example _user_trix.html.erb so I can include some special content.
Hi Franklin,
I believe you can define a #to_trix_content_attachment_partial_path
on your User
model, e.g.
class User < ApplicationRecord
include ActionText::Attachable
...
def to_trix_content_attachment_partial_path
'users/user_trix'
end
end
Hi Adam and Franklin,
I had a similar issue using a Profile model and rendering a _profile_mention.html.erb
partial for the mentions.
Adding this to_trix_content_attachment_partial_path
method pointing to my _profile_mention.html.erb
partial did the trick when editing my rich text.
But when displaying my rich text (@article.content), it still renders the _profile.html.erb
partial.
Any idea how I could fix that please?
Hey Chris,
Awesome episode as usual! Few quick questions:
- Since
ActionText
is storing aglobal_id
reference to records in order to display the updated partial at render time, does that mean it's making a separate DB query to retrieve each of those records? For instance, if I @mention 10 separate users, will it make 10 separate calls? Especially if I'm creating a commenting system that allows @mentions, and there could be 10-20 comments, each with several @mentions, etc... - I notice you used Zurb Tribute for the @mention library; you've done an episode before using AtJs, any benefits to Tribute over AtJs, or just preference?
I really do like the concept of storing a reference to the partial instead of the hard-coded HTML. I'm actually in a situation where I stored the HTML snippets in the text itself and now want to change it, but am struggling with how to do that using the Froala editor. I'll eventually migrate to ActionText
in a few months after Rails 6 has been vetted in the wild.
Always appreciate your timely and very applicable videos!
I will be in a similar situation and my solution will either be to cache the mention or store it in redis using the sgid and set it to auto expire in a few hours. Avatar may be out of date for a short duration because of the cache but it is a small price to pay to reduce load on the DB.
Hi @chris I have a little app that has a venue and user that I would like the user to be able to call something like " Hey @user do you want to go to #venue on saturday?" or the like.
is that possible?
If you set a data map for the Rails path, can you replace fetch(`/mentions.json?query=${text}`)
with this.data.get("url")
? I'm having issues with it where I get an undefined error in console.
I'm building an app that will have this feature, but I also am dealing with multi-tenancy. So how do I pass the account_id ( from multitenancy) in the fetchUsers call, here's what I have and it's not working ( I'm getting an undefined error).
mentions_controller.js
initialize() {
this.account = this.data.get("account")
}
fetchUsers(text, callback, account) {
let account = this.account
fetch(`/accounts/${account}/mentions.json?query=${text}`)
.then(response => response.json())
.then(users => callback(users))
.catch(error => callback([]))
}
and in _form.html.erb
<%= f.rich_text_area :problem, placeholder: 'The raw idea, use case, something that motivates us to work on this.', data: { controller: "mentions", target: "mentions.field", mentions_account: current_account.id} %>
I know the account_id is getting to the initialize method, but not the fetchUsers method. Any thoughts?
I'm a bit of a javascript n00b but I found a solution... My updated fetchUsers method...
fetchUsers(text, callback, account) {
let accountId = document.querySelector("trix-editor").dataset.mentionsAccount
console.log(`This is from fetchUsers ${accountId}`)
fetch(`/accounts/${accountId}/mentions.json?query=${text}`)
.then(response => response.json())
.then(users => callback(users))
.catch(error => callback([]))
}
@Chris, I know this episode is a little old, but this is still something I'm dealing with today.
I understand the rationale behind the move to get rid of jQuery in web apps: jQuery was from a time when we needed cross-browser compatability, when vanilla JS didn't provide functionality, that vanilla JS is now good and fast, etc...
And I agree with many of the points.
However, in almost evey project I've written in the last 2 years that doesn't use jQuery, one of the first things I find myself doing is writing a JS class that has a lot of helper functions that look very similar to jQuery.
This is easiest to see with dynamically appending elements to the document using something like Rails' <action>.js.erb
format.
Writing this:
const fragment = document.createRange().createContextualFragment("<%= j render(partial: '...') %>")
document.querySelector("#some-selector").appendChild(fragment)
Seems a lot more painful than this:
$("#some-selector").append("<%= j render(partial: '...') %>")
I end up writing a class like the below where I throw in all my helper functions, essentially mimicking jQuery:
# JS helper class attached to window.Global (using webpack)
export default class Global {
static append(selector, html) {
const fragment = document.createRange().createContextualFragment(html)
document.querySelector(selector).appendChild(fragment)
}
}
I understand if someone is using a framework like React/Angular/VueJS then they wouldn'd use jQuery, but that's because they're using another framework which abstracts complexity.
Do you find that you and other developers are writing your own helper functions to abstract some of the complexity of vanilla JS away? How do you handle trying to write less code that is easier to maintain with the move away from jQuery?
Hi all - I'm getting some unexpected behavior here, and I was hoping one of you might know of a way to fix it. The _pasteHtml
function in mentions_controller.js
is deleting more text than it should before the @mention. Here's what I'm seeing: https://streamable.com/ye7rni.
I'm using code that's identical to the tutorial.
Thanks for any insight.
Hey John, I had the same issue. Maybe try this out when you are setting the selected range?
this.editor.setSelectedRange([position - endPos + startPos, position + 1]);
Maybe obvious to some, but I wanted to know why this is the case. My understanding now is that position
comes from Trix and begins counting from the start of the rich text area, while startPos
and endPos
come from Tribute and begin counting from the start of the current line.
setSelectedRange
is a Trix method, so it requires start and end position values relative to the beginning of the rich text area, which is why you need to find the length of the Tribute mention and subtract that from the current Trix position.
Another (verbose) way to write _pasteHtml
:
_pasteHtml(html, startPos, endPos) {
let position = this.editor.getPosition()
let tributeLength = endPos - startPos
let trixStartPos = position - tributeLength
let trixEndPos = position
this.editor.setSelectedRange([trixStartPos, trixEndPos])
this.editor.deleteInDirection(“backward”)
}
Thank you so much! Was wrestling with this and having a solution with an explanation is pure gold!
Hey Chris,
This is probably my favorite series as I'm learning to use both actiontext and stimulus, so thanks for the videos
Would it be possible to add another model as an attachment and post stuff on that model from this editor?
Imagine that I have a 'Tasks' model and that I also have an 'New Tasks' button in the trix toolbar, the would potentially give me a task list. Would it then be possible to, when I save the document, to also save a new "task list" in my task model.
This might be a no-brainer but as a designer is not that easy for me to figure it out by myself yet.
Have a look at this video: https://gorails.com/episodes/notifications-action-text-user-mentions
It shows how to extract the all the mentioned users within an action text to notify them via email. You could do something similar for your tasks. Except in your case it will be tasks instead of users, and instead of sending emails, you create a list of tasks.
Hi Chris, I saw your PR related to to_trix_content_attachment_partial_path
so I figured you might know this:
Is it possible to specify a partial path for an attachable, without overriding to_partial_path?
I have @mentions implemented in my application just like you show in this video. I use render @user
all over the place but that users/_user.html.erb
partial is very different from what I'd like the @mentions to look. So I want to specify a different partial path for rendering users within an Action Text.
I could replace my existing render @user
specifying a different partial, but that just seems a like hack. I imagine there must be a to_action_text_partial_path
-like method somewhere, but I haven't found any yet.
Found this! https://github.com/rails/rails/issues/35218#issuecomment-617001327
Let's hope Rails 6.1 will add this.
Great video! I also integrated these mentions with the noticed gem and it seems to be working well. I'm having one issue, though, where when I try to render the rich text body in plain texts for things like SMSs it's stripping out any of the text associated with the user's name, not just the styling, since it's all treated as an attachment. Do you know of a way around this?
Hey! Sorry this is probably too late to help in your case but you can override the: attachable_plain_text_representation(caption)
method on your ActionText::Attachable model. By default this method uses the caption
(for images) but since we aren't setting a caption for @-mentions, the text is blank if you call to_plain_text
.
In this example, you might override the method to return something like "@#{name}" for the plain text formatting.
Hi Chris, or anyone who has some thoughts,
I am utilizing this, but for some purposes have to call the .to_plain_text call on the model I'm using (Comment). When I do that, ActionText removes the @mention, and I'm not quite sure how to add a to_plain_text process for the @mentions. Any ideas?
When I leave the json partial in place for the user I get the JSON rendered in the post content instead of the HTML partial which is meant to render it as a span—any idea why the jbuilder partial would be preferred when rendering the content in a regular HTML view?
Hi, this is perfect, but I have got a problem if I want to run this into the modal. Does anyone know what kind of problem that can be please? :/
Hi Chris,
Thank you for this amazing video. I followed everything, however, I still got undefined when I hit enter on the name.