Icon View Thread

The following is the text of the current message along with any replies.
Messages 1 to 9 of 9 total
Thread Need Help with External JavaScript Component Declaration
Wed, Jun 17 2015 11:58 PMPermanent Link

Doug B

I'm trying to integrate the DropZone component (http://www.dropzonejs.com/#create-dropzones-programmatically) into EWB, but I'm having trouble figuring out how to create the external interface correctly.

For example, to match the constructor of the component, do I create the constructor like this?

JavaScript:

function Dropzone(element, options) {
...
}

EWB:

  external Dropzone = class
  public
    constructor Create(const element, options: string);
  end;

Also, after I get it instantiated, what element id can I specify in the constructor parameter to match the corresponding EWB element? Do I use the component name property?

JS example:

// Dropzone class:
var myDropzone = new Dropzone("div#myId", { url: "/file/post"});

Finally, how would I implement events to match the component events, so I could create an event handler in my EWB form that matches e.g. the "addedfile" event?

JS Example:

var myDropzone = new Dropzone("#my-dropzone");

myDropzone.on("addedfile", function(file) {
 /* Maybe display some more file information on your page */
});

I'm sure if I can get this working, I'll be able to integrate more components like this into EWB, which would be fantastic.

Any help with this would be greatly appreciated!

Thanks,
Doug
Thu, Jun 18 2015 1:00 PMPermanent Link

Doug B

If I have to write this in 100% JavaScript, I will need to know how to get the element type/class selector and id so I can use it to associate the component with my THTMLPage component.

For example:

var myDropzone = new Dropzone("div#myId" <---------- HERE , { url: "/file/post"});

How do I do that?

Thanks,
Doug
Fri, Jun 19 2015 4:56 AMPermanent Link

Matthew Jones

Doug B wrote:

> var myDropzone = new Dropzone("div#myId" <---------- HERE , { url:
> "/file/post"});
>
> How do I do that?

I can't help in the detail, but you have two resources that should
answer the question: the framework source, and the browser "inspect
element" tool. I managed to work out how to add new form fields using
these, and I figure you'd be able to work out what was needed. Tim may
have insight, but I suspect he is in the bunker peddling hard to get
the product shipped right now. 8-)
Fri, Jun 19 2015 9:07 AMPermanent Link

Raul

Team Elevate Team Elevate

On 6/17/2015 11:58 PM, Doug B wrote:
> I'm trying to integrate the DropZone component (http://www.dropzonejs.com/#create-dropzones-programmatically) into EWB, but I'm having trouble figuring out how to create the external interface correctly.
> For example, to match the constructor of the component, do I create the constructor like this?
> Also, after I get it instantiated, what element id can I specify in the constructor parameter to match the corresponding EWB element? Do I use the component name property?

EWB does not attach any elements ID to any of the components - i'm
guessing external javascript library is trying to use getElementById and
then attach an eventlistener.

This might mess things up in EWB but if you want to try then i can
recommend a quick (and very horrible hack) just to see if this would
work in principle (i'm sure there is a better way but until we get full
docs this is pain to trace down).

The DOM element that file picker uses is defined in WebUI.wbs so copy
this file (from C:\Program Files (x86)\Elevate Web Builder 2\library) to
your project folder.

Go find a function "function TFileInputElement.CreateDOMElement:
THTMLElement;" and add to the end of it

Result.setAttribute(HTML_ATTR_ID,'whatevercustomidyouwant');

Note that this affects all instances of TFileComboBox so need better
long term solution.

Anyways, you can now call your dropzone and use id
"whatevercustomidyouwant".


>Finally, how would I implement events to match the component events, so I could create an event handler in my EWB form that matches e.g. the "addedfile" event?
> JS Example:
> var myDropzone = new Dropzone("#my-dropzone");
> myDropzone.on("addedfile", function(file) {
>    /* Maybe display some more file information on your page */
> });
>
> I'm sure if I can get this working, I'll be able to integrate more components like this into EWB, which would be fantastic.
>
> Any help with this would be greatly appreciated!

In theory it looks like you just need a callback function so i would try
just sending that in - basically figure out what the file parameter in
function(file) is and then do an EWB function with same signature and
use that to register the callback.


As i look at this since your goal here is to simply upload the file(s)
it might be easier in short term to just use external code for the whole
thing - create an external web page (html+js) that does the whole thing
and just include a form in EWB app that basically hosts a TBrowser. You
would need to do the file upload from external JS as well but it's
relatively easy - TServerRequest is a thin wrapper around XMLHttpRequest.

I think the file upload area is one Tim can then revise once the EWB2 is
out in and expose more of the functionality natively.

Raul
Fri, Jun 19 2015 9:48 AMPermanent Link

Raul

Team Elevate Team Elevate

On 6/19/2015 9:07 AM, Raul wrote:
> This might mess things up in EWB but if you want to try then i can
> recommend a quick (and very horrible hack) just to see if this would
> work in principle (i'm sure there is a better way but until we get full
> docs this is pain to trace down).
>

And here is a a really basic example with a callback function to read a
file as text and send it back to ewb (in this case it shows it in
multilineedit). Requiers the hack to webui at this time (my element id
is called "mycustomfilepicker") :

