Knockout.js에서 관찰 가능한 바인딩을 지우거나 제거하는 방법은 무엇입니까?


113

사용자가 여러 번 수행 할 수있는 기능을 웹 페이지에 구축하고 있습니다. 사용자의 액션을 통해 ko.applyBindings ()를 사용하여 객체 / 모델을 생성하고 HTML에 적용합니다.

데이터 바인딩 된 HTML은 jQuery 템플릿을 통해 생성됩니다.

여태까지는 그런대로 잘됐다.

두 번째 개체 / 모델을 만들고 ko.applyBindings ()를 호출하여이 단계를 반복하면 두 가지 문제가 발생합니다.

  1. 마크 업은 이전 개체 / 모델과 새 개체 / 모델을 표시합니다.
  2. 객체 / 모델의 속성 중 하나와 관련하여 자바 스크립트 오류가 발생하지만 여전히 마크 업에 렌더링되어 있습니다.

이 문제를 해결하기 위해 첫 번째 패스 후 jQuery의 .empty ()를 호출하여 모든 데이터 바인딩 속성을 포함하는 템플릿 HTML을 제거하여 더 이상 DOM에 있지 않도록합니다. 사용자가 두 번째 패스를 위해 프로세스를 시작하면 데이터 바인딩 된 HTML이 DOM에 다시 추가됩니다.

그러나 내가 말했듯이 HTML이 DOM에 다시 추가되고 새 개체 / 모델에 다시 바인딩되면 첫 번째 개체 / 모델의 데이터가 여전히 포함되며 발생하지 않는 JS 오류가 계속 발생합니다. 첫 번째 패스 동안.

결론은 마크 업이 DOM에서 제거되었지만 Knockout이 이러한 바인딩 된 속성을 유지하고 있다는 것입니다.

그래서 제가 찾고있는 것은 Knockout에서 이러한 바인딩 된 속성을 제거하는 수단입니다. 더 이상 관찰 가능한 모델이 없다고 녹아웃에게 말하고 있습니다. 이를 수행하는 방법이 있습니까?

편집하다

기본 프로세스는 사용자가 파일을 업로드하는 것입니다. 그런 다음 서버는 JSON 개체로 응답하고 데이터 바인딩 된 HTML이 DOM에 추가 된 다음 JSON 개체 모델이 다음을 사용하여이 HTML에 바인딩됩니다.

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

사용자가 모델에서 일부를 선택하면 동일한 객체가 서버에 다시 게시되고 데이터 바인딩 된 HTML이 DOM에서 제거되고 다음 JS가 생성됩니다.

mn.AccountCreationModel = null;

사용자가이 작업을 한 번 더 원하면이 모든 단계가 반복됩니다.

jsFiddle 데모를 수행하기에는 코드가 너무 '관련'되어 있습니다.


특히 동일한 포함 dom 요소에서 ko.applyBindings를 여러 번 호출하는 것은 권장되지 않습니다. 원하는 것을 달성하는 다른 방법이있을 수 있습니다. 하지만 더 많은 코드를 제공해야합니다. 가능한 경우 jsfiddle을 포함하십시오.
madcapnmckay 2012

init적용 할 데이터를 전달 하는 함수를 노출하지 않는 이유는 무엇 입니까?
KyorCode

답변:


169

DOM 요소에서 녹아웃의 깨끗한 노드 메서드를 호출하여 메모리 바인딩 된 개체를 처리해 보셨습니까?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

그런 다음 새 뷰 모델로 해당 요소에만 녹아웃 바인딩을 다시 적용하면 뷰 바인딩이 업데이트됩니다.


33
이것은 작동합니다-감사합니다. 그래도이 방법에 대한 문서를 찾을 수 없습니다.
awj awj

나도이 녹아웃 유틸리티 기능에 대한 문서를 검색했습니다. 소스 코드에서 알 수 delete있듯이, 모든 녹아웃 마법이 저장되어있는 dom 요소 자체의 특정 키를 호출 하고 있습니다. 누군가 문서에 대한 출처를 가지고 있다면 나는 많은 의무가 있습니다.
Patrick M

2
당신은 그것을 찾을 수 없습니다. ko 유틸리티 기능에 대한 문서를 높고 낮게 검색했지만 아무것도 존재하지 않습니다. 이 블로그 게시물은 가장 가까운 게시물이지만 ko.utils의 회원 만 다룹니다. knockmeout.net/2011/04/utility-functions-in-knockoutjs.html
Nick Daniels

1
아래 내 답변에 표시된 것처럼 수동으로 이벤트를 제거하고 싶을 것입니다.
Michael Berkompas

1
@KodeKreachor 나는 이미 아래 작업 예제를 게시했습니다. 내 요점은 바인딩을 그대로 유지하면서 대신 ViewModel 내에서 데이터를 해제하는 것이 더 낫다는 것입니다. 이렇게하면 바인딩 해제 / 재결합을 처리 할 필요가 없습니다. 문서화되지 않은 메서드를 사용하여 DOM에서 직접 바인딩을 해제하는 것보다 깔끔해 보입니다. 그 외에도 CleanNode는 이벤트 핸들러를 해제하지 않기 때문에 문제가 있습니다 (자세한 내용은 여기에 대한 답변 참조 : stackoverflow.com/questions/15063794/… )
Zac

