#javascript #ajax #ruby-on-rails-3
#javascript #ajax #ruby-on-rails-3
Вопрос:
Я пытаюсь заставить свой вызов AJAX работать с rails 3, но, надеюсь, я в замешательстве. У меня есть индексная страница, на которой перечислены все объекты в небольшой таблице с подробной ссылкой позади нее. Страница HAML выглядит следующим образом:
%h1
Step 1: Create/upload your CV
#europasslogo
%table.index
%tr
%th.bottom_border= 'CV ID'
%th.bottom_border= 'CV Title'
%th.bottom_border= 'Links'
- @cvs.each do |cv|
%tr
%td.cell= cv.id
%td.cell= cv.name
%td.cell
= link_to 'Matching', match_resume_url(cv.id)
amp;nbsp;
= link_to "Details", cv_path(cv.id), {:remote => true, :method => :get}
.clear
#details
Идея в том, что когда пользователь нажимает на ссылку «подробности», ответ должен отображаться в #details div внизу. Это действительно просто, поскольку я просто хочу вывод CV в виде открытого текста в этом div.
В моем контроллере я создал следующее :
class CvsController < ApplicationController
def new
@cvs = Cv.all
end
def show
cv = Cv.find params[:id]
@data = IO.readlines("public/cvs/#{cv.id}.xml", "").to_s
render :layout => false
end
def match
@cv = Cv.find params[:id]
@language = Language.find_or_create_by_code :en
@vacancies = Vacancy.joins(:vacancy_occupations).where('vacancy_occupations.concept_id' => @cv.occupations.collect{|o| o.id}).uniq
end
end
Извлекает CV и читает соответствующий файл. Пока ничего особенного.
Когда я открываю страницу и нажимаю на ссылку, я вижу, что запрос AJAX поступает на сервер следующим образом:
Started GET "/cvs/2" for 192.168.33.82 at Fri May 13 00:55:49 -0700 2011
Processing by CvsController#show as JS
Parameters: {"id"=>"2"}
Cv Load (0.1ms) SELECT `cvs`.* FROM `cvs` WHERE `cvs`.`id` = 2 LIMIT 1
Rendered cvs/show.js.haml (2.1ms)
Completed 200 OK in 69ms (Views: 4.3ms | ActiveRecord: 0.1ms)
Итак, я знаю, что вызов AJAX получен сервером и сгенерирован ответ. Используя Firebug, я также могу видеть ответ от сервера в консоли:
$('details').html(amp;<?xml version=amp;quot;1.0amp;quot; encoding=amp;quot;UTF-8amp;quot;?amp;>
amp;<?xml-stylesheet href=amp;quot;http://europass.cedefop.europa.eu/xml/cv_en_GB.xslamp;quot; type=amp;quot;text/xslamp;quot;?amp;>
amp;<europass:learnerinfo xsi:schemaLocation=amp;quot;http://europass.cedefop.europa.eu/Europass/V2.0 http://europass.cedefop.europa.eu/xml/EuropassSchema_V2.0.xsdamp;quot; xmlns:europass=amp;quot;http://europass.cedefop.europa.eu/Europass/V2.0amp;quot; xmlns:xsi=amp;quot;http://www.w3.org/2001/XMLSchema-instanceamp;quot; locale=amp;quot;en_GBamp;quot;amp;>
amp;<docinfoamp;>
amp;<issuedateamp;>2011-05-05T11:31:55 02:00amp;</issuedateamp;>
amp;<xsdversionamp;>V2.0amp;</xsdversionamp;>
amp;<commentamp;>Automatically generated Europass CVamp;</commentamp;>amp;</docinfoamp;>
amp;<prefsamp;>
amp;<field name=amp;quot;step1.firstNameamp;quot; before=amp;quot;step1.lastNameamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.addressInfoamp;quot; keep=amp;quot;trueamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.telephoneamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.mobileamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.faxamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.emailamp;quot; keep=amp;quot;trueamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.nationalityamp;quot; keep=amp;quot;trueamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.birthDateamp;quot; keep=amp;quot;trueamp;quot; format=amp;quot;/numeric/longamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.genderamp;quot; keep=amp;quot;trueamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.photoamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step1.application.labelamp;quot; keep=amp;quot;trueamp;quot;amp;>amp;</fieldamp;>
amp;<field format=amp;quot;/numeric/longamp;quot; name=amp;quot;step3List[0].periodamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step3Listamp;quot; keep=amp;quot;trueamp;quot; before=amp;quot;step4Listamp;quot;amp;>amp;</fieldamp;>
amp;<field keep=amp;quot;falseamp;quot; name=amp;quot;step3List[0].company.sector.labelamp;quot;amp;>amp;</fieldamp;>
amp;<field keep=amp;quot;falseamp;quot; name=amp;quot;step3List[0].company.addressInfoamp;quot;amp;>amp;</fieldamp;>
amp;<field keep=amp;quot;falseamp;quot; name=amp;quot;step3List[0].company.nameamp;quot;amp;>amp;</fieldamp;>
amp;<field keep=amp;quot;trueamp;quot; name=amp;quot;step3List[0].activitiesamp;quot;amp;>amp;</fieldamp;>
amp;<field keep=amp;quot;trueamp;quot; name=amp;quot;step3List[0].position.labelamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step4Listamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step5.motherLanguagesamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step5.foreignLanguageListamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.socialSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.organisationalSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.technicalSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.computerSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.artisticSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.otherSkillsamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step6.drivingLicencesamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step7.additionalInfoamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;step7.annexesamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>
amp;<field name=amp;quot;gridamp;quot; keep=amp;quot;falseamp;quot;amp;>amp;</fieldamp;>amp;</prefsamp;>
amp;<identificationamp;>
amp;<firstnameamp;>Wouteramp;</firstnameamp;>
amp;<lastnameamp;>ESCOmatchamp;</lastnameamp;>
amp;<contactinfoamp;>
amp;<addressamp;>
amp;<addressLineamp;>Haachtstesteenwegamp;</addressLineamp;>
amp;<municipalityamp;>Kampenhoutamp;</municipalityamp;>
amp;<postalCodeamp;>1910amp;</postalCodeamp;>
amp;<countryamp;>
amp;<labelamp;>BElgiumamp;</labelamp;>amp;</countryamp;>amp;</addressamp;>
amp;<telephoneamp;>amp;</telephoneamp;>
amp;<faxamp;>amp;</faxamp;>
amp;<mobileamp;>amp;</mobileamp;>
amp;<emailamp;>wouter.dewanckel@tenforce.comamp;</emailamp;>amp;</contactinfoamp;>
amp;<demographicsamp;>
amp;<birthdateamp;>1975-01-26amp;</birthdateamp;>
amp;<genderamp;>Mamp;</genderamp;>
amp;<nationalityamp;>
amp;<labelamp;>belgiumamp;</labelamp;>amp;</nationalityamp;>amp;</demographicsamp;>amp;</identificationamp;>
amp;<applicationamp;>
amp;<codeamp;>71110amp;</codeamp;>
amp;<labelamp;>Mineramp;</labelamp;>amp;</applicationamp;>
amp;<workexperiencelistamp;>
amp;<workexperienceamp;>
amp;<periodamp;>
amp;<fromamp;>
amp;<yearamp;>2000amp;</yearamp;>
amp;<monthamp;>--02amp;</monthamp;>
amp;<dayamp;>---02amp;</dayamp;>
amp;</fromamp;>
amp;<toamp;>
amp;<yearamp;>2004amp;</yearamp;>
amp;<monthamp;>--02amp;</monthamp;>
amp;<dayamp;>---02amp;</dayamp;>
amp;</toamp;>
amp;</periodamp;>
amp;<positionamp;>
amp;<codeamp;>82121amp;</codeamp;>
amp;<labelamp;>Quartermaster staff, militaryamp;</labelamp;>
amp;</positionamp;>
amp;<activitiesamp;>
process control
diving
military
amp;</activitiesamp;>
amp;<employeramp;>
amp;<nameamp;>amp;</nameamp;>
amp;<addressamp;>
amp;<addressLineamp;>amp;</addressLineamp;>
amp;<municipalityamp;>amp;</municipalityamp;>
amp;<postalCodeamp;>amp;</postalCodeamp;>
amp;<countryamp;>
amp;<labelamp;>amp;</labelamp;>
amp;</countryamp;>
amp;</addressamp;>
amp;<sectoramp;>
amp;<labelamp;>amp;</labelamp;>
amp;</sectoramp;>
amp;</employeramp;>
amp;</workexperienceamp;>
amp;</workexperiencelistamp;>
amp;<languagelistamp;>
amp;<language xsi:type=amp;quot;europass:motheramp;quot;amp;>
amp;<labelamp;> amp;</labelamp;>amp;</languageamp;>amp;</languagelistamp;>
amp;<skilllistamp;>
amp;<skill type=amp;quot;socialamp;quot;amp;>amp;</skillamp;>
amp;<skill type=amp;quot;organisationalamp;quot;amp;>amp;</skillamp;>
amp;<skill type=amp;quot;technicalamp;quot;amp;>amp;</skillamp;>
amp;<skill type=amp;quot;computeramp;quot;amp;>amp;</skillamp;>
amp;<skill type=amp;quot;artisticamp;quot;amp;>amp;</skillamp;>
amp;<skill type=amp;quot;otheramp;quot;amp;>amp;</skillamp;>
amp;<structured-skill xsi:type=amp;quot;europass:drivingamp;quot;amp;>amp;</structured-skillamp;>amp;</skilllistamp;>
amp;<misclistamp;>
amp;<misc type=amp;quot;additionalamp;quot;amp;>amp;</miscamp;>
amp;<misc type=amp;quot;annexesamp;quot;amp;>amp;</miscamp;>amp;</misclistamp;>amp;</europass:learnerinfoamp;>);
Извините, но файлы довольно большие. Итак, я вижу, что генерируется много Javascript. Файл show.js.haml выглядит следующим образом:
= "$('details').html(#{@data});"
Я делаю что-то не так? Я не очень хорош в этом javascript, но то, о чем я спрашиваю, не может быть настолько сложным, верно?
Все, чего я хочу, это чтобы при нажатии на ссылку все, что находится внутри переменной @data, помещалось в мой раздел details…
Ответ №1:
При = "$('details').html(#{@data});"
код экранируется в HAML.
Используйте != "$('details').html(#{@data});"
, после чего код должен быть выполнен и заменить содержимое.
Однако я не уверен, что ‘details’ является правильным выбором. Насколько я знаю, вы должны использовать ‘#details’, если это идентификатор.
Комментарии:
1. нет такой функции .to_html. И то, как форматируется ответ, не имеет значения atm. Я просто хочу, чтобы все, что возвращается, было видно в моем элементе div
2. что такое «подробности»? это класс или идентификатор? похоже, это неправильный селектор. если ‘details’ — это идентификатор, попробуйте $(‘#details’), если это класс, попробуйте $ (‘.details’). Я не разбираюсь в HAML, но вы должны иметь возможность добавить идентификатор с помощью ‘:id => ‘details’
3. это идентификатор. Изменил его, как вы сказали в файле show.js.haml, но по-прежнему ничего
Ответ №2:
Я думаю, вы обнаружите, что гораздо меньше головной боли возникает, когда вы отделяете JS от кода на стороне сервера, и они никогда не должны встречаться. Ваш контроллер в основном просто считывает файл и загружает его на страницу. Мы можем сделать это полностью в JS:
Ссылка на резюме
= link_to "Details", cv_path(cv.id), :'data-cv-id' => cv.id
JS wireup
$('table.index').delegate('a[data-cv-id]', 'click', function (e) {
var id = $(e.target).data('cv-id');
$.ajax({
type: 'GET',
url: '/cv/' id '.xml',
dataType: 'xml',
success: function (xml) {
$('#detail').html(xml);
}
});
e.preventDefault();
});
(бесстыдный плагин) мой плагин jquery может сделать это еще проще:
$('table.index').delegate('a[data-cv-id]', 'click', function (e) {
$.read('/cv/{id}.xml', { id: $(e.target).data('cv-id') })
.then(function (xml) {
$('#details').html(xml);
});
e.preventDefault();
});
JS wireup — прототип
Вот пример использования Prototype.js
var table = $$('table.index')[0];
$(table).on('a[data-cv-id]', 'click', function (event, element) {
new Ajax.Request('/cv/' event.getAttribute('data-cv-id') '.xml', {
method: 'get',
onSuccess: function (transport) {
//do something with transport.responseXML
//not sure how to write Prototype code for this bit
}
});
Event.stop(event);
});
Вы можете сделать еще один шаг и разделить проблемы запроса / представления с помощью amplify.request. Также взгляните на системы шаблонов JS, такие как Handlebars.
Я знаю, что заманчиво объединить шаблоны Rails с Javascript… но, по моему опыту, это никогда не срабатывает. Проще написать свои контроллеры в виде API-сервиса JSON / XML и выполнять запрос данных / создание шаблонов на Javascript.
Есть причина, по которой RJS был удален из Rails: когда вы создаете такого рода ненужные соединения между серверным и интерфейсным кодом, вы делаете свое приложение более хрупким и сложным в обслуживании с течением времени. Ваш контроллер не должен зависеть от разметки в ваших шаблонах.
Комментарии:
1. Исходное сообщение выглядело как jQuery, но теперь похоже, что это может быть Prototype / RJS. Мой код написан на jQuery.
2. оригинал был прототипом, да. Извините, что не упомянул об этом
Ответ №3:
Спасибо за все отзывы, ребята, но мне удалось разобраться с этим с некоторой помощью моего приятеля.
В контроллере у меня есть следующий код:
@cv = Cv.find params[:id]
doc = REXML::Document.new File.open "public/cvs/#{@cv.id}.xml"
@data = REXML::XPath.first(doc.root, "identification").text
@data << REXML::XPath.first(doc.root, "application").text
@data << REXML::XPath.first(doc.root, "workexperiencelist").text
# Render the partial
render(:update) { |page| page.replace_html 'details', :partial => 'cvs/show', :layout => false}
Это загружает все необходимые мне данные и отображает частичное представление. Остальная часть команды выполняет необходимую мне магию и запускает javascript для обновления данных. Лично я нахожу этот подход намного проще для понимания.