Đặt động id và className trong chế độ xem Backbone.js


85

Tôi đang trong quá trình tìm hiểu và sử dụng Backbone.js.

Tôi có mô hình Mặt hàng và chế độ xem Mặt hàng tương ứng. Mỗi cá thể mô hình có các thuộc tính item_class và item_id, mà tôi muốn được phản ánh dưới dạng thuộc tính 'id' và 'class' của chế độ xem tương ứng. Cách chính xác để đạt được điều này là gì?

Thí dụ:

var ItemModel = Backbone.Model.extend({      
});

var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});

var ItemView = Backbone.View.extend({       
});

Tôi nên triển khai chế độ xem như thế nào để các chế độ xem sẽ dịch sang:

<div id="id1" class="nice"></div>
<div id="id2" class="sad"> </div>

Trong hầu hết các ví dụ tôi đã thấy, el của chế độ xem đóng vai trò như một phần tử bao bọc vô nghĩa bên trong mà người ta phải viết mã 'ngữ nghĩa' theo cách thủ công.

var ItemView = Backbone.View.extend({
   tagName:  "div",   // I know it's the default...

   render: function() {
     $(this.el).html("<div id="id1" class="nice"> Some stuff </div>");
   }       
});

Vì vậy, khi kết xuất, người ta sẽ

<div> <!-- el wrapper -->
    <div id="id1" class="nice"> Some stuff </div>
</div>

Nhưng điều này có vẻ như là một sự lãng phí - tại sao lại có div bên ngoài? Tôi muốn el dịch trực tiếp vào div nội bộ!

Câu trả lời:


133

Tóm tắt: đặt động các thuộc tính chế độ xem với dữ liệu mô hình

http://jsfiddle.net/5wd0ma8b/

// View class with `attributes` method
var View = Backbone.View.extend( {
  attributes : function () {
    // Return model data
    return {
      class : this.model.get( 'item_class' ),
      id : this.model.get( 'item_id' )
    };
  }
  // attributes
} );

// Pass model to view constructor
var item = new View( {
  model : new Backbone.Model( {
    item_class : "nice",
    item_id : "id1"
  } )
} );
  • Ví dụ này giả định rằng bạn đang cho phép Backbone tạo phần tử DOM cho bạn.

  • Các attributesphương pháp được gọi là sau khi các thuộc tính truyền cho constructor xem được thiết lập (trong trường hợp này, model), cho phép bạn tự động thiết lập các thuộc tính với các dữ liệu mô hình trước khi Backbone tạo el.

  • Ngược lại với một số câu trả lời khác: không mã cứng các giá trị thuộc tính trong lớp chế độ xem, tự động đặt chúng từ dữ liệu mô hình; không đợi cho đến khi render()đặt vals attr; không liên tục đặt vals attr trong mọi cuộc gọi đến render(); không đặt thủ công vals trên phần tử DOM một cách không cần thiết.

  • Lưu ý rằng nếu thiết lập lớp khi gọi Backbone.View.extendhoặc một phương thức xây dựng dạng xem (ví dụ new Backbone.View), bạn phải sử dụng tên thuộc tính DOM className, nhưng nếu đặt nó qua attributesphương thức băm / (như trong ví dụ này), bạn phải sử dụng tên thuộc tính , class.

  • Kể từ Backbone 0.9.9:

    Khi tuyên bố chế độ xem ... el, tagName, idclassNamenay có thể được định nghĩa là chức năng, nếu bạn muốn giá trị của chúng được xác định tại thời gian chạy.

    Tôi đề cập đến điều này trong trường hợp có một tình huống mà nó sẽ hữu ích như một sự thay thế cho việc sử dụng một attributesphương pháp như được minh họa.

Sử dụng một phần tử hiện có

Nếu bạn đang sử dụng một phần tử hiện có (ví dụ: chuyển eltới phương thức tạo khung nhìn) ...

var item = new View( { el : some_el } );