31

작업중인 프로젝트의 ko.unapplyBindings경우 jQuery 노드와 부울 제거를 허용하는 간단한 함수를 작성했습니다 . ko.cleanNode메서드가 처리하지 않으므로 먼저 모든 jQuery 이벤트의 바인딩을 해제합니다 . 메모리 누수를 테스트했는데 제대로 작동하는 것 같습니다.

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

한 가지주의 할 점 ko.cleanNode()은 전체 html이 대체되지 않고 방금 호출 된 항목에 대한 리 바인딩을 테스트 하지 않았습니다.
Michael Berkompas 2011

4
솔루션이 다른 모든 이벤트 바인딩도 해제하지 않습니까? ko의 이벤트 핸들러 만 제거 할 수 있습니까?
lordvlad

한 코 코어 변경없이
lordvlad

1
사실이지만 핵심 변경 없이는 말할 수있는 한 불가능합니다. : 여기 양육이 문제를 참조하십시오 github.com/SteveSanderson/knockout/issues/724
마이클 Berkompas

KO의 생각은 당신이 직접 돔을 거의 만지지 않아야한다는 생각이 아닙니까? 이 답변은 dom을 반복하며 내 사용 사례에서는 확실히 사용할 수 없습니다.
Blowsie 2013 년

12

녹아웃이 제공하는 with binding을 사용해 볼 수 있습니다. http://knockoutjs.com/documentation/with-binding.html 적용 바인딩을 한 번 사용하고 데이터가 변경 될 때마다 모델을 업데이트하는 것이 아이디어입니다.

최상위 뷰 모델 storeViewModel, cartViewModel로 표시되는 카트 및 해당 카트의 항목 목록 (예 : cartItemsViewModel)이 있다고 가정 해 보겠습니다.

최상위 모델 인 storeViewModel을 전체 페이지에 바인딩합니다. 그런 다음 카트 또는 카트 항목을 담당하는 페이지 부분을 분리 할 수 ​​있습니다.

cartItemsViewModel에 다음 구조가 있다고 가정합니다.

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

cartItemsViewModel은 처음에 비어있을 수 있습니다.

단계는 다음과 같습니다.

  1. html로 바인딩을 정의하십시오. cartItemsViewModel 바인딩을 분리합니다.

      
        <div data-bind="with: cartItemsViewModel">
          <div data-bind="foreach: CartItems">
            <span data-bind="text: ItemName"></span>
            <span data-bind="text: Price"></span>
          </div>
        </div>
      
    
  2. 상점 모델은 서버에서 가져옵니다 (또는 다른 방법으로 생성됨).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. 최상위 뷰 모델에서 빈 모델을 정의합니다. 그런 다음 해당 모델의 구조를 실제 데이터로 업데이트 할 수 있습니다.

      
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
    
  4. 최상위 뷰 모델을 바인딩합니다.

    ko.applyBindings(storeViewModel);

  5. cartItemsViewModel 개체를 사용할 수 있으면 이전에 정의한 자리 표시 자에 할당합니다.

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

장바구니 항목을 지우려면 : storeViewModel.cartItemsViewModel(null);

Knockout은 html을 처리합니다. 즉, 모델이 비어 있지 않을 때 나타나고 div의 내용 ( "바인딩 포함"이있는 내용)이 사라집니다.


9

검색 버튼을 클릭 할 때마다 ko.applyBinding을 호출해야하며 필터링 된 데이터는 서버에서 반환되며이 경우에는 ko.cleanNode를 사용하지 않고 다음 작업을 수행합니다.

foreach를 템플릿으로 바꾸면 collections / observableArray의 경우 잘 작동 할 것임을 경험했습니다.

이 시나리오가 유용 할 수 있습니다.

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

1
비슷한 문제를 해결하기 위해 최소 4 시간을 보냈고 aamir의 솔루션 만 저에게 효과적입니다.
Antonin Jelinek 2013

@AntoninJelinek 내 시나리오에서 경험 한 다른 것, html을 완전히 제거하고 동적으로 다시 추가하여 모든 것을 완전히 제거합니다. 예를 들어 $ .Ajax 성공 결과에 Knockout 코드 컨테이너 div <div id = "knockoutContainerDiv"> </ div>가 있습니다. 서버 메서드가 $ ( "# knockoutContainerDiv"). children.remove (); /를 호출 할 때마다 다음을 수행합니다. / remove content of it call the method to append dynamic html with knockout code $ ( "# knockoutContainerDiv"). append ( "childelements with knockout binding code") 및 applyBinding 다시 호출
aamir sajjad 2013

1
암 미르, 나도 너와 거의 같은 시나리오를 가졌어. 내가 ko.cleanNode (element)를 사용해야한다는 사실을 제외하고는 당신이 언급 한 모든 것이 훌륭하게 작동했습니다. 다시 바인딩하기 전에.
Radoslav Minchev 2014

