backbone marionette incorrect rendering of element - javascript

I'm working on implementing a QR reader in a web based app that uses the PC's webcam. This is currently working correct.
The framework that I use (barcode.js) has a function render(element).
I have 2 different files, ScannerView.html and ScannerView.js. See their code below:
ScannerView.html
<!-- Content Header (Page header) -->
<section class="content-header has-background-image">
<div class="background">
<img src="/assets/temp/header.png" />
</div>
<h1><div class="icon"><i class="fa fa-wrench"></i></div> <%=name%></h1>
</section>
<section class="content">
// Withing the following div, the canvas element should be placed
<div id="scanner"></div>
</section>
ScannerView.js
var YookrApp = require("../../setup");
var scanner = null;
var ScannerView = Backbone.Marionette.CompositeView.extend({
template: require("./ScannerView.html"),
className: "scannerView",
ui: {
scannerDiv: "#scanner"
},
name: "Yookr Code Scanner",
initialize: function(options) {
},
onRender: function() {
w69b.qr.decoding.setWorkerUrl("assets/w69b.qrcode.decodeworker.js");
scanner = new w69b.qr.ui.ContinuousScanner();
// Called when a qr code has been decoded.
scanner.setDecodedCallback(function(result) {
console.log("Decoded qr code:", result);
});
scanner.setStopped(false);
// Render component in element with id "scanner".
console.log("Start rendering");
scanner.render(this.ui.scannerDiv);
console.log("Rendering done");
},
close: function() {
// We have to dispose the scanner object.
// If we don"t do this, the webcam will
// always be enabled
scanner.dispose();
}
});
module.exports = ScannerView;
When I run my app, the scanner.render() function should add a <canvas> element inside <div id="scanner"></div> in ScannerView.html
Problem
I'm not able to render the canvas using scanner.render() properly withing the <div id="scanner"></div> that is located in my html file. I tried to use document.getElementById and this.ui.scannerDiv with different, but not correct result.
Because I defined an UI element in the ScannerView.js file, I should be able to call scanner.render(this.ui.scannerDiv); This should use the div with id scanner in my html view, but instead the canvas is not rendered at all. In my console, I can see the following warnings:
[.Offscreen-For-WebGL-0000020AB57365B0]GL ERROR :GL_INVALID_FRAMEBUFFER_OPERATION : glDrawArrays: framebuffer incomplete
and
[.Offscreen-For-WebGL-0000020AB57365B0]RENDER WARNING: texture bound to texture unit 2 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering.
When I use scanner.render(document.getElementById("scanner"));, I can see that the canvas is rendered, but not in the correct location. Please see my added screenshot for the result:
At this point I'm stuck. I dont know how I can achieve that the canvas is rendered in the correct div.

After searching Google for a while longer, I found a solution.
The onRender: function() { } "gets triggered when the View's DOM subtree is prepared for insertion into the DOM but before those elements are viewable", according to this webiste.
after changing onRender to onShow in my javascript file, the canvas element is rendered in the correct location.

Related

How Can I Pass String Variable to Anonymous Adobe View SDK Script?

