• Recent
  • Tags
  • Unsolved
  • Solved
  • MagicMirror² Repository
  • Documentation
  • 3rd-Party-Modules
  • Donate
  • Discord
  • Register
  • Login
MagicMirror Forum
  • Recent
  • Tags
  • Unsolved
  • Solved
  • MagicMirror² Repository
  • Documentation
  • 3rd-Party-Modules
  • Donate
  • Discord
  • Register
  • Login
A New Chapter for MagicMirror: The Community Takes the Lead
Read the statement by Michael Teeuw here.

Nunjucks and Chartjs / Javascript

Scheduled Pinned Locked Moved Development
15 Posts 2 Posters 3.5k Views 2 Watching
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    lavolp3 Module Developer @lavolp3
    last edited by Sep 27, 2019, 3:17 PM

    Wait. There is an asynch function in the getdom function, isn’t there? So calling my function directly afterwards may lead to a state where the dom is not yet created. Hmm…

    How to troubleshoot modules
    MMM-soccer v2, MMM-AVStock

    S 1 Reply Last reply Sep 27, 2019, 5:11 PM Reply Quote 0
    • S Offline
      sdetweil @lavolp3
      last edited by Sep 27, 2019, 5:11 PM

      @lavolp3 the dom is created way back at the beginning when u receved the DOM_OBJECTS_CREATED notification… so, when your module is called later, the DOM exists… document. exists

      YOUR contribution to the dom may not be there for some time after your getDom() routine returns. MM does not specify how long…

      if I need to fiddle with the dom directly, I usually do that thru a time routine, set for 1-2 seconds after getdom returns.

      Sam

      How to add modules

      learning how to use browser developers window for css changes

      L 1 Reply Last reply Sep 30, 2019, 3:29 PM Reply Quote 0
      • L Offline
        lavolp3 Module Developer @sdetweil
        last edited by Sep 30, 2019, 3:29 PM

        @sdetweil
        Sam, thanks again for your valuable contribution.
        I tried to meddle with the getdom function, which does not work because the returned module div is appended to a parent node somewhere else in the MM code hierarchy. And only then I can refer to the DOM elements. (I guess that’s what you meant and I haven’t understood).

        So, although I didn’t like it, I kind of did it your way and included a 3-second setTimeout() call directly after the updateDom() call, in which I called my chart function.

                            self.updateDom(self.config.animationSpeed);
                            setTimeout (function() {
                              self.createBarChart(...);
                            }, 3000);
        
        

        It works now.
        I still don’t like it. :-)
        But I want to move on.

        How to troubleshoot modules
        MMM-soccer v2, MMM-AVStock

        S 1 Reply Last reply Sep 30, 2019, 3:46 PM Reply Quote 0
        • S Offline
          sdetweil @lavolp3
          last edited by Sep 30, 2019, 3:46 PM

          @lavolp3 said in Nunjucks and Chartjs / Javascript:

          I tried to meddle with the getdom function, which does not work because the returned module div is appended to a parent node somewhere else in the MM code hierarchy. And only then I can refer to the DOM elements. (I guess that’s what you meant and I haven’t understood).

          main.js

          	var updateModuleContent = function(module, newHeader, newContent) {
          		var moduleWrapper = document.getElementById(module.identifier);
                  if (moduleWrapper === null) return;
          		var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
          		var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
          
          		contentWrapper[0].innerHTML = "";
          		contentWrapper[0].appendChild(newContent);
          
          		if( headerWrapper.length > 0 && newHeader) {
          			headerWrapper[0].innerHTML = newHeader;
          		}
          	};
          

          takes the content returned from getDom() and inserts it into the div created by module_id in the dom tree in the ‘position’ specified.

          in my code that is why i create the div ‘canvas’. I have the div object, in getDom, and can generate the content immediately… no timer waiting to look it up later

                      canvas = document.createElement("canvas");    // < ---
                      canvas.id = "myChart" + this_pin;
                      c.appendChild(canvas);                                           // < ---
                    }
                    // if the chart has been created
                    if (wLself.charts[pin_index] != null) {
                        // destroy it, update doesn't work reliably
                        wLself.charts[pin_index].destroy();
                        // make it unreferenced
                        wLself.charts[pin_index] = 0;
                    }
          					try { 
          						// create it now
          						wLself.charts[pin_index] = new Chart(canvas, {   // < ---
          

          then getDom() returns the completed chart… no need for build later

          Sam

          How to add modules

          learning how to use browser developers window for css changes

          L 1 Reply Last reply Sep 30, 2019, 10:11 PM Reply Quote 0
          • S Offline
            sdetweil
            last edited by sdetweil Sep 30, 2019, 4:52 PM Sep 30, 2019, 4:04 PM

            the dom looks like this before any content is output (no < > to save errors)
            matching index.html, with a div for each module, in its position div section
            in fifo order

            head
            body
                div class=fullscreen below"
                    div id="module_id_1" 
                        div class="module-header"
                        end-div
                        div class="module-content"
                        end div
                    end-div
                    div id="module_id_2"
                    end-div
               div class="region top bar"
                  div class="container",
                      end-div
                  		div class="region top left" 
                                div class="container"
                                end-div
                            end-div
                               etc
                  	</div>
            

            then getDom() in each module is called, and its little html contribution is placed in its div id=“module-content” (by module_id)

            but, until the dom.div.addChild(content) is done, the content is just in memory, NOT IN the dom…

            then later the module signals the mirror runtime via updateDom()… ‘I have new content’,
            and MM runtime calls the module’s getDom() and we repeat the process… note in the updateModule content
            it REMOVES the prior content and injects the new…

                            contentWrapper[0].innerHTML = "";  // < --- clear all content, the rough way
            		contentWrapper[0].appendChild(newContent);  // < --- append the new module content tree
            

            SO, BIG dom object tree cleanup and rebuild…

            i keep the module contribution, and modify it where appropriate, to reduce the change impact.

            I always think the getDom() should be named getDomContribution()

            Sam

            How to add modules

            learning how to use browser developers window for css changes

            1 Reply Last reply Reply Quote 1
            • L Offline
              lavolp3 Module Developer @sdetweil
              last edited by Sep 30, 2019, 10:11 PM

              @sdetweil
              Well…I have to say I’m not through with this.

              getDom creates/updates the module-specific dom objects. Right?
              So let’s say there’s two ways to have a canvas:

              1. document.createElement in an overwritten getDom method
              2. Nunjucks template, which is being converted into HTML during getDom.

              The Nunjucks way seems asynchronous, overwriting the getDom however makes it synchronous and has the advantage of “immediate full control”. You can then code your graph directly into the getdom method (like you have done as far as I understand). All good, but does not work with Nunjucks without messing with several layers of core MM code.

              OR you can use another process (like socketNotification from Node_helper) to do

              1. getCanvasById and
              2. write the graph into it.
                That should work with both options, the async nunjucks and the overwritten getDom, since it is on a completely different chain of commands.

              That didn’t work for me with Nunjucks, because it seems to do rewrite more often than just with getDom. I remember that someone on this forum had the same assumption.

              Thanks for the intersting discussion. Learned a lot and will try out a bit more.

              How to troubleshoot modules
              MMM-soccer v2, MMM-AVStock

              S 1 Reply Last reply Sep 30, 2019, 10:29 PM Reply Quote 0
              • S Offline
                sdetweil @lavolp3
                last edited by sdetweil Sep 30, 2019, 10:31 PM Sep 30, 2019, 10:29 PM

                @lavolp3 both your 1 and 2 are processed as async, just 1 is done when getDom() returns a dom object tree.
                main.js

                updateDom() calls getDom() immediately. and then checks what it gets back.

                either a promise (generated content will return later) or dom objects (all done generating)
                if content, then it creates a promise and resolves it immediately with the data
                (something will wait on the promise eventually)

                now we have a promise, so can wait (.then), get the data (either approach), and then inject it into the dom.(updateDomWithContent)
                (via a promise, as it may take a while to do that update, don’t want to block everybody else)
                and then call back whoever called updateDom()

                var updateDom = function(module, speed) {
                		return new Promise(function(resolve) {
                			var newContentPromise = module.getDom();
                			var newHeader = module.getHeader();
                
                			if (!(newContentPromise instanceof Promise)) {
                				// convert to a promise if not already one to avoid if/else's everywhere
                				newContentPromise = Promise.resolve(newContentPromise);
                			}
                
                			newContentPromise.then(function(newContent) {
                				var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
                
                				updatePromise.then(resolve).catch(Log.error);
                			}).catch(Log.error);
                		});
                	};
                

                Sam

                How to add modules

                learning how to use browser developers window for css changes

                L 1 Reply Last reply Nov 18, 2019, 1:08 PM Reply Quote 1
                • L Offline
                  lavolp3 Module Developer @sdetweil
                  last edited by Nov 18, 2019, 1:08 PM

                  @sdetweil Sam, sorry to say that after such a long time I’m still not through with this.
                  Promises make my head explode, so I hope you can help me.

                      scheduleUpdates: function() {
                        var self = this;
                  
                        setInterval(function() {
                          var conf = self.config;
                          self.updateDom(conf.animationSpeed);
                  
                          setTimeout(function() {
                            self.createBarChart();
                          }, 2000);
                        }, this.config.updateInterval);
                      },
                  

                  This is an easified version of my loop to update the dom.
                  You can see the timeout function I have built in to wait 2 seconds and be sure the dom has been created. Then in the createBarChart function I can write the chart into the dom.
                  This works, but I don’t like it at all.
                  Isn’t there a way to “await” the resolved promise from outside of the updateDom function? After all updateDom() returns a promise.
                  I have tried to do self.updateDom().then( function() {self.createBarChart() });
                  It didn’t work.

                  Thanks for your support. Really appreciated.

                  How to troubleshoot modules
                  MMM-soccer v2, MMM-AVStock

                  S 1 Reply Last reply Nov 18, 2019, 2:33 PM Reply Quote 0
                  • S Offline
                    sdetweil @lavolp3
                    last edited by Nov 18, 2019, 2:33 PM

                    @lavolp3 updateDom() returns a promise, but then IMMEDIATELY calls getDom()…

                    so the wait is ineffective

                    all you can do is check the dom to see if your minimum content some anchoring div) is present, and retry that until it is.
                    then call updateDom() to force a refresh

                    this is what I do in getDom(). i have an array of different charts to present

                    		// if we are not suspended/hidden due to sleep or whatever
                    		if (wself.suspended == false) {
                    			// make sure we don't start before the data gets here
                    			if (!this.loaded) {    < -  just a flag for first time
                    				this.loaded = true;
                    				return wself.wrapper;
                    			} else {
                    				// loop thru the data from the blynk server, one chart per data point/pin
                    				for (var pin_index = 0; pin_index < wself.config.Pins.length; pin_index++) {
                    					// get the pin text name. used for index into the data hash
                    					var this_pin = wself.config.Pins[pin_index];
                    					// clear the work variable
                    					var canvas = null;
                    					// try to locate the existing chart
                    					if ((canvas = document.getElementById("myChart" + this_pin)) == null) {
                    						var c = document.createElement("div");
                    						c.style.width = wself.config.width + "px";
                    						c.style.height = wself.config.height + "px";
                    						if (!wself.config.stacked)
                    						     {c.style.display = "inline-block";}
                    						wself.wrapper.appendChild(c);
                    
                    						canvas = document.createElement("canvas");
                    						canvas.id = "myChart" + this_pin;
                    						c.appendChild(canvas);
                    					}
                    					// if the chart has been created
                    					if (wself.charts[pin_index] != null) {
                    						// destroy it, update doesn't work reliably
                    						wself.charts[pin_index].destroy();
                    						// make it unreferenced
                    						wself.charts[pin_index] = 0;
                    					}
                    					// create it now, into the special div object for this chart
                    					wself.charts[pin_index] = new Chart(canvas, {
                    

                    my ‘schedule update’ asks for more data from my node_helper…
                    it informs me when its back. and I call updateDom() after saving the data where getDom() will look

                    u can see my code at https://github.com/sdetweil/WaterLevels

                    Sam

                    How to add modules

                    learning how to use browser developers window for css changes

                    1 Reply Last reply Reply Quote 0
                    • 1
                    • 2
                    • 2 / 2
                    • First post
                      Last post
                    Enjoying MagicMirror? Please consider a donation!
                    MagicMirror created by Michael Teeuw.
                    Forum managed by Sam, technical setup by Karsten.
                    This forum is using NodeBB as its core | Contributors
                    Contact | Privacy Policy