@RadoslavMinchev가 더 도움이 될 수 있다고 생각하십니까? 그렇다면 특정 질문에 대한 제 생각 / 경험을 공유하게되어 기쁩니다.
아미르는 사자 드

고마워. @aamirsajjad, 저에게 효과가 있었던 것은 cleanNode () 함수를 호출하여 작동하도록하는 것이라고 언급하고 싶었습니다.
Radoslav Minchev 2014

6

KO의 내부 함수를 사용하고 JQuery의 포괄적 인 이벤트 핸들러 제거를 처리하는 대신에 with또는 template바인딩을 사용하는 것이 훨씬 더 좋습니다 . 이렇게하면 ko는 DOM의 해당 부분을 다시 생성하므로 자동으로 정리됩니다. 이것은 또한 권장되는 방법입니다. https://stackoverflow.com/a/15069509/207661을 참조하십시오 .


4

바인딩을 전체 시간 동안 유지하고 관련 데이터를 업데이트하는 것이 더 나을 것이라고 생각합니다. 이 문제가 발생하여 .resetAll()데이터를 보관하고있는 배열에서 메서드를 사용하여 호출하는 것이 가장 효과적인 방법이라는 것을 알았습니다.

기본적으로 ViewModel을 통해 렌더링 할 데이터를 포함하는 전역 변수로 시작할 수 있습니다.

var myLiveData = ko.observableArray();

myLiveData정상적인 배열을 만들 수 없다는 것을 깨닫는 데 시간이 걸렸습니다 ko.oberservableArray. 부품이 중요했습니다.

그런 다음 원하는대로 진행할 수 있습니다 myLiveData. 예를 들어 다음과 같이 $.getJSON전화를 겁니다.

$.getJSON("http://foo.bar/data.json?callback=?", function(data) {
    myLiveData.removeAll();
    /* parse the JSON data however you want, get it into myLiveData, as below */
    myLiveData.push(data[0].foo);
    myLiveData.push(data[4].bar);
});

이 작업을 완료하면 평소처럼 ViewModel을 사용하여 바인딩을 적용 할 수 있습니다.

function MyViewModel() {
    var self = this;
    self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());

그런 다음 HTML에서 myData평소 처럼 사용하십시오 .

이렇게하면 어떤 함수에서든 myLiveData를 사용할 수 있습니다. 예를 들어, 몇 초마다 업데이트하려면 해당 $.getJSON줄을 함수로 감싸고 호출 setInterval하면됩니다. myLiveData.removeAll();줄 을 유지하는 것을 기억하는 한 바인딩을 제거 할 필요가 없습니다 .

데이터가 정말 방대하지 않으면 사용자는 어레이를 재설정 한 다음 최신 데이터를 다시 추가하는 사이의 시간도 알아 차리지 못할 것입니다.


질문을 게시 한 이후로 이것이 제가 지금하는 일입니다. 이러한 Knockout 메서드 중 일부는 문서화되지 않았거나 실제로 수행하는 작업을 찾기 위해 (이미 함수 이름을 알고 있음) 주위를 둘러 봐야합니다.
awj

나는 이것을 찾기 위해 매우 힘들게보아야했다 (문서를 파헤치는 시간이 결과적인 코드 줄 수를 초과 할 때 ... 와우). 작동하게되어 기쁩니다.
Zac


1

이것에 대해 생각해 보셨습니까?

try {
    ko.applyBindings(PersonListViewModel);
}
catch (err) {
    console.log(err.message);
}

Knockout에서이 코드를 찾았 기 때문에 이것을 생각해 냈습니다.

    var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
    if (!sourceBindings) {
        if (alreadyBound) {
            throw Error("You cannot apply bindings multiple times to the same element.");
        }
        ko.utils.domData.set(node, boundElementDomDataKey, true);
    }

그래서 나에게 그것은 이미 묶여있는 문제가 아니며 오류가 포착되지 않고 처리되지 않았다는 것입니다 ...


0

뷰 모델에 많은 div 바인딩이 포함 된 경우이를 지우는 가장 좋은 방법 ko.applyBindings(new someModelView);은 사용하는 것입니다. ko.cleanNode($("body")[0]);이렇게하면 ko.applyBindings(new someModelView2);이전 뷰 모델이 여전히 바인딩 될 염려없이 새를 동적으로 호출 할 수 있습니다 .


4
추가하고 싶은 몇 가지 사항이 있습니다. (1) 웹 페이지에서 모든 바인딩이 지워 지므로 응용 프로그램에 적합 할 수 있지만 개별적으로 페이지의 여러 부분에 바인딩이 추가 된 응용 프로그램이 많이 있다고 생각합니다. 원인. 단일 스위핑 명령으로 모든 바인딩을 정리하는 것은 많은 사용자에게 도움이되지 않을 수 있습니다. (2) 더 빠르고 효율적인 네이티브 JavaScript 검색 방법 $("body")[0]document.body.
awj 2013
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.