Вызов AJAX не работает в rails 3

#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 для обновления данных. Лично я нахожу этот подход намного проще для понимания.