У меня есть следующий код Javascript, содержащийся в моем файле profile.js
в Rails 7:
window.addEventListener('load', function () {
updateProfile();
})
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
window.addEventListener('popstate', function() {
updateProfile();
});
window.addEventListener( "pageshow", function ( event ) {
var historyTraversal = event.persisted ||
( typeof window.performance != "undefined" &&
window.performance.navigation.type === 2 );
if ( historyTraversal ) {
// Handle page restore.
window.location.reload();
}
});
function fileSelected() {
// Get the selected file
const file = document.querySelector('#fileInput').files[0];
if (file == null){
return
}
// Create a new FileReader object
const reader = new FileReader();
// Set the onload event handler for the FileReader object
reader.onload = function(event) {
// Update the src attribute of the profile image
document.querySelector('#profile-image').src = event.target.result;
};
// Read the selected file as a DataURL
reader.readAsDataURL(file);
}
function updateProfile(){
var toggle_switch = document.getElementById('toggle');
var save_button = document.getElementById('save-button');
let nameInput = document.getElementById('name');
nameInput.addEventListener('input', function(event) {
let error_element = document.getElementById('error-message-name');
let regex = /^.{3,}$/; // Regex that requires at least 3 characters
if (regex.test(value)) {
// Value is valid
error_element.classList.remove("visible-error")
error_element.classList.add("invisible-error")
} else {
// Value is invalid
error_element.classList.remove("invisible-error")
error_element.classList.add('visible-error');
}
});
toggle_switch.addEventListener('click', function() {
if (this.getAttribute('data-type') == "influencer"){
this.setAttribute('data-type', "vender");
document.getElementById('profile_type').value = "vender";
}else{
this.setAttribute('data-type', "influencer");
document.getElementById('profile_type').value = "influencer";
}
});
save_button.addEventListener('click', function() {
let new_name = document.getElementById('name').value;
let new_headline = document.getElementById('headline').value;
let new_country = document.getElementById('country').value;
let new_city = document.getElementById('city').value;
let new_about = document.getElementById("about").value;
let new_profile_type = document.getElementById('toggle').getAttribute('data-type');
let div = document.getElementById('user_id');
let user_id = div.getAttribute('data');
var fileInput = document.getElementById("fileInput");
var file = fileInput.files[0];
if (file == null) {
$.ajax({ //A new image was not uploaded for change
type: "PATCH",
url: encodeURI('/users/' + user_id),
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name = "csrf-token"]').attr('content'))},
data: { user: { profile_type: new_profile_type, name: new_name, headline: new_headline, country: new_country, city: new_city, about: new_about} },
success: function(response) {
console.info("Update success.")
}
});
} else {
const formData = new FormData();
formData.append("avatar", file);
$.ajax({
url: encodeURI('/users/' + user_id),
type: "PUT",
beforeSend(xhr, options) {
xhr.setRequestHeader('X-CSRF-Token', $('meta[name = "csrf-token"]').attr('content'));
xhr.setRequestHeader('image-change', true);
options.data = formData;
},
success: function(response) {
console.info("Image Update success.")
},
error: () => {
alert("An issue occured. Please try again.");
}
});
}
});
}
Этот код следует запускать, когда пользователь переходит на страницу моего профиля со следующим html.erb
:
<head>
<%= stylesheet_link_tag 'linked_card', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_link_tag 'profile_card', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_link_tag 'alert', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag "profile", "data": { "turbolinks-track": "reload" } %>
</head>
<header>
<%= render partial: "layouts/header" %>
</header>
<body class = "profile_main_body">
<%= render partial: "layouts/profile_card", locals: { user: @user } %>
<br>
<br>
<div class = "col-md-6 mx-auto text-center">
<h3 class = "heading-black">Link Accounts</h3>
<p class = "text-muted lead">Click the button below to link an account to your profile. You will be redirected to the chosen service.</p>
<a href = "<%= user_profile_path(@user.username)%>" class = "border px-3 p-1 add-experience"><i class = "fa fa-link"></i> Link Account</a>
</div>
<br>
<br>
<div class = "container">
<div class = "row">
<%= render partial: "layouts/linked_card" %>
<%= render partial: "layouts/linked_card" %>
</div>
</div>
<br>
</body>
<footer>
<%= render partial: "layouts/footer" %>
</footer>
Я добавил следующую строку:
<%= javascript_include_tag "profile", "data": { "turbolinks-track": "reload" } %>
Потому что я думал, что это проблема турболинков. Ничего не изменилось, хотя с строкой выше. Могу ли я в любом случае повторно запустить мой javascript, когда пользователь нажимает на турбо-ссылку? JavaScript работает нормально при полной перезагрузке страницы. Любая помощь будет большим спасибо!
Turbolinks заменен на Turbo в Rails 7. Однако есть много совпадений в том, как вы должны думать о JS.
Turbolinks/Turbo (или почти любой фреймворк SPA в этом отношении) создает постоянный сеанс браузера между страницами. Думать в терминах «когда страница загружается, я хочу прикрепить обработчик события к X, который делает Y», возможно, было бы нормально в базовом руководстве по JS десять лет назад, но на самом деле это контрпродуктивно:
turbolinks:change
, вы можете добавить обработчик события несколько раз к одному и тому же элементу. Простое отключение события load/ready для эквивалента Turbo/Turbolinks не обязательно исправит вонючий код и может просто вызвать новые проблемы.Прекратите назначать идентификаторы и обработчики событий напрямую всему. Вы просто получите дублирующиеся идентификаторы и мусорный JS.
Вместо этого используйте делегирование, чтобы поймать событие, когда оно всплывет в верхней части DOM:
// Do something awesome when the user clicks buttons with class = "foo"
document.addEventHandler('click', function(event){
let el = event.target;
if (!el.matches('.foo')) return;
// ...
});
Используйте классы и атрибуты для целевых элементов. Не удостоверения личности. Это не 2010 год, и запросы к DOM намного быстрее.
Используйте обход DOM и API формы для получения элементов относительно элемента, который был нажат/изменен/и т. д. Помните, что в JS функции могут принимать аргументы. Используйте атрибуты данных, если вам нужно передать дополнительную информацию из бэкэнда в ваш JS.
Это в основном то, что делает Stimulus:
<div data-controller = "hello">
<input type = "text">
<button>Greet</button>
</div>
// src/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
console.info("Hello, Stimulus!", this.element)
}
}
Я знаю, что этот ответ на самом деле не касается кода в вопросе. Но вам, скорее всего, даже не нужен этот код. ActiveStorage имеет компонент JS для загрузки файлов, что намного лучше. Вы можете справиться с отправкой формы с помощью Turbo гораздо менее запутанным способом.
Спасибо за подробный ответ. Как именно я могу отправить форму с турбо/обработкой ошибок, как указано выше. Не могли бы вы предложить обернуть его так, чтобы он пузырился из дома, как вы упомянули выше?
Я бы предложил отказаться от этого кода. Вам потребуется больше усилий, чтобы спасти его, чем оно того стоит.
Смотрите hotrails.dev/turbo-rails для обучения.
Я не использовал его много, но читая о Rails 7 при подготовке к нему, я подумал, что турболинки были заменены на Turbo и Stimulus.