Использование Backbone для визуализации формы при изменении модели вызывает ошибки пользовательского интерфейса
У меня есть простой вид, который привязывает себя к перерисовке, когда модель изменяется, поскольку большинство направляющих указывают:
this.model.bind('change', this.render, this);
Этот вид имеет форму с полем ввода и кнопкой отправки. Я привязываюсь к событию "change" в поле ввода, чтобы изменить связанный элемент модели. (Я могу сделать это вручную или с проектом ModelBinder, работает одинаково в любом случае.)
Пользователь меняет значение в поле ввода формы, затем нажимает кнопку отправки. Модель обновляется, вид и форма повторно отображаются. Событие нажатия кнопки отправки распадается.
Я использую свойство Backbone events:
events : {
'submit form' : 'save'
},
Я знаю, что событие игнорируется, потому что элемент DOM, на который был нажат, больше не существует. Я могу поместить небольшой setTimeout внутри render(), чтобы предотвратить замену HTML, и все работает так, как ожидалось, но для этого требуется ожидание.
Я не могу быть первым человеком, который мог бы бороться с этим - какой стандартный способ захвата формы "изменить" события, обновить модель и перерисовать представление, не теряя при этом нажатия клавиш или нажатия клавиш?
Аналогично, если у меня есть несколько элементов формы, пользователь не может заносить вкладку между элементами после изменения содержимого по мере того, как форма перерисовывается.
Обновление 1 - 3 дня спустя
По-прежнему пытается найти хорошее решение. Вещи, которые я пробовал:
- перемещение или клонирование содержимого представления в другую область на странице. Событие клика по-прежнему никогда не принимается.
- регистрация события click с $(document).on или $(document).live вместо объекта стандартных событий представления
- отделяя форму так, чтобы вся форма (входы и кнопки) оставалась вместе, не перерисовываясь. Перерисовать родительские элементы (которые полагаются на значения формы) и повторно вставить уже нарисованную форму. Это устраняет связанную с этим проблему неспособности выполнить вкладку по элементу, но не фиксирует события щелчка.
- работает как угодно в firefox 4, но не ie9 или хром.
*
Обновление 2 - с примером кода
Один из комментариев задал какой-то код. Я значительно упростил код на одной странице и включил его ниже. Фактическое приложение намного сложнее. С помощью кода, приведенного ниже, я мог бы просто вручную повторно отобразить части страницы при изменении. В фактическом приложении я использую шаблоны dustjs, и даже если я не повторно визуализую форму, но повторно переопределяю элементы, содержащие форму, на которую возникают проблемы, нажав на submit. Я надеюсь на "шаблон", который типичен для базовых приложений, включая сложные страницы, модели и формы.
Большинство "демонстрационных" приложений и даже сайтов, которые я видел с использованием магистральной сети, как правило, представляют собой приложения, ориентированные на презентацию, которые на самом деле не собирают много ввода от пользователя. Если вы знаете о хорошем приложении/демонстрации, ориентированном на сбор данных, на основе основы, которая была бы полезной.
код:
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.js"></script>
<script src="libs/underscore.js"></script>
<script src="libs/backbone.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/template" id="edit_user_template">
<h3>Edit User <%=id%> : <%=name%></h3>
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'[email protected]'});
var UserView = Backbone.View.extend({
events : {
'submit form' : 'save',
'change input' : 'inputChange'
},
initialize: function(){
this.model.bind('change', this.render, this);
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
},
inputChange : function(evt){
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
save : function(event){
console.log('saving');
event.preventDefault();
//this.model.save();
}
});
var view = new UserView({model: model, el: '#container'});
view.render();
</script>
</body>
</html>
Ответы
Ответ 1
Вы просите шаблон в позвоночнике. Шаблон должен использовать модели для хранения информации о форме (например, вы делаете), а затем обновлять определенные элементы на странице вместо повторного рендеринга каждый раз, когда изменяется модель. ModelBinder предлагает хороший способ сделать все это (https://github.com/theironcook/Backbone.ModelBinder).
Я не думаю, что вы найдете способ избежать возможности представить форму, которая больше не существует. Вам нужно выполнить то, что вы хотите по-другому.
Ответ 2
Если я правильно понимаю, что вам нужно сделать, это разделить представление на 2 более мелкие. Один будет повторно отображать при изменении, другой будет отображать форму.
В зависимости от вашего случая вы можете или не захотите также третьего вида связать эти два вместе.
Боковой комментарий:
При использовании шаблона проектирования MVC он имеет тенденцию быть рекурсивным. Я думаю, что это так. Подумайте о данных в форме как о модели, о представлении повторного рендеринга как о "представлении" MVC, а в виде вида "контроллер".
Ответ 3
Не совсем понимаю, что вы пытаетесь сделать, наблюдая за кодом, который я понимаю, что вы хотите имитировать модель привязки knockoutjs, которая является удивительной.
В любом случае вы можете получить аналогичный "эффект", если вы разделите на два представления, которые работают на одной модели. Один из них обновляет модель, а другой - наблюдаемые изменения и автообновления.
Вот код:
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.min.js"></script>
<script src="libs/underscore.min.js"></script>
<script src="libs/backbone.min.js"></script>
</head>
<body>
<div id="header"></div>
<div id="container"></div>
<script type="text/template" id="user_template">
<h3>Edit User <%=id%> : <%=name%></h3>
</script>
<script type="text/template" id="edit_user_template">
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'[email protected]'});
var UserView = Backbone.View.extend({
initialize: function(){
this.model.on("change", this.render, this);
},
render: function(){
var template = _.template($('#user_template').html(), this.model.toJSON());
this.$el.html(template);
return this;
},
});
var FormView = Backbone.View.extend({
events : {
'submit form' : 'save',
'keyup input' : 'inputChange'
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
return this;
},
inputChange : function(evt){
console.log('change');
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
save : function(event){
console.log('saving');
event.preventDefault();
//this.model.save();
}
});
var user = new UserView({model: model, el: '#header'});
user.render();
var form = new FormView({model: model, el: '#container'});
form.render();
</script>
</body>
</html>
Ответ 4
В вашей проблеме должно быть два возможных решения:
-
Привязать к вводу ввода вместо самого события отправки.
events: {
'click input[type=submit]' : 'save'
}
Это должно выдержать полную замену html.
-
Повторно делегировать события вручную после рендеринга. Это обычно необходимо, если вы повторно просматриваете представление с суб-представлениями, но оно будет применяться и здесь.
this.delegateEvents()
Ответ 5
Я не знаю, буду ли я полностью полагаться на позвоночник, чтобы решить вашу проблему. Я думаю, что это одна из тех ситуаций, когда позвоночник позволяет вам заполнить свой собственный код. Вы можете немного изменить шаблоны и прислушаться к изменениям в модели, а затем решить, что представление о погоде должно быть повторно отображено или просто обновлено. Я не думаю, что попробую перепрофилировать части форм, если модель не будет reset
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.min.js"></script>
<script src="libs/underscore.min.js"></script>
<script src="libs/backbone.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/template" id="edit_user_template">
<h3>Edit User <span class="f-id"><%=id%></span> : <span class="f-name"><%=name%></span></h3>
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'[email protected]'});
var UserView = Backbone.View.extend({
events : {
'submit form' : 'save',
'change input' : 'inputChange'
},
initialize: function(){
//you could have the change event trigger updates instead of re-rendering the form
//this.model.bind('change', this.update, this);
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
},
inputChange : function(evt){
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
update:function(){
// you could trigger other things here if you need other views to know what is going on.
$(".f-id").text(this.model.get("id"));
$(".f-name").text(this.model.get("name"));
},
save : function(event){
console.log('saving');
this.update();
event.preventDefault();
}
});
var view = new UserView({model: model, el: '#container'});
view.render();
</script>
</body>
</html>