Fwd: What is the best way to approach this dojo page?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Fwd: What is the best way to approach this dojo page?

karozans
Hi everyone.  I am trying to develop a medium sized web app with dojo.

I have started out with a basic BorderContainer that fits the entire page and now I need to fill up each section with widgets.  I would like to keep my code very easily manageable and I don't like to have files that are massively long if I can keep from it.

I have the basic BorderContainer and I would like to create fully modularized widgets so that I can easily insert each widget by one line of code, like...

var topTabs = new TopTabs();

All of the widgets that I will be inserting into each of the sections will have instances of other Dijits in them.  Like TabContainers, buttons, and checkboxes.

So my question is....

What is the "best" way to approach this?  Should I create template based widgets for the widgets that want to put in each section?

Or is it better to create widgets that programmatically insert dijits?

Can someone please provide a simple, basic, skeleton of what my widgets and file structure should be for good code maintainability and such?

Thanks

--
Dojo Toolkit: http://dojotoolkit.org/
Tutorials: http://dojotoolkit.org/documentation/

[hidden email]
To unsubscribe, visit: http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: What is the best way to approach this dojo page?

Jean-Claude Hujeux
See stackoverflow for the answer to your first question.

As per a "simple, basic skeleton, of the overall layout", I can share the overall organization of my application, the result of several years of refactoring, and hoping it's good practice enough ...

  • the main html file (corresponds to the index.html file of the boiler plate - in my case it does not because I've got an authentication layer and server side stuff going on before the main html file can be sent to the server):
<!DOCTYPE HTML>
<html
    <head>
        <meta charset="utf-8">
        <title>...</title>
        <link rel="stylesheet" href="...myApp.css" media="screen">
    </head>
    <body class="claro">
        <div id="appLayout" class="demoLayout" ></div>
         <script>var dojoConfig ={...};
        </script>
        <script src=".../dojo/dojo.js"></script>

        <script>
var pageDescription = {...}; // the initial description of the page.
require([ "{appDir}/PageManager"],
                function(PageManager){
                    PageManager.initialize(pageDescription);
                    document.body.className += ' loaded';
            });
            </script>
    </body>
</html>

  • the js side: everything is AMD modules, in the src/myApp directory (and sub-directories):

    • PageManager.js: this is where all what takes place at page level is orchestrated:
define([...], function(...){
    var ...;
    return {
        initialize: function(args){ // args is an object that contains the initial description of the page to be displayed and is used throughout the application. PageManager.initialize() is called from the html page above
                ...
                // building the overall page layout (see the layout tutorial here)
                var appLayout = new BorderContainer({...}, "appLayout"), leftPane = new AccordionContainer({...}), headerPane = new ContentPane({content: args.headerContent, ...}, tabsContainer = new TabsContainer({...});
                appLayout.addChild(leftPane);
                appLayout.addChild(headerPane);
                appLayout.addChild(CenterPane);

                // adding accordions & tabs
                this.accordion = new AccordionManager({container: leftPane);
                args.accordionDescription.forEach(function(description){
                     this.accordion.create(description);
                });
                this.tabs = new TabsManager({container: tabsContainer);
                args.tabsDescription.forEach(function(description){
                     this.tabs.create(description);
                });
                ...
                ready(function(){
                    appLayout.startup();
                });
        },
        ... here other functions made available to all application modules that need it, in particular:
        serverDialog: function(...){ // calls dojo/request and is used by the application modules every time the page needs to interact with the server, (allows to standardize server interaction and centralize feedback / error handling for the page)
         },
         etc.
});
    • AccordionManager.js, TabsManager.js: //an intermediate layer that provides methods to manipulate the page tabs and accordion panes in the various modules (create a new tab, go to a specific tab, etc.)
define([...], function(...){
    return declare([...], {
        constructor: function(args){
            this.container =   args.container;       
               ...
        },
        create: function(tabDescription){
            ...
            var myNewTab = new MyAppTab(tabDescription)        
            this.container.addChild();
            return myNewTab;
        },
        gotoTab: function(...){
        },
        etc.       
    });
});
    • MyAppTab.js: this is where the tab is laid out based on its description: layout and associated widgets. Is typically the extension of a layout widget (e.g a ContentPane,  a BorderContainer, ...)

      define([...], function(...){
          return declare([ContentPane], {
               postCreate(){
                // here multiple (recursive) instantiations dojox/layout/TableContainer based on the tab description
              },
              refresh: function(...){
              },
              etc.       
          });
      });
    • MyCustomWidget1.js //
    • MyCustomWidget2.js, etc.

For tab and accordion panes (and TooltipDialog, etc.) layout, I use dojox/layout/TableContainer which displays the contained child widgets as a table (and can be nested). Not perfect,  does the job though.

When a file becomes too large, or to share code between modules I make extensive use of mixins and utilities modules. This way, the majority of my files fit on the editor screen (or close to).

Finally, to describe my page, I need to fill in the pageDescription object in the main html file, e.g.:

    var pageDescription = {
        headerContent: {...},        
        accordionDescription: [{title: "first pane title", layout: {<the first pane layout description>}, widgets: {widget1: {label: 'name', type: 'TextBox', value: 'this is my name'}, { ...}, { ...}}},{title: "second pane title, ...}],
        tabsDescription: [{title: "first tab title", layout: {<the first tab layout description >}, widgets: {widget1: {label: 'xyz', type: 'MyCustomWidget1', value: 'this is xyz'},  widget2:  { ...},...}},{title: "second tab title, ...}],
        etc.
    };

I did it this way initially as I had made the choice to put the business logics (which drives the content of the page) on the server side, using php / mySql: in my case, pageDescription is built by this php layer.  Then the js/dojo side focuses on the presentation and interaction logics. There are obviously other possible design choices.

Of course this is only the tip of the iceberg, still I hope you'll find it useful,

jc

Le 22/08/2017 à 00:43, Karo Zans a écrit :
Hi everyone.  I am trying to develop a medium sized web app with dojo.

I have started out with a basic BorderContainer that fits the entire page and now I need to fill up each section with widgets.  I would like to keep my code very easily manageable and I don't like to have files that are massively long if I can keep from it.

I have the basic BorderContainer and I would like to create fully modularized widgets so that I can easily insert each widget by one line of code, like...

var topTabs = new TopTabs();

All of the widgets that I will be inserting into each of the sections will have instances of other Dijits in them.  Like TabContainers, buttons, and checkboxes.

So my question is....

What is the "best" way to approach this?  Should I create template based widgets for the widgets that want to put in each section?

Or is it better to create widgets that programmatically insert dijits?

Can someone please provide a simple, basic, skeleton of what my widgets and file structure should be for good code maintainability and such?

Thanks




--
Dojo Toolkit: http://dojotoolkit.org/
Tutorials: http://dojotoolkit.org/documentation/

[hidden email]
To unsubscribe, visit: http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: What is the best way to approach this dojo page?

denov
i would suggest sub classing _WidgetBase and not using your own init function - https://dojotoolkit.org/reference-guide/1.10/dijit/_WidgetBase.html  - at the very least  - own() is pretty useful.  if not i would at least have my init function returns a promise ensure correct timing/ordering of things. 

you also need to be careful of scoping when doing things like

                args.tabsDescription.forEach(function(description){
                     this.tabs.create(description);
                });


2 issues here.  the use of an anon function - this will make you're stack hard to read while debugging..  use name functions.   this.tabs is referring to the this of the anon function and the other scope you're actually trying to ref.




On 8/23/2017 2:52 PM, Hujeux Jean-Claude wrote:
See stackoverflow for the answer to your first question.

As per a "simple, basic skeleton, of the overall layout", I can share the overall organization of my application, the result of several years of refactoring, and hoping it's good practice enough ...

  • the main html file (corresponds to the index.html file of the boiler plate - in my case it does not because I've got an authentication layer and server side stuff going on before the main html file can be sent to the server):
<!DOCTYPE HTML>
<html
    <head>
        <meta charset="utf-8">
        <title>...</title>
        <link rel="stylesheet" href="...myApp.css" media="screen">
    </head>
    <body class="claro">
        <div id="appLayout" class="demoLayout" ></div>
         <script>var dojoConfig ={...};
        </script>
        <script src=".../dojo/dojo.js"></script>

        <script>
var pageDescription = {...}; // the initial description of the page.
require([ "{appDir}/PageManager"],
                function(PageManager){
                    PageManager.initialize(pageDescription);
                    document.body.className += ' loaded';
            });
            </script>
    </body>
</html>

  • the js side: everything is AMD modules, in the src/myApp directory (and sub-directories):

    • PageManager.js: this is where all what takes place at page level is orchestrated:
define([...], function(...){
    var ...;
    return {
        initialize: function(args){ // args is an object that contains the initial description of the page to be displayed and is used throughout the application. PageManager.initialize() is called from the html page above
                ...
                // building the overall page layout (see the layout tutorial here)
                var appLayout = new BorderContainer({...}, "appLayout"), leftPane = new AccordionContainer({...}), headerPane = new ContentPane({content: args.headerContent, ...}, tabsContainer = new TabsContainer({...});
                appLayout.addChild(leftPane);
                appLayout.addChild(headerPane);
                appLayout.addChild(CenterPane);

                // adding accordions & tabs
                this.accordion = new AccordionManager({container: leftPane);
                args.accordionDescription.forEach(function(description){
                     this.accordion.create(description);
                });
                this.tabs = new TabsManager({container: tabsContainer);
                args.tabsDescription.forEach(function(description){
                     this.tabs.create(description);
                });
                ...
                ready(function(){
                    appLayout.startup();
                });
        },
        ... here other functions made available to all application modules that need it, in particular:
        serverDialog: function(...){ // calls dojo/request and is used by the application modules every time the page needs to interact with the server, (allows to standardize server interaction and centralize feedback / error handling for the page)
         },
         etc.
});
    • AccordionManager.js, TabsManager.js: //an intermediate layer that provides methods to manipulate the page tabs and accordion panes in the various modules (create a new tab, go to a specific tab, etc.)
define([...], function(...){
    return declare([...], {
        constructor: function(args){
            this.container =   args.container;       
               ...
        },
        create: function(tabDescription){
            ...
            var myNewTab = new MyAppTab(tabDescription)        
            this.container.addChild();
            return myNewTab;
        },
        gotoTab: function(...){
        },
        etc.       
    });
});
    • MyAppTab.js: this is where the tab is laid out based on its description: layout and associated widgets. Is typically the extension of a layout widget (e.g a ContentPane,  a BorderContainer, ...)

      define([...], function(...){
          return declare([ContentPane], {
               postCreate(){
                // here multiple (recursive) instantiations dojox/layout/TableContainer based on the tab description
              },
              refresh: function(...){
              },
              etc.       
          });
      });
    • MyCustomWidget1.js //
    • MyCustomWidget2.js, etc.

For tab and accordion panes (and TooltipDialog, etc.) layout, I use dojox/layout/TableContainer which displays the contained child widgets as a table (and can be nested). Not perfect,  does the job though.

When a file becomes too large, or to share code between modules I make extensive use of mixins and utilities modules. This way, the majority of my files fit on the editor screen (or close to).

Finally, to describe my page, I need to fill in the pageDescription object in the main html file, e.g.:

    var pageDescription = {
        headerContent: {...},        
        accordionDescription: [{title: "first pane title", layout: {<the first pane layout description>}, widgets: {widget1: {label: 'name', type: 'TextBox', value: 'this is my name'}, { ...}, { ...}}},{title: "second pane title, ...}],
        tabsDescription: [{title: "first tab title", layout: {<the first tab layout description >}, widgets: {widget1: {label: 'xyz', type: 'MyCustomWidget1', value: 'this is xyz'},  widget2:  { ...},...}},{title: "second tab title, ...}],
        etc.
    };

I did it this way initially as I had made the choice to put the business logics (which drives the content of the page) on the server side, using php / mySql: in my case, pageDescription is built by this php layer.  Then the js/dojo side focuses on the presentation and interaction logics. There are obviously other possible design choices.

Of course this is only the tip of the iceberg, still I hope you'll find it useful,

jc

Le 22/08/2017 à 00:43, Karo Zans a écrit :
Hi everyone.  I am trying to develop a medium sized web app with dojo.

I have started out with a basic BorderContainer that fits the entire page and now I need to fill up each section with widgets.  I would like to keep my code very easily manageable and I don't like to have files that are massively long if I can keep from it.

I have the basic BorderContainer and I would like to create fully modularized widgets so that I can easily insert each widget by one line of code, like...

var topTabs = new TopTabs();

All of the widgets that I will be inserting into each of the sections will have instances of other Dijits in them.  Like TabContainers, buttons, and checkboxes.

So my question is....

What is the "best" way to approach this?  Should I create template based widgets for the widgets that want to put in each section?

Or is it better to create widgets that programmatically insert dijits?

Can someone please provide a simple, basic, skeleton of what my widgets and file structure should be for good code maintainability and such?

Thanks






-- 
www.fullstacksystems.com

--
Dojo Toolkit: http://dojotoolkit.org/
Tutorials: http://dojotoolkit.org/documentation/

[hidden email]
To unsubscribe, visit: http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: What is the best way to approach this dojo page?

Jean-Claude Hujeux

Thanks for your comments Deno :-),

  • for the init, I guess you are referring to PageManager.initialze(). Indeed, PageManager is not a widget, it is  even not a class (does not use dojo/declare), as there is only one PageManager object so instantiation is not needed. A few years ago almost all my modules were classes. Over time I identified quite a few situations where what was needed could be simpler than a class to instantiate only once, so I have now more modules returning plain js  objects (I guess the savant word for it is singleton ?). PageManager is one of them (and as I write this email some googling makes me conclude it is some sort of a façade pattern). Also 100% of the action takes place within PageManager.initialize, so there is nothing to wait for. The waiting takes place inside initialize(), that's the reason for the presence of the ready(){} part inside PageManager.initialize();
ready(function(){
                    appLayout.startup();
 });
The real PageManager.initialize() has nested ready(){} sections. And I guess I should have called is PageManager.displayPage() rather than PageManager.initialize(), which is misleading (I'll do that in the next refactoring ;-)).
  • for args.tabsDescription.forEach(), you're perfetcly right. I made a mistake on preparing the skeleton, scoping is wrong, so code woud not work (as it is, "this" refers to the window object, not to the PageManager object). Thanks for spotting this! The real PageManager code is:
var self = this;
...
args.tabsDescription.forEach(function(description){
    self.tabs.create(description);
}
You're right again, when you state that one needs" to be careful of scoping". It tooks me quite a few years before I master it enough. What came as a saver is lang.hitch (described here). The above could be re-written:
args.tabsDescription.forEach(lang.hitch(this, function(description){
    this.tabs.create(description);
}));
Sometimes it is the only possible approach.

As per anon versus named function, right again, another possible implementation is:

            return {
                initialize: function(){
                    ...;
                    args.tabsDescription.forEach(createTab);
                },
                createTab: function(description){
                    this.tabs.create(description);
                }
            }
Advantage, createTab() is visible to other modules: PageManager.CreateTab(description), so it is probably a better solution (another candidate to my next refactoring;-)). I use both approaches thorough my code, choice being made often "by instinct". What I like about anonymous for small functions is that everything is at the same place. As soon as the pattern is to be used more than once, the named function wins though. Idem when the anonymous function becomes large and clutters the calling function.

To further contribute to Karo's asking "best way to aproach this" and the "promise ensure proper ordering of things" mentioned by Keno, let me first come back to "ready()": this has been my other saver: for several years I thought "dojo/domReady!" would do most of the job (and at least did not harm). When things did not go the way I wanted and I concluded this was a timing / ordering issue, I was inserting the corresponding code as a callback to setTimeout(), and played with the number of milliseconds to wait (0 was often enough, but not always).

I had read about ready() (here), but I guess I got misled by the sentences. " ... has been mostly replaced by ..." and "... In these cases , dojo/ready() functions can still be useful..."., interpreting it as  "seldom". In fact as over time I try to make the page more responsive, I encounter more of these situations where a section of code should "wait for something to complete" which I have not the control of (and from day one dijit/editor and dgrid/onDemandGrid at least generated such situations). Well, this is just what dojo/ready() does.

=> I started using it, and now I have removed many of my dojo/domReady! references (maybe because I don't use templates ? I am still unsure when it is mandatory or not), and replaced most of those setTimeout(...) with ready(...), successfully.

As a final remark to a lenghty answer (it's all about sharing, isn't it ?), ready() is still a "last resort approach" - the only one possible sometimes. There is another construct I wish I would have mastered earlier and that Deno mentions: promises, which allow to self-document and instruct the system about the correct ordering of things. That's the proper way to take control of many of these "wait for something to complete" situations. deferredOrPromise.then(...) and dojo/when() have become pervasive in my code.

Yes, one can say about dojo: "it's a vast world" (I did not say "wild";-)), hard to get it right the first time :-[, continuous learning and refactoring have been, and still are, close companions to my dojo experience,

Cheers,
jc


Le 24/08/2017 à 00:22, Deno Vichas a écrit :
i would suggest sub classing _WidgetBase and not using your own init function - https://dojotoolkit.org/reference-guide/1.10/dijit/_WidgetBase.html  - at the very least  - own() is pretty useful.  if not i would at least have my init function returns a promise ensure correct timing/ordering of things. 

you also need to be careful of scoping when doing things like

                args.tabsDescription.forEach(function(description){
                     this.tabs.create(description);
                });


2 issues here.  the use of an anon function - this will make you're stack hard to read while debugging..  use name functions.   this.tabs is referring to the this of the anon function and the other scope you're actually trying to ref.




On 8/23/2017 2:52 PM, Hujeux Jean-Claude wrote:
See stackoverflow for the answer to your first question.

As per a "simple, basic skeleton, of the overall layout", I can share the overall organization of my application, the result of several years of refactoring, and hoping it's good practice enough ...

  • the main html file (corresponds to the index.html file of the boiler plate - in my case it does not because I've got an authentication layer and server side stuff going on before the main html file can be sent to the server):
<!DOCTYPE HTML>
<html
    <head>
        <meta charset="utf-8">
        <title>...</title>
        <link rel="stylesheet" href="...myApp.css" media="screen">
    </head>
    <body class="claro">
        <div id="appLayout" class="demoLayout" ></div>
         <script>var dojoConfig ={...};
        </script>
        <script src=".../dojo/dojo.js"></script>

        <script>
var pageDescription = {...}; // the initial description of the page.
require([ "{appDir}/PageManager"],
                function(PageManager){
                    PageManager.initialize(pageDescription);
                    document.body.className += ' loaded';
            });
            </script>
    </body>
</html>

  • the js side: everything is AMD modules, in the src/myApp directory (and sub-directories):

    • PageManager.js: this is where all what takes place at page level is orchestrated:
define([...], function(...){
    var ...;
    return {
        initialize: function(args){ // args is an object that contains the initial description of the page to be displayed and is used throughout the application. PageManager.initialize() is called from the html page above
                ...
                // building the overall page layout (see the layout tutorial here)
                var appLayout = new BorderContainer({...}, "appLayout"), leftPane = new AccordionContainer({...}), headerPane = new ContentPane({content: args.headerContent, ...}, tabsContainer = new TabsContainer({...});
                appLayout.addChild(leftPane);
                appLayout.addChild(headerPane);
                appLayout.addChild(CenterPane);

                // adding accordions & tabs
                this.accordion = new AccordionManager({container: leftPane);
                args.accordionDescription.forEach(function(description){
                     this.accordion.create(description);
                });
                this.tabs = new TabsManager({container: tabsContainer);
                args.tabsDescription.forEach(function(description){
                     this.tabs.create(description);
                });
                ...
                ready(function(){
                    appLayout.startup();
                });
        },
        ... here other functions made available to all application modules that need it, in particular:
        serverDialog: function(...){ // calls dojo/request and is used by the application modules every time the page needs to interact with the server, (allows to standardize server interaction and centralize feedback / error handling for the page)
         },
         etc.
});
    • AccordionManager.js, TabsManager.js: //an intermediate layer that provides methods to manipulate the page tabs and accordion panes in the various modules (create a new tab, go to a specific tab, etc.)
define([...], function(...){
    return declare([...], {
        constructor: function(args){
            this.container =   args.container;       
               ...
        },
        create: function(tabDescription){
            ...
            var myNewTab = new MyAppTab(tabDescription)        
            this.container.addChild();
            return myNewTab;
        },
        gotoTab: function(...){
        },
        etc.       
    });
});
    • MyAppTab.js: this is where the tab is laid out based on its description: layout and associated widgets. Is typically the extension of a layout widget (e.g a ContentPane,  a BorderContainer, ...)

      define([...], function(...){
          return declare([ContentPane], {
               postCreate(){
                // here multiple (recursive) instantiations dojox/layout/TableContainer based on the tab description
              },
              refresh: function(...){
              },
              etc.       
          });
      });
    • MyCustomWidget1.js //
    • MyCustomWidget2.js, etc.

For tab and accordion panes (and TooltipDialog, etc.) layout, I use dojox/layout/TableContainer which displays the contained child widgets as a table (and can be nested). Not perfect,  does the job though.

When a file becomes too large, or to share code between modules I make extensive use of mixins and utilities modules. This way, the majority of my files fit on the editor screen (or close to).

Finally, to describe my page, I need to fill in the pageDescription object in the main html file, e.g.:

    var pageDescription = {
        headerContent: {...},        
        accordionDescription: [{title: "first pane title", layout: {<the first pane layout description>}, widgets: {widget1: {label: 'name', type: 'TextBox', value: 'this is my name'}, { ...}, { ...}}},{title: "second pane title, ...}],
        tabsDescription: [{title: "first tab title", layout: {<the first tab layout description >}, widgets: {widget1: {label: 'xyz', type: 'MyCustomWidget1', value: 'this is xyz'},  widget2:  { ...},...}},{title: "second tab title, ...}],
        etc.
    };

I did it this way initially as I had made the choice to put the business logics (which drives the content of the page) on the server side, using php / mySql: in my case, pageDescription is built by this php layer.  Then the js/dojo side focuses on the presentation and interaction logics. There are obviously other possible design choices.

Of course this is only the tip of the iceberg, still I hope you'll find it useful,

jc

Le 22/08/2017 à 00:43, Karo Zans a écrit :
Hi everyone.  I am trying to develop a medium sized web app with dojo.

I have started out with a basic BorderContainer that fits the entire page and now I need to fill up each section with widgets.  I would like to keep my code very easily manageable and I don't like to have files that are massively long if I can keep from it.

I have the basic BorderContainer and I would like to create fully modularized widgets so that I can easily insert each widget by one line of code, like...

var topTabs = new TopTabs();

All of the widgets that I will be inserting into each of the sections will have instances of other Dijits in them.  Like TabContainers, buttons, and checkboxes.

So my question is....

What is the "best" way to approach this?  Should I create template based widgets for the widgets that want to put in each section?

Or is it better to create widgets that programmatically insert dijits?

Can someone please provide a simple, basic, skeleton of what my widgets and file structure should be for good code maintainability and such?

Thanks






-- 
www.fullstacksystems.com




--
Dojo Toolkit: http://dojotoolkit.org/
Tutorials: http://dojotoolkit.org/documentation/

[hidden email]
To unsubscribe, visit: http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest