Событие в дочернем представлении не запускается — Backbone.js

#javascript #events #backbone.js #view #amd

#javascript #Мероприятия #backbone.js #Вид #amd

Вопрос:

Я разрабатываю небольшой проект в попытке научиться backbone.js и я использую AMD (рекомендовано в руководстве). У меня есть дочернее представление для «Элемента управления спортсменом», в основном представление, которое я планирую повторно использовать на других страницах / представлениях. Событие щелчка в дочернем представлении (AthleteView) не срабатывает.

AthleteView отображается в div в TimerView, а TimerView отображается в div с идентификатором «страница» на индексной странице. Я включил все, что приведено ниже, поскольку подозреваю, что проблема может быть в том, как я все настраиваю.

Основной Вид:

 define([
  'jquery',
  'underscore',
  'backbone',
  'views/SettingsView',
  'views/AthleteControlView',
  'text!templates/timerTemplate.html',
  'timer'
], function($, _, Backbone, SettingsView, AthleteView, timerTemplate){

  var TimerView = Backbone.View.extend({
    el: $("#page"),

    render: function(){      
      this.$el.html(timerTemplate);
      var settingsView = new SettingsView();
      settingsView.render();

      var athleteView = new AthleteView();
      athleteView.render();

      console.log('rendered Timer');

    }
  });

  return TimerView;

});
  

Дочерний вид:

 define([
  'jquery',
  'underscore',
  'backbone',
  'text!templates/AthleteControlTemplate.html',
  'jqueryui'
], function($, _, Backbone, keypadTemplate){

  var AthleteView = Backbone.View.extend({

    el: $("#AthleteControl"),

    events: {
          'click': 'clickHandler'
    },

    render: function(){
      $("#AthleteControl").html(keypadTemplate);
      console.log('rendered AthleteControl');
    },


    clickHandler: function()
    {
      alert('click');
      console.log('click');
    }

  });

  return AthleteView;

});
  

ROUTER.JS

 define([
  'jquery',
  'underscore',
  'backbone',
  'views/TimerView'
], function($, _, Backbone, TimerView) {

  var AppRouter = Backbone.Router.extend({
    routes: {
      '*actions': 'defaultAction'
    }
  });

  var initialize = function(){
    var app_router = new AppRouter;
    app_router.on('route:defaultAction', function (actions) {
        var timerView = new TimerView();
        timerView.render();
    });

    Backbone.history.start();
  };
  return { 
    initialize: initialize
  };
});
  

Index.html

 <html>
<head>
    <script data-main="js/main" src="js/libs/require/require.js"></script>
</head>
<body>
  <div id="page">
    Loading....
  </div>
  <div id="footer"></div>
</div> 

</body>
</html>
  

ШАБЛОН ТАЙМЕРА:

 <div class = "Controls">
     <button id="Start">Start</button>
     <button id="Stop">Stop</button>
      <button id="Reset">Reset</button>
 </div>
 <div id="AthleteControl"></div>
  

Комментарии:

1. Существуют ли #AthleteControl и #page при загрузке ваших представлений? Использование el: $(...) почти всегда плохая идея, IMO. Я думаю, el: 'some selector string' что это тоже плохая идея, позволяя каждому представлению создавать свое собственное el и позволяя вызывающему пользователю размещать его на странице, обходя всевозможные проблемы.

2. Я включил дополнительные файлы, чтобы продемонстрировать, как все загружается в данный момент (извиняюсь, если это излишне). Я думаю, что оба существуют при загрузке представлений, но моего понимания того, как это работает на самом деле, все еще не хватает. Не могли бы вы проиллюстрировать, что вы имеете в виду, позволяя каждому представлению создавать свой собственный el и т.д.? Спасибо

Ответ №1:

Таким образом, id="AthleteControl" элемент поступает изнутри timerTemplate.html . Это означает, что $('#AthleteControl") ничего не найдет при выполнении этого кода:

 var AthleteView = Backbone.View.extend({
  el: $("#AthleteControl"),
  

Это означает, что AthleteView в конечном итоге будет вести себя так, как если бы вы сказали:

 var AthleteView = Backbone.View.extend({
  el: undefined,
  

Если вы не укажете представление el явно, представление создаст пустое <div> значение el . Такие вещи, как:

 el: $("some-selector")
  

почти всегда ошибка из-за проблем с синхронизацией. Вместо этого просто используйте строку селектора и позвольте Backbone преобразовать ее в узел DOM, когда это необходимо:

 el: "some-selector"
  

Быстрое исправление должно заключаться в отмене jQuery el s:

 el: '#page'

el: '#AthleteControl'
  

Что касается «позволить представлениям создавать свои собственные el s», вы позволяете представлениям создавать свои собственные узлы DOM для использования в качестве своих el вместо того, чтобы пытаться привязать представления к el s, которые уже есть на странице. Этот подход:

  1. Делает ваши представления более автономными.
  2. Помогает уменьшить конфликты привязки событий, поскольку ваше приложение становится больше и включает в себя больше просмотров.
  3. Помогает уменьшить утечки памяти, поскольку каждое представление имеет один узел DOM (и наоборот) во время работы приложения.
  4. Удаление представления — это простой view.remove() вызов, а не ручная очистка привязок событий и HTML. Если у вас есть подвиды, то вы бы переопределили remove в своем представлении вызов remove подвидов перед подключением к remove реализации по умолчанию.

Этот подход (без AMD/require.js материал для ясности) будет больше похоже на следующее.

HTML:

 <script id="timerTemplate" type="text/x-underscore">
    <div class="Controls">
        <button id="Start">Start</button>
        <button id="Stop">Stop</button>
        <button id="Reset">Reset</button>
    </div>
</script>

<div id="page">
</div>
  

JavaScript:

 var AthleteView = Backbone.View.extend({
    id: 'AthleteControl',
    render: function() {
        this.$el.html('Athlete control stuff goes here.');
        return this; // This is standard practice, you'll see why below.
    }
});

var TimerView = Backbone.View.extend({
    render: function () {
        var tmpl = _.template($('#timerTemplate').html());
        this.$el.html(tmpl());

        var athleteView = new AthleteView();
        this.$el.append(athleteView.render().el);
        return this;
    }
});

var timer_view = new TimerView;
$('#page').html(timer_view.render().el);
  

Обратите внимание на return this в render методах и обратите внимание, как this.$el.html и this.$el.append используются для построения и объединения представлений.

Демонстрация:http://jsfiddle.net/ambiguous/r4tsA/1 /

Комментарии:

1. Спасибо за этот исчерпывающий ответ, высоко ценится. Это исправило проблему, и я начинаю лучше понимать. Я бы поддержал ваш ответ, но пока у меня нет необходимой репутации, но я сделаю это, как только сделаю. Приветствия

Ответ №2:

Чтобы привязать событие к действию, вам нужно указать, какой элемент прослушивает какое событие, например

 .., var AthleteView = Backbone.View.extend({
..
events: {
  'click button.btn': 'doSomething'
},
..
doSomething: function(e) {
  // code goes here
},
..
  

Комментарии:

1. Я боюсь, что это не сработает. Я добавил простую кнопку и сделал именно это, но без радости.

2. Неверно. 'click': 'doSomething' в events привязывает событие щелчка к el самому представлению, в документации даже говорится об этом: «Исключение selector приводит к привязке события к корневому элементу представления ( this.el )».