... thì attributessẽ không được áp dụng cho phần tử. Nếu các thuộc tính mong muốn chưa được đặt trên phần tử hoặc bạn không muốn sao chép dữ liệu đó trong lớp chế độ xem của mình và một vị trí khác, thì bạn có thể muốn thêm một initializephương thức áp dụng attributescho hàm tạo chế độ xem của mình el. Một cái gì đó như thế này (sử dụng jQuery.attr):

View.prototype.initialize = function ( options ) {
  this.$el.attr( _.result( this, 'attributes' ) );
};

Sử dụng el, hiển thị, tránh trình bao bọc

Trong hầu hết các ví dụ tôi đã thấy, el của chế độ xem đóng vai trò như một phần tử bao bọc vô nghĩa bên trong mà người ta phải viết mã 'ngữ nghĩa' theo cách thủ công.

Không có lý do gì view.elcần phải là "một phần tử bao bọc vô nghĩa". Trên thực tế, điều đó thường sẽ phá vỡ cấu trúc DOM. Nếu một lớp dạng xem đại diện cho một <li>phần tử chẳng hạn, nó cần được hiển thị dưới dạng một <li>- việc hiển thị nó dưới dạng một <div>hoặc bất kỳ phần tử nào khác sẽ phá vỡ mô hình nội dung. Bạn có thể sẽ muốn tập trung vào việc thiết một cách chính xác lên yếu tố của quan điểm của bạn (sử dụng tài sản như tagName, classNameid) và sau đó render nó nội dung sau đó.

Các tùy chọn về cách để các đối tượng dạng xem Backbone của bạn tương tác với DOM rất rộng mở. Có 2 kịch bản ban đầu cơ bản:

  • Bạn có thể đính kèm một phần tử DOM hiện có vào một khung nhìn Backbone.

  • Bạn có thể cho phép Backbone tạo một phần tử mới bị ngắt kết nối khỏi tài liệu, sau đó bằng cách nào đó chèn nó vào tài liệu.

Có nhiều cách khác nhau để bạn có thể tạo nội dung cho phần tử (đặt một chuỗi ký tự, như trong ví dụ của bạn; sử dụng thư viện tạo mẫu như Mustache, Handlebars, v.v.). Bạn nên sử dụng thuộc eltính của chế độ xem như thế nào tùy thuộc vào việc bạn đang làm.

Yếu tố hiện có

Ví dụ kết xuất của bạn cho thấy rằng bạn có một phần tử hiện có mà bạn đang gán cho dạng xem, mặc dù bạn không hiển thị phần khởi tạo của dạng xem. Nếu đúng như vậy và phần tử đã có trong tài liệu, thì bạn có thể muốn làm điều gì đó như thế này (cập nhật nội dung của el, nhưng không thay đổi elchính nó):

render : function () {
  this.$el.html( "Some stuff" );
}

http://jsfiddle.net/vQMa2/1/

Phần tử đã tạo

Giả sử bạn không có một phần tử hiện có và bạn cho phép Backbone tạo một phần tử cho bạn. Bạn có thể muốn làm điều gì đó như thế này (nhưng tốt hơn là nên kiến ​​trúc mọi thứ để chế độ xem của bạn không có trách nhiệm biết về bất cứ điều gì bên ngoài chính nó):

render : function () {
  this.$el.html( "Some stuff" );
  $( "#some-container" ).append( this.el );
}

http://jsfiddle.net/vQMa2/

Mẫu

Trong trường hợp của tôi, tôi đang sử dụng các mẫu, ví dụ:

<div class="player" id="{{id}}">
<input name="name" value="{{name}}" />
<input name="score" value="{{score}}" />
</div>
<!-- .player -->

Mẫu đại diện cho chế độ xem hoàn chỉnh. Nói cách khác, sẽ không có trình bao bọc xung quanh mẫu - div.playersẽ là phần tử gốc hoặc phần tử ngoài cùng của chế độ xem của tôi.

Lớp người chơi của tôi sẽ trông giống như thế này (với ví dụ rất đơn giản về render()):

Backbone.View.extend( {
  tagName : 'div',
  className : 'player',

  attributes : function () {
    return {
      id : "player-" + this.model.cid
    };
  },
  // attributes

  render : function {
    var rendered_template = $( ... );

    // Note that since the top level element in my template (and therefore
    // in `rendered_template`) represents the same element as `this.el`, I'm
    // extracting the content of `rendered_template`'s top level element and
    // replacing the content of `this.el` with that.
    this.$el.empty().append( rendered_template.children() );
  }      
} );