////////////// ewb code - the whole thing

unit main;

interface

uses WebCore, WebUI, WebForms, WebCtrls, WebEdits;

type

   TForm1 = class(TForm)
      FileComboBox1: TFileComboBox;
      MultiLineEdit1: TMultiLineEdit;
      procedure Form1Create(Sender: TObject);
   private
      { Private declarations }
      procedure FileReadCallback(fileData:String);
   public
      { Public declarations }
   end;

   //callback function and external file reader listener
   TFileCallBack = procedure (FileData:string) of object;
   external procedure AttachFileHandler(s:string);
   external procedure AttachCallback( f:TFileCallBack);

var
   Form1: TForm1;

implementation

procedure TForm1.Form1Create(Sender: TObject);
begin
   AttachFileHandler('mycustomfilepicker');
   AttachCallback(FileReadCallback);
end;

procedure TForm1.FileReadCallback(fileData:String);
begin
   MultiLineEdit1.Lines.Add('==== FILE START ====');
   MultiLineEdit1.Lines.Add(fileData);
   MultiLineEdit1.Lines.Add('==== FILE END ====');
end;

end.

////////////// ewb code - end

and then you also need the external JS file that you add to project as
external file. Mine looks like this :


////////////// js code

var customCallback = null;

function doFileRead(evt)
{         
      var files = evt.target.files;

    for (var i = 0, f; f = files[i]; i++) {
      var reader = new FileReader();
        reader.onload = function(e) {           
           customCallback( reader.result);                                 
         }
      reader.readAsText(f); //read as text in this case
    }
}

function AttachFileHandler(elemid )
{
   document.getElementById(elemid).addEventListener('change', doFileRead,
false);
}

function AttachCallback( f )
{
   customCallback = f;
}

////////////// js code end
Sat, Jun 20 2015 7:05 AMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Raul,

<< EWB does not attach any elements ID to any of the components >>

Correct.  The issue is that it is often the case that dynamically-created
components in EWB *don't* have a name.  Since it isn't possible to establish
a unique ID in all cases and EWB doesn't need them/they just take up space,
EWB just leaves them out.

You can still set them, if you need to, but it requires creating a new (or
descendant) component to get access to the DOM element via its TElement
wrapper.

Tim Young
Elevate Software
www.elevatesoft.com
Sat, Jun 20 2015 7:07 AMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Raul,

<< In theory it looks like you just need a callback function so i would try
just sending that in - basically figure out what the file parameter in
function(file) is and then do an EWB function with same signature and use
that to register the callback. >>

Almost forgot:  EWB's method creation results in a simple function
reference, so EWB methods can always be used wherever a function callback is
required.  The only difference is the current instance scope used with the
function, which is enforced by EWB to be the instance of the method being
passed as the callback.

Tim Young
Elevate Software
www.elevatesoft.com


Sat, Jun 20 2015 7:22 AMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Doug,

<< I'm sure if I can get this working, I'll be able to integrate more
components like this into EWB, which would be fantastic. >>

Please keep in mind that using external JS components can sometimes be a
problem because:

1) They can cause issues with EWB at runtime if duplicate global
variable/object/property identifiers are used.

2) EWB has no knowledge of any DOM elements created by such external JS
code, so they can interfere with the operation of EWB at runtime if such
elements are inserted into the DOM tree *above* any EWB elements.

So, the first choice should always be to use the native APIs directly in
EWB, preferably wrapped in a component/control.

Tim Young
Elevate Software
www.elevatesoft.com


Sun, Jun 21 2015 3:37 PMPermanent Link

Raul

Team Elevate Team Elevate

On 6/20/2015 7:05 AM, Tim Young [Elevate Software] wrote:
> You can still set them, if you need to, but it requires creating a new
> (or descendant) component to get access to the DOM element via its
> TElement wrapper.

Thanks.

I took another look and there is another small challenge in that the
file input (TFileInputElement for which i'd like to set the attribute)
is actually a protected member of the TFileComboBox and
TFileInputElement itself has TElement as a protected member so i'd need
to create 2 descendant classes to do it "properly".

Anyways, i cheated and added another public property to the base
TElement class (PubDOMElement: THTMLElement read FDOMElement) and now i
can just derive a custom TFileComboBox with this :

   TCustomFileComboBox = class(TFileComboBox)
   public
      public procedure setCustomID(newID:string);
   end;

and implementation of :

procedure TCustomFileComboBox.setCustomID(newID:string);
begin
TFileInputElement(GetInputElement).PubDOMElement.setAttribute(HTML_ATTR_ID,newID);
end;


and the whole setup phase is now :
   myFileCombo := TCustomFileComboBox.Create(self);
   //position it as you wish - top/left and layout

   myFileCombo.setCustomID('CustomFilePickID');
   AttachFileHandler('CustomFilePickID');
   AttachCallback(FileReadCallback);

so a lot cleaner and universal solution.

I do think it would be nice to get TElement FDOMElement published as a
public property.

I guess i could have overwritten the TFileComboBox FInputElement with a
custom implementation of TFileInputElement but on surface this looks
like a lot more work (to ensure TFileComboBox still properly registers
all the FInputElement events and sets its config).

Raul
Image