Delay External JS until Object rendered

I’ve searched around a bit and wasn’t sure if there was an easy way to do this…

Basically I am looking to delay running this script until a Server Connect Function is complete (image is loaded and fully rendered).

<script src="../../assets/js/cropper.js" id="cropperScript"></script>

It seems so simple but I am not sure how to properly use the async / defer function as they did not seem to do anything but simply checking the box.

Any thoughts?

Try putting the script into a conditional region, and only open that conditional region once your server connect has run. For example, on success of server connect, throw a toggle to “checked” and make the conditional region only if toggle.checked.

1 Like

Are you sure you don’t just need to initialize the script when the data is loaded? Why load the cropper.js include later - load it normally, and then initialize it when your data is ready?

1 Like

So doing that did not actually fix my issue (you can see the code below inspired by @mebeingken and askjarvis.io (Great app on AppSumo that will use AI to Generate code for you, great for “concepts” but havn’t used it too much!)

STILL PROBLEM WITH RENDERING PROPERLY:

  • Cropper.JS “Functionality” works properly when I hardcode the Image but not when I dynamically change the src (after a Server Connect Function).

My thoughts were originally by delaying the script until after the src is changed this would fix the issue but no the case…See video below and Full Code.

Video got cut off short but shows how the code should work / whats happening / not sure if anyone has any ideas!

CODE BELOW:
For anyone wondering how to do a delay before running External JS files see the code below

<script>
$(document).ready(function() {
setTimeout(function() {
    var head = document.getElementsByTagName('HEAD')[0];
    var script = document.createElement('script');
    var script2 = document.createElement('script');
    var css = document.createElement('link');
    script.src = '../../assets/js/cropper-script.js';
    script2.src = '../../assets/js/cropper.js';
    css.rel = 'stylesheet';
    css.type = 'text/css';
    css.href = '../../assets/css/cropper.css';
    script.id = 'cropperScript';
    script2.id = 'cropperScript';
    document.body.appendChild(script);
    document.body.appendChild(script2);
    head.appendChild(css);
}, 3000);
});
</script>

Zip File attached with relevant items.

wappler issue.zip (15.2 KB)

Once again, why would you do that? You need to initialize the cropper script once the data is loaded on your page.

You are currently trying to initialize it on document ready, which is quite too early, as the data is not loaded yet:

$(document).ready(function() {
    const image = document.getElementById('image');
    cropper = new Cropper(image, {

        strict: true,
        modal: false,
        background: false,
        minCanvasWidth: 569,
        minCanvasHeight: 569,
        minCropBoxWidth: 50,
        minCropBoxHeight: 50,
        scalable: false,
        center: true,
        guides: true,
        zoomable: false,
        zoomOnTouch: false,
        zoomOnWheel: false,
        crop(event) {
            document.getElementById('left').value = event.detail.x;
            document.getElementById('top').value = event.detail.y;
            document.getElementById('width').value = event.detail.width;
            document.getElementById('height').value = event.detail.height;
        },
    });



    image.addEventListener('ready', function() {
        if (this.cropper === cropper) {
            cropper.setCropBoxData({
                left: 200,
                top: 200,
                width: 300,
                height: 300
            })

        }
        // > true
    });
    
})

Change this to:

function cropper_init() {
    const image = document.getElementById('image');
    cropper = new Cropper(image, {
        strict: true,
        modal: false,
        background: false,
        minCanvasWidth: 569,
        minCanvasHeight: 569,
        minCropBoxWidth: 50,
        minCropBoxHeight: 50,
        scalable: false,
        center: true,
        guides: true,
        zoomable: false,
        zoomOnTouch: false,
        zoomOnWheel: false,
        crop(event) {
            document.getElementById('left').value = event.detail.x;
            document.getElementById('top').value = event.detail.y;
            document.getElementById('width').value = event.detail.width;
            document.getElementById('height').value = event.detail.height;
        }
    });
     image.addEventListener('ready', function() {
        if (this.cropper === cropper) {
            cropper.setCropBoxData({
                left: 200,
                top: 200,
                width: 300,
                height: 300
            })

        }
        // > true
    });    
}

and call the cropper_init() function on sc_list_prod_details server action > static events > done?

Ha....didn't even notice this was for croppie. As Teodor shows, just load the script normally and setup functions to perform the various Croppie tasks like loading, saving, etc.

I have cropper.js integrated in one of my apps and I can confirm @Teodor approach is the one I follow.
The only difference is that I have the cropper called on a modal shown.

<div class="modal fade" id="modal1" is="dmx-bs4-modal" tabindex="-1" role="dialog" dmx-on:shown-bs-modal="'cropInit()'" dmx-on:hidden-bs-modal="'cropDestroy()'" dmx-bind:show="s3upload1.state.ready">

If you are happy reading code below you have my script. This was from before I learnt how to create frontend custom components which is how I would refactor all this in a better way if I ever need to use it again.

<script>
  var image = document.getElementById('profile_photo');
  var cropBoxData;
  var canvasData;
  var cropper;
  var c;
  function cropInit() {
    dmx.app.set('cropped', false);
    original = dmx.app.data.view2.s3upload1.file.dataUrl;
    cropper = new Cropper(image, {
      dragMode: 'move',
      restore: false,
      guides: false,
      center: false,
      highlight: false,
      cropBoxMovable: false,
      cropBoxResizable: false,
      toggleDragModeOnDblclick: false,
      aspectRatio: 1 / 1,
      autoCropArea: 0.5,
      ready: function () {
        //Should set crop box data first here
        cropper.setCropBoxData(cropBoxData).setCanvasData(canvasData);
        cropper.reset();
      }
    });
  }
  function cropDestroy() {
    cropBoxData = cropper.getCropBoxData();
    canvasData = cropper.getCanvasData();
    cropper.destroy();    
    dmx.parse('s3upload1.reset()')
  }
  function crop() {
     dmx.app.data.view2.s3upload1.file.dataUrl = cropper.getCroppedCanvas({"width" : "200", "height" : "200", "imageSmoothingQuality" : "high", "imageSmoothingEnabled" : true}).toDataURL("image/jpeg");
     dmx.app.set('cropped', true);
     cropper.getCroppedCanvas({"width" : "200", "height" : "200", "imageSmoothingQuality" : "high", "imageSmoothingEnabled" : true}).toBlob(function(blob) {
       document.getElementById('s3upload1').dmxComponent.file = new File([blob], "profile.jpeg", { lastModified: new Date().getTime(), type: blob.type });
     }, 'image/jpeg', 0.8);
  }
  function cancel() {    
    dmx.app.data.view2.s3upload1.file.dataUrl = original;
    dmx.app.set('cropped', false);  
  }
</script>
1 Like

Great got it!

Server Connect Function (DONE) was still to early so just used this...

<script>
$(document).ready(function() {
setTimeout(function() {
    cropper_init();
}, 1000);
});
</script>

You don’t want to wait for the document ready, you want to check if the server action has loaded first. What if your server action doesn’t load in this 1 second after the document ready? Better remove the document ready part and call it on server action load event.

1 Like

Ok good point, yeah I’ll have to change this as a failsafe.