2014-09-08 8 views
5

Ho un'applicazione ember con risorse nidificate in cui sto mostrando video. La risorsa esterna (video) mostra semplicemente tutti i video. Quando si fa clic su un video, la risorsa nidificata video viene attivata e viene visualizzato un titolo/lettore.Ember che aggiorna le fonti video ma non viene riflesso dal browser

Questo funziona perfettamente prima del tempo impostato su. Il video appare e suona. TUTTAVIA, quando si fa clic su un altro video, il video della risorsa nidificata viene aggiornato e i DOM <source> vengono aggiornati, ma il video OLD continua a essere riprodotto! Perché sta succedendo questo e come lo risolvo?

Working Example on JSFiddle

+0

La mia ipotesi è che il tag di origine non supporta dinamicamente modificare lo src dopo che è caricato . – Kingpin2k

+0

http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#the-source-element, Modifica dinamica di un elemento di origine e del relativo attributo quando l'elemento è già inserito in un video o l'elemento audio non avrà alcun effetto. – Kingpin2k

+0

Interessante @ Kingpin2k Come si può risolvere in modo "ember"? –

risposta

3

userei un componente per creare il lettore video, e concludere la logica nuovamente sottoposti a rendering nel componente.

Il componente sarebbe simile

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

e il modello di componente sarebbe simile

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

Si sarebbe quindi in grado di solo riferimento al componente nel modello come questo

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

È possibile visualizzare un raccoglitore di lavoro qui: http://emberjs.jsbin.com/fehipa/2/edit


Come bonus .. Se è stato creato un componente video player si sarebbe anche in grado di avvolgere la capacità di mettere in pausa e riprodurre il video nel componente. In modo che tu possa avere un riproduttore video totalmente riutilizzabile da utilizzare in qualsiasi punto della tua app.

+0

All'interno della definizione del componente, è necessario creare una funzione separata che assicuri innanzitutto che la vista sia ancora nel DOM prima di chiamare il rerender. Quindi dovresti rimbalzare quella funzione. In caso contrario, le condizioni di gara e i test di integrazione rapidi possono attivare asserzioni all'avvio del debounce e la vista non è nel DOM. Altrimenti, +1 –

+0

Buon punto. Aggiornerò la risposta – tikotzky

+0

Perché utilizzare il debounce su una delle altre funzioni 'Ember.run'? Inoltre, se il debounce è davvero quello che dovrebbe essere usato perché 200ms? È solo un numero arbitrario che hai selezionato? –

0

E 'un po' brutto, ma questo può essere raggiunto dal rerendering la vista. Aggiungere il seguente codice:

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

Grazie per questo. Anche se questo funziona, aspetterò un po 'prima di assegnare punti per vedere se c'è una soluzione elegante per le mosse. –

+0

Penso che il sentimento di rerenderizzazione della vista sia corretto. Ma non è compito della via fare questo. Al suo posto dovrebbe essere usata una vista. –

0

vorrei adottare un approccio simile a @AlliterativeAlice, ma avrei sicuramente non raccomandare fare questa operazione a livello di percorso. Questo problema è una conseguenza della sicurezza DOM e quindi è un lavoro per la vista. La cosa migliore è impostare un osservatore nella classe di visualizzazione e chiamare il rerender da lì. Es .:

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

ho anche aggiornato il vostro violino per fare questo lavoro: http://jsfiddle.net/rverobx9/2/

0

Mentre le risposte che suggeriscono di eseguire il re-rendering in qualsiasi modo potrebbero risolvere il problema, se si ha accesso all'API del lettore sarebbe bello vedere se l'interruzione del video nella schermata willDestroyElement non verrà risolta quella.

Inoltre si potrebbe avere il lettore video sia una componente Ember, garantendo una buona messa a punto, teardown e aggiornamento:

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') 
}); 

Questo vi permetterà di essere sicuri che tutto sia impostato e teardown correttamente, e dato che è un componente puoi riutilizzare, sarà molto facile avere un ripiego su un flash player, per esempio.Quindi tutto ciò che si deve gestire e risolvere cose relative al giocatore, sarà a tale componente, e ti resta che sostituire la parte del vostro modello con il giocatore con:

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

Perché qualcuno ha votato meno questa risposta? C'è un negativo qui che non vedo? –

+0

Sì, come @kyledecot mi piacerebbe conoscere il motivo del downvote ... – Huafu

1

Se si sposta il {{bind-attr src="src"}} dall'elemento <source> fino all'elemento <video>, funziona senza alcun hack.

il codice cambierebbe da:

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

a

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

esempio di lavoro: http://jsfiddle.net/jfmzwhxx/

+0

Beh, il mio esempio è un po 'troppo semplificato. Ho più fonti per supportare diversi dispositivi. Altrimenti, sì, funzionerebbe. –

Problemi correlati