I have a very basic knowledge of javascript and I have been unable to find a solution for my specific use of the Adobe View SDK API, though it seems like there should be a way. I am working on a web page to show newsletters in the pdf viewer. I have created a w3.css modal element so that I can open the viewer with a button click, and then close it with an "x" in the corner. The button click and the "x" toggle between the display style being "none" or "block". I really like this solution as it lets me use small images of the newsletters as the buttons, and it can be observed here: Test News Page by clicking on the newsletter image below May 4, 2020.
The ultimate goal I have is to be able to change the name of the pdf document that is opened in the viewer by clicking the button, which would need to pass a string variable called "docName" to the url called by the View SDK script. Since the url is already specified in the script inside my modal element when the page loads, here is the thinking I have for the additional script I need to pass my string variables: The button-click invokes my script (function changeName(docName)) and passes the "docName" variable. Then my script needs to pass this variable to the url in the View script (this is the part I don't know how to do), then refresh the page to reload my modal, and then change the display style of the modal to "block". I will copy the code for the View SDK below, showing where I need to insert the string variable with my document name:
<script src="https://documentcloud.adobe.com/view-sdk/main.js"></script>
<script type="text/javascript">
document.addEventListener("adobe_dc_view_sdk.ready", function(){
var adobeDCView = new AdobeDC.View({clientId: "06179511ab964c9284f1b0887eca1b46", divId: "adobe-dc-view"});
adobeDCView.previewFile({
content:{location: {url: "https://www.shcsfarmington.org/" + docName + ".pdf"}},
metaData:{fileName: "Newsletter_050420.pdf"}
}, {embedMode: "FULL_WINDOW", defaultViewMode: "FIT_WIDTH"});
});
</script>
It seems like this should work, but with my limited knowledge of javascript I don't know how to pass this variable to the anonymous function in the View SDK code, and I would need as much detail and specifics in the syntax of the solution. I appreciate any help with this. Thanks.
EDIT: I thought maybe it would help to show the code for the function that I have come up with so far - then it could be examined and easier to debug and comment on:
<button id="CSS-050420" onclick="changeDoc('Newsletter_050420');"></button>
<script>
function changeDoc(docName) {
/* Need to pass docName to url=https://shcsfarmington.org/2020/news/Newsletter_" + newsDate + ".pdf"; */
window.location.reload(true);
document.getElementById('viewerModal').style.display='block';
}
</script>
I created a CodePen here for you to look at.
Basically, you'll load the first file when the SDK is ready but then you need to set the adobeDCView to null before recreating it.
function showPDF(url) {
adobeDCView = null;
fetch(url)
.then((res) => res.blob())
.then((blob) => {
adobeDCView = new AdobeDC.View({
// This clientId can be used for any CodePen example
clientId: "e800d12fc12c4d60960778b2bc4370af",
// The id of the container for the PDF Viewer
divId: "adobe-dc-view"
});
adobeDCView.previewFile(
{
content: { promise: Promise.resolve(blob.arrayBuffer()) },
metaData: { fileName: url.split("/").slice(-1)[0] }
},
{
embedMode: "FULL_WINDOW",
defaultViewMode: "FIT_PAGE",
showDownloadPDF: true,
showPrintPDF: true,
showLeftHandPanel: false,
showAnnotationTools: false
}
);
});
}
The link click even will pass the url to the PDF and then display it.

Launch Avairy without a dialog (inside div)

I need to use Avairy (from Adobe Creative SDK, Web version) inside my own custom dialog.
Is there any way to get Avairy's div instead of the dialog? I will place it inside my own dialog.
The main problem is the method avairyEditor.launch() opens a dialog and I do not see simple way to do something else.
Is there the only way to do it writing my own hacks (invisible showing the dialog and cutting div from it) or I can do it shedding hardly any blood?
Yes, you need to use the appendTo option in the configuration.
Your HTML:
<!-- Aviary div container. You can use absolute/relative positioning -->
<div id='aviary' style='width:600px;height:400px;'></div>
<!-- Add an edit button, passing the HTML id of the image
and the public URL to the image -->
<a href="#" onclick="return launchEditor('editableimage1',
'http://example.com/public/images/goat.jpg');">Edit!</a>
<!-- original line of HTML here: -->
<img id="editableimage1" src="http://example.com/public/images/goat.jpg"/>
Your JavaScript:
var featherEditor = new Aviary.Feather({
apiKey: '1234567',
appendTo: 'aviary'
onSave: function(imageID, newURL) {
var img = document.getElementById(imageID);
img.src = newURL;
}
});
function launchEditor(id, src) {
featherEditor.launch({
image: id,
url: src
});
return false;
}
Documentation:
https://creativesdk.adobe.com/docs/web/#/articles/gettingstarted/index.html#constructor-config-appendTo
To get closer to what you are trying to do, consider using the appendTo property, as mentioned by Juangui, in addition to the following properties:
theme: 'minimum',
noCloseButton: 'true'
After that you can add some custom styling to better match the rest of your app.

Angular and CreateJS not finding the stage element

