2014-09-08 5 views
5

Ich habe eine Ember-Anwendung mit verschachtelten Ressourcen, in denen ich Videos zeige. Die äußere Ressource (Videos) zeigt einfach alle Videos an. Wenn Sie auf ein Video klicken, wird die verschachtelte Ressource video aktiviert und ein Titel/Player wird angezeigt.Ember Aktualisieren von Videoquellen, aber nicht vom Browser reflektiert

Das funktioniert gut die erste Zeit, die Sie etwas klicken. Das Video wird angezeigt und es wird abgespielt. Wenn Sie jedoch auf ein anderes Video klicken, wird das verschachtelte Ressourcenvideo aktualisiert und die DOMs <source> werden aktualisiert, das OLD-Video wird jedoch weiterhin wiedergegeben. Warum passiert das und wie repariere ich es?

Working Example on JSFiddle

+0

Meine Vermutung ist, dass der Quell-Tag nicht dynamisch die src unterstützt Modifizieren, nachdem es geladen ist . – Kingpin2k

+0

http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#the-source-element, Dynamisches Ändern eines Quellenelements und seines Attributs, wenn das Element bereits in ein Video eingefügt wurde oder Audio-Element wird keine Wirkung haben. – Kingpin2k

+0

Interessant @ Kingpin2k Wie würde man das auf "Glut" -Lösung lösen? –

Antwort

3

Ich würde eine Komponente verwenden, um den Video-Player zu erstellen, und wickeln Sie die rerender Logik in der Komponente auf.

Die Komponente wäre so etwas wie

App.VideoPlayerComponent = Ember.Component.extend({ 
    src: null, 
    rerenderPlayer: function(){ 
    if(this.$()) { 
     Ember.run.debounce(this, this.rerender, 200); 
    } 
    }, 
    srcObserver: function(){ 
    this.rerenderPlayer(); 
    }.observes('src') 
}); 

aussehen und die Komponentenvorlage würde aussehen wie

<script type="text/x-handlebars" data-template-name="components/video-player"> 
    <video controls> 
    <source {{bind-attr src=src}} type = "video/mp4"></source> 
    </video> 
</script> 

Sie würden dann in der Lage sein zu verweisen nur die Komponente in Ihrer Vorlage wie diese

<script type="text/x-handlebars" data-template-name="video"> 
    <h2>{{title}}</h2> 
    {{video-player src=src}} 
</script> 

Sie können einen Arbeitsbehälter hier sehen: http://emberjs.jsbin.com/fehipa/2/edit


Als Bonus .. Wenn Sie eine Video-Player-Komponente erstellt würden Sie auch in der Lage sein, die Fähigkeit zu einpacken zu pausieren und das Video in der Komponente zu spielen. So können Sie einen vollständig wiederverwendbaren Videoplayer verwenden, den Sie überall in Ihrer App verwenden können.

+0

Innerhalb Ihrer Komponentendefinition sollten Sie eine separate Funktion erstellen, die zuerst sicherstellt, dass sich die Ansicht noch im DOM befindet, bevor Sie rerender aufrufen. Dann sollten Sie diese Funktion entprellen. Andernfalls können Racebedingungen und schnelle Integrationstests Assertionen auslösen, wenn das Entprellen ausgelöst wird und die Ansicht nicht im DOM ist. Ansonsten, +1 –

+0

Guter Punkt. Ich werde die Antwort aktualisieren. – tikotzky

+0

Warum Entprellen über eine der anderen 'Ember.run' Funktionen? Auch, wenn Debounce wirklich was ist, warum sollten 200ms verwendet werden? Ist das nur eine willkürliche Nummer, die Sie ausgewählt haben? –

0

Es ist ein bisschen hässlich, aber dies kann durch rerendering die Ansicht erreicht werden. Fügen Sie den folgenden Code:

App.VideoRoute = Ember.Route.extend({ 
    oldVideoId: null, 
    afterModel: function (resolvedModel) { 
     if (this.get('oldVideoId') !== resolvedModel.id) { 
      this.set('oldVideoId', resolvedModel.id); 
      $.each(Ember.View.views, function(i, view) { 
       if (view.renderedName === "videos"){ 
        view.rerender(); 
       } 
      }); 
     } 
    } 
}); 

Working Fiddle

+0