Cách tuyệt vời để ghi đè thuộc tính thuộc tính bằng một hàm và trả lại một đối tượng!
Kel

2
@Kel yeah, đó là một cách tốt để thực hiện một cái gì đó động như những gì câu hỏi yêu cầu, điền các thuộc tính với dữ liệu mô hình mà không cần phải sử dụng mã lặp lại nơi các chế độ xem được khởi tạo. Bạn có thể biết điều này, nhưng chỉ trong trường hợp không rõ ràng, đó là một tính năng của Backbone mà bạn có thể sử dụng một hàm trả về giá trị băm attributes, giống như một số thuộc tính Backbone khác có thể được cung cấp dưới dạng một hàm hoặc một số khác loại giá trị. Trong những trường hợp đó, Backbone kiểm tra xem giá trị có phải là một hàm hay không, gọi nó và sử dụng giá trị trả về.
JMM

95

Theo quan điểm của bạn, chỉ cần làm điều gì đó như thế này

var ItemView = Backbone.View.extend({
   tagName:  "div",   // I know it's the default...

   render: function() {
     $(this.el).attr('id', 'id1').addClass('nice').html('Some Stuff'); 
   }       
});

Đây là câu trả lời đúng cho câu hỏi này và nó sẽ được chấp nhận
reach4thelasers

13
Câu trả lời này không chứng minh việc thiết lập động các thuộc tính chế độ xem dựa trên dữ liệu mô hình, nó chỉ hiển thị một phương pháp mã hóa cứng các giá trị thuộc tính thay thế.
JMM

3
@JMM - Mã mẫu của anh ấy cũng không sử dụng dữ liệu mô hình. Câu trả lời này hoạt động dựa trên mã mẫu của anh ấy. Rõ ràng là dữ liệu mô hình có thể được thay thế cho các giá trị.
Clint

5
@Clint, tôi sẽ không tin vào việc đó là điều hiển nhiên với OP. "Mã mẫu của anh ấy cũng không sử dụng dữ liệu mô hình." - đó là bởi vì anh ấy không biết làm thế nào, và do đó tại sao anh ấy yêu cầu sự giúp đỡ. Đối với tôi, có vẻ như anh ấy đang hỏi cách thiết lập các tập tin của view.el bằng cách sử dụng dữ liệu mô hình và không biết phải làm như thế nào. Câu trả lời thậm chí không chỉ ra cách làm điều đó, và tại sao bạn phải đợi cho đến khi kết xuất để làm điều đó hoặc làm lại mỗi khi bạn kết xuất? "Câu trả lời này hoạt động ..." - nó hoạt động như thế nào? Mọi chế độ xem được tạo như vậy sẽ có cùng vals attr. Điều duy nhất nó chỉ ra là làm thế nào để tránh được lớp bao bọc.
JMM

OP đã biến mất kể từ ngày 12 tháng 2. :( Đây là một +1 khác cho câu trả lời này.
Almo

27

Bạn có thể đặt các thuộc tính classNameidtrên phần tử gốc: http://documentcloud.github.com/backbone/#View-extend

var ItemView = Backbone.View.extend({
   tagName:  "div",   // I know it's the default...
   className : 'nice',
   id : 'id1',
   render: function() {
     $(this.el).html("Some stuff");
   }       
});

CHỈNH SỬA Bao gồm ví dụ về cài đặt id dựa trên các tham số của hàm tạo

Nếu các khung nhìn được xây dựng như đã đề cập:

var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});

Sau đó, các giá trị có thể được đặt theo cách này:

// ...
className: function(){
    return this.options.item_class;
},
id: function(){
    return this.options.item_id;
}
// ...

3
Tôi không cảm thấy câu trả lời này đúng vì sau đó mọi thứ ItemViewsẽ có id: 'id1'. Điều này phải được tính toán trong thời gian thực hiện dựa trên model.id.
fguillen