I have an Angular view that is inherited from an ng-view element.
In the view file, I have a canvas. The code is controlled by a controller. What I want to do is call up a stage to add elements on the canvas.
However when I run the createJS.Stage function, a stage is created with everything as null and no bounds.
I'm guessing the element is not getting bound to the stage but I can't figure out why
index.html
<ng-view style='height: 100%;'></ng-view>
template.html
<div class='middle' style="min-height:90%;height:90%">
<div class='col-lg-8'>
<canvas id="demoCanvas" class='col-lg-11' style='margin-top:10px'></canvas>
</div>
</div>
JS
app.controller('taskController',function($scope,$location,$routeParams,dataFactory){
var stage = new createjs.Stage("demoCanvas");
console.log(stage.getBounds());
//bounds turn up as null as well as style and any other function that I run
}
So turns out the problem is that the DOM is not completely loaded when an AngularJS controller is run
The trick to running it once the DOM has finished loading is
app.controller('taskController',function($scope,$location,$routeParams,dataFactory){
$timeout(function(){
visualInspectionHandler();
});
function visualInspectionHandler(){
var stage = new createjs.Stage("demoCanvas");
console.log(stage.getBounds());
}
}

Accessing element in Ember View

I have a view setup that has a canvas and several inputs in it.
I am calling an action in the view when a button is pressed and I need to access the canvas and input's.
Obviously I could use something like what I have below but if I have more than 1 of these view's on a page then getting an element via ID just isn't going to cut it.
var canvas = document.getElementById("canvasThumbResult");
I have the canvas setup like this in the view's template
<canvas id="canvasThumbResult" width="128" height="128"></canvas>
Is there a good way to get an element from an action in Ember or is there a way I can search for the element only inside the current view's output?
you can access any HTML Element within your Ember.View with this.$(/*jquery identifier*/) so, for an example you can access your canvas like
var App.MyCanvasView = Ember.View.extend({
actions: {
myAction: function () {
console.log(this.$('#canvasThumbResult'));
}
}
});

Tabs in backbone, underscore, jquery page

I'm making a toy app, trying to learn about these libraries and was hoping to understand how I would present a "tab" interface. I'd like to have a few buttons (or links, whatever is most common) on top. When one selected, it looks selected and the main content on the page changes to present what's on that tab.
My learning app is very simple at the moment:
<body>
<div class="content"></div>
</body>
And a backbone view:
var ContentView = Backbone.View.extend({
el: $('.content'),
// ..
So far, I've looked into using <% tag in the html to make a partial, making the ContentView somehow responsible for rendering different stuff in there. The other idea I've had but don't quite know how to pursue is several Backbone views taking turns being in charge of changing the one div.
Would sure appreciate some advice about the canonical approach here, including how to present the tabs buttons and how to cleanly separate view logic for the different tabs.
Follow a 'separation of concerns' model. You've got a main content view, which handles tab navigation. Anytime someone clicks on a tab, that main content view should tell the view that the tab belongs to that it is now active. Then that sub-view handles things from there. Here's some example code:
Let's say this is the HTML:
<div id="tabContainer" class='tabs'>
<div class='tab' id="content1Tab"></div>
<div class='tab' id="content2Tab"></div>
<div class='tab' id="content3Tab"></div>
</div>
<div id="contentContainer">
</div>
This might be the javascript.
ContentView = new (Backbone.View.extend({}
events: {
'click .tab': 'openTab'
},
el: $("#tabContainer"),
tabViews: {},
openTab: function (e) {
var el = $(e.currentTarget);
$("#contentContainer").children().detach();
if(!this.tabViews[el.attr('id')]) {
this.tabViews[el.attr('id')] = this.createTabViewForEl(el);
}
this.tabViews[el.attr('id')].render($("#contentContainer"));
},
createTabViewForEl: function (el) {
var tab;
switch(el.attr('id')) {
case "content1Tab":
tab = new FirstContentTab();
break;
/* etc */
}
return tab;
}
))();
FirstContentTab = Backbone.View.extend({
render: function (targetEl) {
this.setElement($("#someContentEl"));
this.$el.appendTo(targetEl);
}
/** stuff like loading in content for tab, or making a monkey dance when it opens **/
});
There are more elegant ways of doing this, like referencing the module that the tab belongs to, then using requirejs or some other module loader to load in that module and give it the tab in question. But, either way, don't let that one main view do too much. Otherwise you'll end up with something that's way more complicated than it needs to be.

Categories