Hi,
I have some controls nested like this :
container1
..container2
....button1, button2, ...
The buttons are added dynamically during runtime (different for each user).
I want to replace FiC's HTML for the buttons with my own HTML, so I put this in button.wcHTMLGen() :
LPARAMETERS toHTMLgen AS awHTMLgen OF awHTML.prg, tlInnerHTML && {en} doc in Parent Code {fr} doc dans le code parent
Local MetroUIHTML
Text to MetroUIHTML textmerge noshow pretext 2
<button type="button" id="<<this.wcID>>" class="bg-orange tile-medium" title="<<this.ToolTip>>" tabindex="20" data-role="tile" data-role-tile="true"><<this.Caption>></button>
Endtext
this.wcHTML = MetroUIHTML
nodefault
But at runtime I continue to get the FiC HTML instead.
What am I doing wrong?
Also, I want to include this HTML in only the form with the buttons (to make the buttons work using the MetroUI framework) :
<script type="text/javascript" charset="utf-8" src="/awScripts/bower/jquery/dist/jquery.min.js?2.30.0-beta.4"></script>
<script type="text/javascript" src="Extensions/vendor/Metro-UI-CSS-master/build/js/metro.js"></script>
<link href="Extensions/vendor/Metro-UI-CSS-master/build/css/metro.css" rel="stylesheet">
<link href="Extensions/vendor/Metro-UI-CSS-master/build/css/metro-icons.css" rel="stylesheet">
I can put it in xxxserver.cawJSinc(), which works (not sure about the stylesheets, I think so), but I would like to know if there might be a better place for it. Maybe a better place just for the stylesheets?
Thanks
Paul
Hi,
You can do directly:
TEXT TO THIS.wcHTML ...
and supress yourNODEFAUT
I think it's the right place. Example:
RETURN DODEFAULT (m.tcJSadd + ", metro.js")
buttons are added dynamically during runtime (different for each user)
FoxInCloud generates the form's HTML upon first demand, the same for all users; if container.wlContentDynamic
, HTML of content is replaced on each user request. However this dynamic HTML replacement is meant for display only and does not (yet) support user events, and your buttons must have some.
The solution is to generate ALL buttons for ALL users and to hide/show those that each user needs (I understand metroUI
is responsive and uses dynamic positioning like Bootstrap, not absolute positioning like VFP and FoxInCloud's classic rendering mode)
procedure button.Init
…
wcPropSaveEdit(this, 'Visible')
return dodefault()
procedure form.Init
&& after .wlInitFirst section
for each button in container.controls foxobject
button.Visible = function(User)
button.Left = ?? && for desktop mode
endfor
This way the buttons' events will be properly implemented.
button.wcHTMLGen()
Your implementation should work even if, like Vincent pointed out, your code can be more simple. Also, you need to implement the button's events.
LPARAMETERS toHTMLgen AS awHTMLgen OF awHTML.prg, tlInnerHTML && {en} doc in Parent Code {fr} doc dans le code parent
local cEvents
cEvents = m.toHTMLgen.cEvents() && better outside of textmerge()
this.wcHMTL = textmerge([<button type="button" id="<<this.wcID>>" class="bg-orange tile-medium" title="<<this.ToolTip>>" tabindex="20" data-role="tile" data-role-tile="true"<<m.cEvents>>><<this.Caption>></button>])
xxxProcess.cawJSinc()
That's the way to go: .cawJSinc()
for JS, .cawCSSinc()
for CSS; please note:
- these are
xxxProcess
methods, notxxxServer
- you can load the library only for the master forms that need it (not applicable to child forms)
PROTECTED FUNCTION cawJSinc && <script></script>+ tags {en} for FoxInCloud, application, form [, custom] {fr} De FoxInCloud, de l'application, du formulaire[, spécifiques]
LPARAMETERS ;
tcJSAdd; && [''] {en} Application, current form[, and custom] {fr} JS files URLs (UTF-8 encoded) {fr} URL des fichiers JS de l'application et du formulaire courant (encodés en UTF-8)
, toForm as awFrm of aw.vcx; && {en} Reference to form && {fr} Référence au formulaire
, tcForm; && {en} .Name of form {fr} Nom du formulaire
&& if inlist(m.tcForm, 'form1', 'form2', …) && possible optimization
return dodefault("Extensions/vendor/Metro-UI-CSS-master/build/js/metro.js," + m. tcJSAdd)
PROTECTED FUNCTION cawCSSinc
LPARAMETERS ;
tcCSSadd;
, toForm as awFrm of aw.vcx; && {en} Reference to form && {fr} Référence au formulaire
, tcForm; && {en} .Name of form {fr} Nom du formulaire
&& if inlist(m.tcForm, 'form1', 'form2', …) && possible optimization
return dodefault(''; && loads custom CSS before the form's CSS
+ "Extensions/vendor/Metro-UI-CSS-master/build/css/metro.css,"
+ "Extensions/vendor/Metro-UI-CSS-master/build/css/metro-icons.css,"
+ m. tcCSSAdd;
)
Thanks,
I found the cause of my problem : I am also replacing the HTML of the container via container.wcHTMLGen() and did not put a =dodefault() there.
I understand what you are saying about the dynamic buttons, but the dynamic buttons work correctly already without me having to do what you have suggested, with events.
I see that Form.htm contains only the HTML of the container and the button HTML is injected later via ajax.
What I am trying to do now is strip away the outer container, or at least remove its height, width etc, as it is constraining the MetroUI buttons. Can I do something to simply remove it from the HTML, or at least remove its position attributes?
the dynamic buttons work correctly already without me having to do what you have suggested, with events
My bad, since we moved from PrototypeJS
to jQuery
, as jQuery automatically removes the event handlers when removing elements, we now support events on dynamic contents without memory leak.
Having events implemented without executing m.toHTMLgen.cEvents()
? Can't figure how it's possible…
However there's a limitation: in a multi-user environment, the server does not destroy and recreate the contained buttons for each user; in other words you will run in scenarios where an event on the button (say .click()
) occurs on an ID for a button that is not instantiated on the server.
You can implement the event on the parent container instead (eg. .btnClick()
) and implement the button click this way:
procedure button.click()
lparameters nButton, nShift, nXcoord, nYcoord
it thisForm.wlHTMLgen
return textmerge([FoxInCloud.MethExec(];
+ [this] && {en} event or event source object
+ [, "<<this.Parent.wcID>>"] && {en} id of HTML object matching VFP object holding the method
+ [, "btnClick"] && {en} method
+ [, <<this.btnID>>] && {en} Value to be passed as method parameter - undefined for none
+ [)];
)
endif
return this.Parent.btnClick(this.btnID) && new property
procedure container.btnClick(btnID)
do case
case m.btnID == 1
…
endcase
Form.htm contains only the HTML of the container
I guess it contains no button at .wlInitFirst
strip away the outer container, or at least remove its height, width etc.
You need it positioned but not dimensioned so that its width and height adapt to its content. Here is what you can do:
procedure button.wcHTMLgen
LPARAMETERS toHTMLgen AS awHTMLgen OF awHTML.prg, tlInnerHTML && {en} doc in Parent Code {fr} doc dans le code parent
local cStyle, cHTML
cStyle = m.toHTMLgen.cStyle('position') && better outside of textmerge()
cHTML = m.toHTMLgen.getHTML_(.T.) && inner HTML only && not sure it's useful is your case
this.wcHMTL = textmerge([<div id="<<this.wcID>>"<<m.cStyle>>><<m.cHTML>></div>])
Yes, I have tried setting the container to a simple div, but FiC always injects both the position and dimension elements automatically.
Even the code you have suggested still results in all style elements being present!
No, FoxInCloud injects nothing after you set .wcHTML
.
Please provide more details such as the HTML/CSS/JS generated for this container, and where you see the dimensions specified.
You are quite right; I had =dodefault() at the end of the code and that was causing the generation of the additional tags.
The next thing I would to do is take those dynamic buttons inside the containers and use them in a custom HTML page.
In a quick test I did not get the buttons being generated - is this something I can achieve?
i.e. the form HTML is replaced by :
toHTMLgen.getHTML_()
this.wcHTML = "some new HTML"
and I use the same ID's on the divs (containers) that would normally be present in the standard FiC page.
Do you think this should work?
Thanks.
In a quick test I did not get the buttons being generated
What did your test do exactly?
use the same ID's on the divs (containers) that would normally be present in the standard FoxinCloud page
can't understand what you expect; can you post a screenshot or anything else visual?
Do you think this should work?
Don't have the slightest idea of what you expect; this code sample may help you:
* ------------------------------------------------------------
protected procedure wFormStandardPage_Output_ && {fr} page standard FoxInCloud pour un formulaire
lparameters ;
tcTitle; && [''] <title> contents
, tcHTML; && [''] {en} HTML of page contents {fr} HTML du contenu de la page (formulaire[s])
, tcScript; && [''] {en} Script for page's form(s) {fr} Script du ou des formulaires figurant dans la page
, toForm as awFrm of aw.vcx; && {en} Reference to form && {fr} Référence au formulaire
, tcForm; && {en} .Name of form {fr} Nom du formulaire
if m.tcForm == "xx"
this.oHTML.oControl = m.toForm.container
tcHTML = this.oHTML.getHTML()
text to tcHTML …
endtext
endif
return dodefault(;
m.tcTitle; && [''] <title> contents
, m.tcHTML; && [''] {en} HTML of page contents {fr} HTML du contenu de la page (formulaire[s])
, m.tcScript; && [''] {en} Script for page's form(s) {fr} Script du ou des formulaires figurant dans la page
, m.toForm as awFrm of aw.vcx; && {en} Reference to form && {fr} Référence au formulaire
, m.tcForm; && {en} .Name of form {fr} Nom du formulaire
)
I have a form that works fine in FiC :
.form
..container
....container (.wlContentDynamic = .T.)
......code that adds dynamic controls per user
All good.
What I want to achieve is replace the "page/form level" HTML with something else, but keep the ability (HTML, AJAX, whatever) to have the dynamic buttons added per user to a div of the replacement page.
So, I replace the page HTML as I have done elsewhere :
function form.wcHTMLGen
toHTMLgen.getHTML_() && this generates the standard JS for the form
this.wcHTML = "some new HTML"
(This code does not call dodefault(), since calling that would render the standard HTML instead of the custom HTML. The problem is though that somewhere in the parent code the "event binding" of the dynamic controls is normally set up.)
In the custom HTML I create a div for each container using the same ID as the standard HTML - I was hoping that this would be sufficient to keep the dynamic content working. Alas not - the divs are present in the page but I assume the "trigger" for the AJAX dynamic system is not present.
I have done this before to bind the FiC events to controls in custom HTML, but I guess this is a different concept.
So, what can I do to achieve this please?
(I don't quite understand where your sample code would fit)
If this is not possible then ok, at least I know what I can and cannot do.
You may ask - why not alter the Foxpro form to achieve what I want?
Yes, that would be appropriate in many circumstances, but in this scenario I want to produce a very different "fancy HTML5" look and it seems easier to simply create new HTML.
Thanks
Paul
Hi Paul,
State maintenance/visual change detection and HTML generation are completely independent processes; IOW .wlContentDynamic
and .wlContentChanged
work regardless of how you generate the page's HTML, as long as the HTML elements such as <div>
have id="<<this.wcID>>"
(case sensitive).
If it does not work in your case, it requires some debugging.
FWIW .getHTML_()
returns the generated HMTL and accepts a boolean 1rst parameter to generate inside HTML only. By using this feature you can just wrap the 'innerHTML' inside your form's 'fancy HTML5'.