Backbone.js는 중첩 된 객체 속성을 가져오고 설정합니다.


105

Backbone.js의 getset 함수 에 대한 간단한 질문이 있습니다.

1) 아래 코드를 사용하여 obj1.myAttribute1을 직접 '가져올'또는 '설정'할 수 있습니까?

다른 질문:

2) 모델에서 기본 객체를 제외하고 백본의 get 및 set 메서드를 통해 액세스 할 수 있도록 모델의 다른 속성을 어디에서 선언해야합니까?

var MyModel = Backbone.Model.extend({
    defaults: {
        obj1 : {
            "myAttribute1" : false,
            "myAttribute2" : true,
        }
    }
})

var MyView = Backbone.View.extend({
    myFunc: function(){
        console.log(this.model.get("obj1"));
        //returns the obj1 object
        //but how do I get obj1.myAttribute1 directly so that it returns false?
    }
});

나는 내가 할 수 있다는 것을 안다.

this.model.get("obj1").myAttribute1;

하지만 그게 좋은 습관인가요?


3
질문에 대한 답은 아니지만 defaults(이 경우 obj1 )에서 객체 (참조로 전달 된 모든 것)를 지정할 때마다 동일한 객체가 모델의 모든 인스턴스에서 공유됩니다. 현재 관행은 defaults기본값으로 사용할 객체를 반환하는 함수 로 정의 하는 것입니다. backbonejs.org/#Model-defaults (이탤릭체 메모 참조)
Jonathan F

1
@JonathanF 코멘트는 답변을 위한 것이 아니므로 선언이 필요하지 않았습니다. :)
TJ

답변:


144

하지만 this.model.get("obj1").myAttribute1잘은 다음 세트에 대한 물건의 동일한 유형을 유혹 할 수 있기 때문에, 그것은, 즉 약간의 문제입니다

this.model.get("obj1").myAttribute1 = true;

그러나 이렇게하면 myAttribute1변경 이벤트 또는 유효성 검사와 같은에 대한 백본 모델의 이점을 얻을 수 없습니다 .

더 나은 해결책은 모델에 POJSO ( "일반 JavaScript 객체")를 중첩하지 않고 대신 사용자 정의 모델 클래스를 중첩하는 것입니다. 따라서 다음과 같이 보일 것입니다.

var Obj = Backbone.Model.extend({
    defaults: {
        myAttribute1: false,
        myAttribute2: true
    }
});

var MyModel = Backbone.Model.extend({
    initialize: function () {
        this.set("obj1", new Obj());
    }
});

그러면 액세스 코드는

var x = this.model.get("obj1").get("myAttribute1");

그러나 더 중요한 것은 설정 코드가

this.model.get("obj1").set({ myAttribute1: true });

적절한 변경 이벤트 등이 발생합니다. 작업 예 : http://jsfiddle.net/g3U7j/


24
이 답변에이 솔루션이 널리 퍼진 데메테르 법칙 위반에 대해 시끄럽다는 조언을 추가합니다. 중첩 된 개체에 대한 탐색을 숨기는 편리한 메서드를 추가하는 것이 좋습니다. 기본적으로 호출자는 모델의 내부 구조를 알 필요가 없습니다. 결국, 그것은 변경 될 수 있으며 호출자는 현명하지 않아야합니다.
Bill Eisenhauer 2011 년

7
나를 위해 작동하도록 할 수 없습니다. 오류 발생 :Uncaught TypeError: Object #<Object> has no method 'set'
wilsonpage

1
@ChristianNunciato, pagewil, Benno : 백본 모델 내부에 백본 모델을 중첩하는 게시물의 요점을 놓친 것 같습니다. 백본 모델 내에 일반 객체를 중첩하지 마십시오. 작업 예제 : jsfiddle.net/g3U7j
Domenic

1
backbone.js 코드를 검사하지는 않았지만 내 테스트에서 중첩 된 커스텀 모델이 있고 set ()으로 속성을 변경하면 부모 모델 자체가 '변경'이벤트를 발생시키지 않습니다. 이벤트를 직접 시작해야했습니다. 나는 정말로 코드를 검사해야하지만 이것도 당신의 이해입니까?
tom