Tất nhiên bạn có thể đặt ID theo bất kỳ cách nào bạn muốn. Sử dụng một hàm, biến hoặc bất cứ thứ gì. Mã của tôi chỉ bao gồm một ví dụ, chỉ ra cách đặt giá trị trên phần tử gốc.
Jørgen

Tôi đã thêm một ví dụ làm rõ cách đặt các giá trị động dựa trên các tham số của hàm tạo.
Jørgen,

Đây là câu trả lời chính xác. Nó sử dụng các tính năng của Backbone một cách chính xác để giải quyết vấn đề.
Marc-Antoine Lemieux

6

Tôi biết đó là một câu hỏi cũ, nhưng được thêm vào để tham khảo. Điều này dường như dễ dàng hơn trong các phiên bản xương sống mới. Trong Backbone 1.1, thuộc tính id và className được đánh giá trong hàm ensureElement(xem từ nguồn ) bằng cách sử dụng dấu gạch dưới _.resultcó nghĩa là nếu classNamehoặc idlà một hàm, nó sẽ được gọi, nếu không giá trị của nó sẽ được sử dụng.

Vì vậy, bạn có thể cung cấp className trực tiếp trong hàm tạo, cung cấp một tham số khác sẽ được sử dụng trong className, v.v. Rất nhiều tùy chọn

vì vậy điều này sẽ hoạt động

var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});

var ItemView = Backbone.View.extend({       
  id: function() { return this.model.get('item_id'); },
  className: function() { return this.model.get('item_class'); }
});

Ví dụ bạn không hợp lệ, bạn muốnid: function() { return this.model.get('item_id'); })
Cobby

4

Các ví dụ khác không chỉ ra cách thực sự lấy dữ liệu từ mô hình. Để thêm động id và lớp từ dữ liệu của mô hình:

var ItemView = Backbone.View.extend({
   tagName:  "div",

   render: function() {
     this.id = this.model.get('item_id');
     this.class = this.model.get('item_class');
     $(this.el).attr('id',this.id).addClass(this.class).html('Some Stuff'); 
   }       
});

Đó là 'this.className' hay 'this.class'?
Gabe Rainbow

2

Bạn cần xóa tagName và khai báo một el.

'tagName' biểu thị rằng bạn muốn xương sống để tạo một phần tử. Nếu phần tử đã tồn tại trong DOM, bạn có thể chỉ định một el như:

el: $('#emotions'),

và sau đó:

render: function() { 
     $(this.el).append(this.model.toJSON());
}

2

Cố gắng gán các giá trị trong phương thức khởi tạo, điều này sẽ chỉ định trực tiếp id và lớp cho thuộc tính div động.

var ItemView = Backbone.View.extend( {
    tagName : "div",   
    id      : '',
    class   : '',

    initialize : function( options ) {
        if ( ! _.isUndefined( options ) ) {
            this.id = options.item_id;
            this.class= options.item_class;
        }
    },

    render : function() {
        $( this.el ).html( this.template( "stuff goes here" ) ); 
    }
} );

@Michel Pleasae đưa ra tài liệu này, backbonejs.org/#View-constructor
Hemanth

0

Đây là một cách tối thiểu để thay đổi động lớp của phần tử của chế độ xem thông qua một mô hình và cập nhật nó khi thay đổi mô hình.

var VMenuTabItem = Backbone.View.extend({
    tagName: 'li',
    events: {
        'click': 'onClick'
    },
    initialize: function(options) {

        // auto render on change of the class. 
        // Useful if parent view changes this model (e.g. via a collection)
        this.listenTo(this.model, 'change:active', this.render);

    },
    render: function() {

        // toggle a class only if the attribute is set.
        this.$el.toggleClass('active', Boolean(this.model.get('active')));
        this.$el.toggleClass('empty', Boolean(this.model.get('empty')));

        return this;
    },
    onClicked: function(e) {
        if (!this.model.get('empty')) {

            // optional: notify our parents of the click
            this.model.trigger('tab:click', this.model);

            // then update the model, which triggers a render.
            this.model.set({ active: true });
        }
    }
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.