Danke dafür. Auch wenn das funktioniert, werde ich ein wenig warten, bevor ich irgendwelche Punkte vergebe, um zu sehen, ob es eine elegante Lösung gibt. –

+0

Ich denke, das Gefühl, die Sicht neu zu beleben, ist richtig. Aber es ist nicht die Aufgabe, dies zu tun. Stattdessen sollte eine Ansicht verwendet werden. –

0

Ich würde einen ähnlichen Ansatz zu @AlliterativeAlice, aber ich würde auf jeden Fall nicht empfehlen auf der Route Ebene dieser Operation zu tun. Dieses Problem ist eine Konsequenz der DOM-Sicherheit und daher eine Aufgabe für die Ansicht. Am besten stellst du einen Beobachter in deiner Sichtklasse ein und rennst von dort aus rerender an. Z.B .:

App.VideoView = Ember.View.extend({ 
    _didChangeVideoSrc: function() { 
    // Make the view is still in the DOM 
    if(this.$()) { 
     this.rerender(); 
    } 
    }.observes('controller.src') 
}); 

I aktualisiert Ihre Geige auch diese Arbeit zu machen: http://jsfiddle.net/rverobx9/2/

0

Während Antworten, die vorschlagen, in irgendeiner Weise zu rendern, Ihr Problem möglicherweise lösen, wenn Sie Zugriff auf die API des Players haben, wäre es schön zu sehen, wenn das Video in der willDestroyElement Ihrer Ansicht nicht beheben wird Das.

Auch könnten Sie Ihren Video-Player ist eine Ember Komponente haben, um sicherzustellen gutes Setup, Teardown und Update:

App.VideoPlayerComponent = Ember.Component.extend({ 
    source: null, // used in the template using that component 
    isPlayerReady: false, // used to know internally if the player is ready 

    setupPlayer: (function(){ 
     // somehow setup the video player if needed 
     // (if Flash player for example, make the flash detection and whatever) 
     // once ready, trigger our `videoPlayerReady` event 
     this.set('isPlayerReady', true); 
     this.trigger('videoPlayerReady'); 
    }).on('didInsertElement'), 

    updatePlayerSource: (function(){ 
     // start playing if we have a source and our player is ready 
     if (this.get('isPlayerReady') && this.get('source')) 
     { 
     // replace with the correct js to start the video 
     this.$('.class-of-player').videoPlay(this.get('source')); 
     } 
    }).observes('source').on('videoPlayerReady'), 

    teardownSource: (function(){ 
     // stop playing if our player is ready since our source is going to change 
     if (this.get('source') && this.get('isPlayerReady')) 
     { 
     // replace with the correct js to stop the player 
     this.$('.class-of-player').videoStop(); 
     } 
    }).observesBefore('source'), 

    teardownPlayer: (function(){ 
     // teardown the player somehow (do the opposite of what is setup in `setupPlayer`) 
     // then register that our player isn't ready 
     this.set('isPlayerReady', false); 
    }).on('willDestroyElement') 
}); 

Dies ermöglicht es Ihnen sicher zu sein, alles ist Setup und Teardown richtig, und da es eine Komponente Sie können es wiederverwenden, es ist sehr einfach, einen Fallback zu einem Flash-Player zum Beispiel zu haben.Dann, was Sie haben zu handhaben und so den Spieler im Zusammenhang zu beheben, wird es in dieser Komponente sein, und Sie müssen nur den Teil der Vorlage ersetzen mit dem Spieler mit:

{{video-player source=ctrl.videoUrl}} 
+1

Warum hat jemand diese Antwort abgelehnt? Gibt es hier ein Negativ, das ich nicht sehe? –

+0

Ja, als @ kyledecot würde ich gerne den Grund des Downvote wissen ... – Huafu

1

Wenn Sie die bewegen {{bind-attr src="src"}} vom <source> Element bis zum <video> Element, es funktioniert einfach ohne irgendwelche Hacks.

Code würde von ändern:

<video controls> 
    <source {{bind-attr src="src"}} type = "video/mp4"></source> 
</video> 

zu

<video controls {{bind-attr src="src"}}></video> 

Arbeits Beispiel: http://jsfiddle.net/jfmzwhxx/

+0

Nun, mein Beispiel ist etwas zu stark vereinfacht. Ich habe mehrere Quellen zur Unterstützung verschiedener Geräte. Ansonsten würde ja das funktionieren. –