2
@tom이 맞습니다. 백본은 모델의 속성이의 인스턴스 인 경우 특별한 경우가 아니며 Backbone.Model마법의 이벤트 버블 링을 시작합니다.
Domenic

74

이를 위해 backbone-deep-model 을 만들었습니다. Backbone.Model 대신 Backbone.DeepModel을 확장 한 다음 경로를 사용하여 중첩 된 모델 속성을 가져 오거나 설정할 수 있습니다. 변경 이벤트도 유지합니다.

model.bind('change:user.name.first', function(){...});
model.set({'user.name.first': 'Eric'});
model.get('user.name.first'); //Eric

1
네, 그것은 당신이 보면, 않습니다 API 와 같은 예를 들어이//You can use index notation to fetch from arrays console.log(model.get('otherSpies.0.name')) //'Lana'
tawheed

잘 작동합니다! 그러나 귀하의 예제에서 2 번째 줄에 쉼표 대신 콜론이 필요합니까?
mariachi

16

Domenic의 솔루션은 작동하지만 각각의 새로운 MyModel은 동일한 Obj 인스턴스를 가리 킵니다. 이를 방지하기 위해 MyModel은 다음과 같아야합니다.

var MyModel = Backbone.Model.extend({
  initialize: function() {
     myDefaults = {
       obj1: new Obj()
     } 
     this.set(myDefaults);
  }
});

전체 설명 은 c3rin의 답변 @ https://stackoverflow.com/a/6364480/1072653 을 참조하십시오 .


1
미래의 독자를 위해 내 답변은 Rusty의 답변 중 최고를 통합하도록 업데이트되었습니다.
Domenic

2
질문자는이를 수락 된 답변으로 표시해야합니다. Domenic 's는 좋은 시작이지만 이것은 문제를 해결합니다.
Jon Raasch 2012

5

이 접근 방식을 사용합니다.

다음과 같은 백본 모델이있는 경우 :

var nestedAttrModel = new Backbone.Model({
    a: {b: 1, c: 2}
});

다음을 사용하여 "ab"속성을 설정할 수 있습니다.

var _a = _.omit(nestedAttrModel.get('a')); // from underscore.js
_a.b = 3;
nestedAttrModel.set('a', _a);

이제 모델에는 다음과 같은 속성이 있습니다.

{a: {b: 3, c: 2}}

"변경"이벤트가 발생했습니다.


