How to re-render component?


#1

have read a lot of guides and did not understand - how to rerender ember’s component internally? (not by user’s action).

e.g. I have a component that shows a list of objects received from websockets and want to redraw the list when a new object is received:

component js code:

import Ember from 'ember';
import Phoenix from 'ember-phoenix-chan';
const {Socket, Channel, LongPoll, Ajax, Presence} = Phoenix;
//import {Socket, LongPoller} from "phoenix"

const { get, inject } = Ember;

export default Ember.Component.extend({
    didRender() {
       console.log("didRender")
     },
	clientList: [],
	doubleClick() {
	    Ember.Logger.info("DoubleClickableComponent was clicked");
	    	

		    let socket = new Socket(ws://1.1.1.1:4000/socket", {
		      logger: ((kind, msg, data) => { console.log(`!!!! ${kind}: ${msg}`, data) })
		    })

		    //socket.connect({user_id: "123"})
		    var $guid = "Sashka";
			socket.connect({client_id: $guid, client_type: "browser", client_name: "anonymous"});


		    var chan = socket.channel("rooms:666", {});
			
		    chan.join()
			           //.receive("error", resp => { console.log("error on JOIN", resp) } )
					   //.receive("ignore", resp => { console.log("auth error", resp) } )
			           .receive("ok", resp => { console.log("join ok", resp) } )
			           //.after(10000, resp => { console.log("Connection interruption", resp) })
			
		    chan.onError(e => console.log("something went wrong", e))
		    chan.onClose(e => console.log("channel closed", e))
/*
		    $input.off("keypress").on("keypress", e => {
		      if (e.keyCode == 13) {
		        chan.push("new:msg", {user: $username.val(), body: $input.val()})
		        $input.val("")
		      }
		    })
*/
			chan.on("join", obj => {
				console.log("rcvd JOIN", obj)
				this.get('clientList').push(obj)
				this.update
			})
			
			chan.on("client:connected", obj => {
				console.log("rcvd CLCND", obj)
			})
			
		    chan.on("new:msg", msg => {
				console.log("NEW:MSG")
		      $messages.append(this.messageTemplate(msg))
		      scrollTo(0, document.body.scrollHeight)
		    })

		    chan.on("user:entered", msg => {
		      var username = this.sanitize(msg.user || "anonymous")
		      $messages.append(`<br/><i>[${username} entered]</i>`)
		    })
		  

		return true;
		
	  }
});

Components tamplate:

{{outlet}}

websockets sample

<br>
<div class="page">
  <div class="content-padded">
    {{#each messages as |message|}}
      {{message}}
    {{/each}}
	<br><br>
    <div class="form-control">
      <label>New Message</label>
      {{input value=newMessage class="form-control"}}
      <button class="btn" {{action 'sendMessage'}}>Send Message</button>
    </div>
  </div>
</div>
<div class="footer">
</div>

{{#ws-component}}
click here!
{{/ws-component}}

Objects List:

{{#each clientListD as |client|}}
  <li>{{client}}</li>
{{/each}}

the problem is that when this.get(‘clientList’).push(obj) happens, the template is not redrawing the list, i want to redraw it correctly after the list is changed. how?


#2

It is not the good solution but you can force the component to update:

Wrap your template code in an

{{#if myTemp}}
       //re render component goes here
{{/if}}

and then just update the myTemp variable -> true / false

This will force update the component.

some time some code is still runing then it is better to use

this.set('temp',false);
setTimeout(()=>{
  this.set('myTemp',true)
},2);

#3

This is a really bad idea and no one should need to do this.

The issue in the original post is he/she was mutating an array without going through any of the Array,proto extension methods or Ember.NativeArray methods that will understand a change occurred (pushObject, addObject, removeAt, insertAt, etc.etc.).

Example:


#4

Your clientList should be defined like: clientList: A() (Ember array, at Ember.A) and to push use this.get('clientList').pushObject(obj).

Besides that, I would handle key presses with Ember (see keyPress), not registering a jQuery once.