1
이거 확실하니? 이것은 나를 위해 작동하지 않습니다. meta2= m.get('x'); meta2.id=110; m.set('x', meta2). 이것은 :( 나를 위해 변경 이벤트를 트리거하지 않습니다
HungryCoder

1
같은 속성을 복제하면 작동합니다 _.clone(m.get('x')). 감사
HungryCoder

@HungryCoder에게 감사드립니다. 복제했을 때도 저에게 효과적이었습니다. 백본은 설정된 시간에 setting있는 객체와 현재있는 객체를 비교해야합니다 getting. 따라서 두 개체를 복제하지 않으면 비교되는 두 개체가 설정된 시간에 정확히 동일합니다.
Derek Dahmer 2013 년

객체는 참조로 전달되며 문자열 및 숫자 프리미티브와 달리 변경 가능합니다. Backbone의 집합 및 생성자 메서드는 인수로 전달 된 모든 개체 참조의 얕은 복제를 시도합니다. 해당 개체의 속성에있는 다른 개체에 대한 참조는 복제되지 않습니다. 이를 설정하고 검색 할 때 참조는 동일하므로 변경을 트리거하지 않고 모델을 변경할 수 있습니다.
niall.campbell

3

아직 아무도 생각하지 못한 하나의 솔루션이 있으며 사용할 것이 많습니다. 원하지 않는 타사 라이브러리를 사용하지 않는 한 실제로 중첩 된 속성을 직접 설정할 수 없습니다. 그러나 할 수있는 일은 원본 사전의 복제본을 만들고 중첩 된 속성을 설정 한 다음 전체 사전을 설정하는 것입니다. 케이크 조각.

//How model.obj1 looks like
obj1: {
    myAttribute1: false,
    myAttribute2: true,
    anotherNestedDict: {
        myAttribute3: false
    }
}

//Make a clone of it
var cloneOfObject1 = JSON.parse(JSON.stringify(this.model.get('obj1')));

//Let's day we want to change myAttribute1 to false and myAttribute3 to true
cloneOfObject1.myAttribute2 = false;
cloneOfObject1.anotherNestedDict.myAttribute3 = true;

//And now we set the whole dictionary
this.model.set('obj1', cloneOfObject1);

//Job done, happy birthday

2

@pagewil과 @Benno가 @Domenic의 솔루션과 동일한 문제를 겪었습니다. 내 대답은 대신 문제를 해결하는 Backbone.Model의 간단한 하위 클래스를 작성하는 것이 었습니다.

// Special model implementation that allows you to easily nest Backbone models as properties.
Backbone.NestedModel = Backbone.Model.extend({
    // Define Backbone models that are present in properties
    // Expected Format:
    // [{key: 'courses', model: Course}]
    models: [],

    set: function(key, value, options) {
        var attrs, attr, val;

        if (_.isObject(key) || key == null) {
            attrs = key;
            options = value;
        } else {
            attrs = {};
            attrs[key] = value;
        }

        _.each(this.models, function(item){
            if (_.isObject(attrs[item.key])) {
                attrs[item.key] = new item.model(attrs[item.key]);
            }
        },this);

        return Backbone.Model.prototype.set.call(this, attrs, options);
    }
});

var Obj = Backbone.Model.extend({
    defaults: {
        myAttribute1: false,
        myAttribute2: true
    }
});

var MyModel = Backbone.NestedModel.extend({
    defaults: {
        obj1: new Obj()
    },

    models: [{key: 'obj1', model: Obj}]
});

NestedModel이하는 일은 이러한 작업을 허용하는 것입니다 (이는 JSON 데이터를 통해 myModel이 설정 될 때 발생합니다).

var myModel = new MyModel();
myModel.set({ obj1: { myAttribute1: 'abc', myAttribute2: 'xyz' } });
myModel.set('obj1', { myAttribute1: 123, myAttribute2: 456 });

초기화시 자동으로 모델 목록을 생성하는 것은 쉽지만이 솔루션은 저에게 충분했습니다.


2

Domenic이 제안한 솔루션에는 몇 가지 단점이 있습니다. '변경'이벤트를 듣고 싶다고 가정 해 보겠습니다. 이 경우 'initialize'메소드가 실행되지 않고 속성에 대한 사용자 정의 값이 서버의 json 객체로 대체됩니다. 내 프로젝트에서 나는이 문제에 직면했습니다. 모델의 'set'메서드를 재정의하는 내 솔루션 :

set: function(key, val, options) {
    if (typeof key === 'object') {
        var attrs = key;
        attrs.content = new module.BaseItem(attrs.content || {});
        attrs.children = new module.MenuItems(attrs.children || []);
    }

    return Backbone.Model.prototype.set.call(this, key, val, options);
}, 

0

Domenic이 언급했듯이 중첩 된 Object 속성 대신 Backbone 모델을 사용하는 경우가 있지만 더 간단한 경우에는 모델에서 setter 함수를 만들 수 있습니다.

var MyModel = Backbone.Model.extend({
    defaults: {
        obj1 : {
            "myAttribute1" : false,
            "myAttribute2" : true,
        }
    },
    setObj1Attribute: function(name, value) {
        var obj1 = this.get('obj1');
        obj1[name] = value;
        this.set('obj1', obj1);
    }
})

0

중첩 구조가있는 개체가 필요한 백엔드와 상호 작용하는 경우. 그러나 백본을 사용하면 선형 구조로 작업하기가 더 쉽습니다.

backbone.linear 가 도움이 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.