LWC: Sibling component not rerendering after Parent is updated - apex

I have an LWC which acts as a ToolBox: a user selects a tool from the ToolBelt and then a WorkArea is populated with the business logic for that tool.
Components involved:
Parent: Wrapper.js
Child 1: ToolBelt.js
Child 2: WorkArea.js
Where things are working properly: First, Wrapper.js passes a ToolSelection handler down to ToolBelt.js. On select, an event is emitted from ToolBelt.js to Wrapper.js where the selectedTool variable is being updated.
Where things are not working properly: selectedTool is decorated with #api in the parent, #track in the child, and the value is being successfully updated in the parent. But it is not being rerendered in the child component.
Parent.js:
import { LightningElement, api } from 'lwc';
export default class Toolbox extends LightningElement {
#api selectedTool
#api tools = [redacted]
toolSelectionHandler(event){
let updatedTools
let selectedTool;
const id = event.detail.id
const action = event.detail.action
if( action === 'unselect'){
updatedTools = this.tools.map( (tool) => {
tool.selected = false
return tool
})
this.selectedTool = null
} else{
updatedTools = this.tools.map( (tool) => {
if(tool.id === id){
tool.selected = true
selectedTool = tool
}
else {
tool.selected = false
}
return tool
})
this.selectedTool = selectedTool
}
this.tools = updatedTools
}
}
Parent.html:
<template>
<div class="slds-grid slds-wrap slds-grid--pull-padded">
<div class="slds-p-horizontal--small slds-size--1-of-2 slds-medium-size--1-of-6 slds-large-size--4-of-12" >
<c-toolbelt
tools={tools}
ontoolselected={toolSelectionHandler}
></c-toolbelt>
</div>
<div class="slds-p-horizontal--small slds-size--1-of-2 slds-medium-size--5-of-6 slds-large-size--8-of-12">
<c-work-area
selected-tool={selectedTool}
>
</c-work-area>
</div>
</div>
Leaving out Toolbelt.js and Toolbelt.html because the selection handler is working as expected.
WorkArea.js:
import { LightningElement, track } from 'lwc';
export default class WorkArea extends LightningElement {
#track selectedTool
#track isLoading = false
get tool1(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool1') {
matchBool = true
}
}
return matchBool;
}
get tool2(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool2') {
matchBool = true
}
}
return matchBool;
}
get tool3(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool3') {
matchBool = true
}
}
return matchBool;
}
spinnerHandler(){
this.isLoading = !this.isLoading
}
}
WorkArea.html:
<template>
<div class="work-area">
<div if:true={toolOne} class="tool-detail-area selected">
<c-tool-one
onspinnerhandler={spinnerHandler} >
</c-tool-one>
</div>
<div if:true={toolTwo} class="tool-detail-area selected">
<c-tool-two
onspinnerhandler={spinnerHandler} >
</c-tool-two>
</div>
<div if:true={toolThree} class="tool-detail-area selected">
<c-tool-three
onspinnerhandler={spinnerHandler} >
</c-tool-three>
</div>
<div if:false={selectedTool} class="tool-detail-area default">
<c-no-tool-display></c-no-tool-display>
</div>
<div if:true={isLoading}>
<c-loading-spinner></c-loading-spinner>
</div>
</div>
I've seen a few SO posts about LWC Child components not registering changes made to parent, but most of them are Parent > Child relationship directly. And because events aren't emitted from the child. I haven't seen any where a child modifies state of parent, and tracked variable in sibling isn't re-rendering.
Any help technically or conceptually would be appreciated.

(Converted from comments / expanded)
Why WorkArea's selectTool is just #track and not #api? Do you manage to pass anything at all to the child that way? I'm bit surprised. If it's a primitive (String, number etc) you shouldn't even need track.
It has to be #api to be part of the component's public interface so I think you setting the value on parent didn't have any effect. That variable was child component's private matter, nice try parent.
in parent's event handler try to rebuild the variable, this.selectedTool = JSON.parse(JSON.stringify(this.selectedTool)); and see if it helps
This one's complicated and reeks of cargo cult programming. I don't know. Rebuilding the variable (object or array of object) sometimes helps the #track / #api realise the value changed and propagate it properly. There's some caching / saving on network roundtrips at play.
https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reactivity_fields
It shouldn't be needed, #track should be sufficient... But people struggle with it, you can see the trick on some blog posts or
https://salesforce.stackexchange.com/q/274011/799
https://salesforce.stackexchange.com/q/354052/799
JSON.parse "dance" is OK but of course you'll lose any functions you might have had attached to the object, Dates will flatten to strings... It's quite common to use it when debugging something in JS console and you're getting angry with all the Proxy objects. Array spread operator works OK too for giving the #track/#api a nudge, probably faster execution too.
Another place where it helps is when you need to modify what was sent from Apex. Normally that object is readonly, you need the JSON.parse or spread to make a copy(?). For example good luck using <lightning-tree-grid> with any data coming from server-side. It requires child nodes to be _children but Apex doesn't compile with variable names starting with underscore. So you need to get your data and then decorate it a bit in JS: https://salesforce.stackexchange.com/a/235214/799

Related

AureliaJS - Call child function from parent

I'm using AureliaJS to build a dynamic forms scenario, where I have a parent form with the gross operations needed and multiple child's form's, that change based on user input.
These child's form's have only two specific things themselves. Their model and the validation rules for their model.
So my question is, how can the parent form call the validation rules from the current child form? From child I know that is possible call parent's view model. But from parent, how can I invoke any function from the child?
The scenario is similar off having one base class, that has one method and this method could be overriding on the child classes.
Any suggestion? I'm glad to change the approach if needed.
Here's an example: https://gist.run?id=1865041a15af60600cb7b538018bdccd
app.html
<template>
<span>This is an APP</span>
</p>
<compose view-model.bind="'parentForm'"></compose>
</template>
app.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class App {
}
childForm1.html
<template>
<label> Price : </label>
<input value.bind="model.data.price">
<p/>
<label> VAT : </label>
<input value.bind="model.data.vat">
<p/>
</template>
childForm1.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm1 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.price != '' && this.model.data.vat == '' )
this.model.validateMessage = 'VAT is mandatory';
}
}
childForm2.html
<template>
<label>Address : </label>
<input value.bind="model.data.address">
<p/>
<label>Phone : </label>
<input value.bind="model.data.phone">
<p/>
</template>
childForm2.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm2 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.phone != '' && this.model.data.address == '' )
this.model.validateMessage = 'Address is mandatory';
}
}
index.html
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
parentForm.html
<template>
<button click.delegate="changeChildForm1()">Change Child Form 1</button>
<button click.delegate="changeChildForm2()">Change Child Form 2</button>
<p/>
<p/>
<form>
<label>User : </label>
<input value.bind="model.data.user">
<p/>
<compose view-model.bind="childFormVM" model.bind="model"></compose>
<button click.delegate="save()">Save</button>
<p/>
<span> Validation Message : ${model.validateMessage}</span>
</form>
<p/>
<span>Price : ${model.data.price}</span><p/>
<span>Vat : ${model.data.vat}</span><p/>
<span>Phone : ${model.data.phone}</span><p/>
<span>Address : ${model.data.address}</span><p/>
</template>
parentForm.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ParentForm {
model = {
validateMessage : '',
data : {
user : 'My Name'
}
};
childFormVM = 'childForm1';
validateMessage = '';
changeChildForm1() {
this.childFormVM = 'childForm1';
}
changeChildForm2() {
this.childFormVM = 'childForm2';
}
save(){
this.validateRules();
// How to call the validation rules from child ?
}
validateRules (){
this.model.validateMessage = 'Validate by parent';
}
}
Bind a function call to the child so that you have a handle to invoke it from the parent. I usually prefer to directly bind the child components rather than using compose, but you can make it work with compose by passing a complex model object rather than only the model, and passing the binding function as one of the model properties.
Parent View-Model:
class Parent {
model = {};
child1Validate = null;
changeChildForm1() {
if (typeof this.child1Validate === 'function') {
// the binding was successful; proceed with function call
let result = this.child1Validate();
console.log(result);
}
}
}
Parent View:
<my-child1 model="parentModel" go-validate="child1Validate"></my-child1>
Child View-Model:
class MyChild1 {
#bindable model;
#bindable goValidate;
bind() {
// bind the child function to the parent that instantiates the child
this.goValidate = this.runValidation.bind(this);
}
runValidation() {
// do the validation and pass result to parent...
return 'Success!';
}
}
That's how you can do it:
parent-form.html
<compose view-model.bind="childFormVM" view-model.ref="childFormInstance" model.bind="model"></compose>
parent-form.js
save() {
this.childFormInstance.currentViewModel.validateRules();
}
Helpful Notes
Only use <compose> when necessary. For example, in the app.html you should replace <compose> for:
<require from="parentForm"></require>
<parent-form></parent-form>
Use kebab-case instead of camel-case to name your files. For example, instead of parentForm.html and parentForm.js, use parent-form.html and parent-form.js. This won't change a thing in your code but you will be following nice javascript standards :)
When binding directly to a string you don't need to use .bind. For example, view-model.bind="'parentForm'" could be replaced for view-model="./parentForm"
Hope this helps!
One thing that immediately comes to mind is that you can inject the parent model into your child model in the constructor -- the injected instance will be the same, not a newly created one. This way, your parent can define a method that allows the child to register itself on the parent, and the parent can then invoke whatever methods exist on the child at the time of its choosing.
This creates a rather strong coupling between the components, though, so you will need to consider whether or not that is acceptable to you.
If it isn't, another way to approach the issue is to use the event aggregator. The parent form can dispatch an event on the aggregator, and the children will be subscribers listening for the event. In this case, depending on whether or not you host multiple such combinations on one page, you may want to include a unique identifier for the form that is sent along with the event and bind that ID to the child components, so they will know to only listen for events from their parent.

How can I detect/watch "dirty-status" of an angular2-form in the right way?

I have a form in Angular2 (e.g)
<form id="myLovelyForm" name="myLovelyForm" #myLovelyForm="ngForm">
<label [attr.for]="myLovelyCheckbox">
<input [attr.id]="myLovelyCheckbox" type="checkbox"
[(ngModel)]="myLovelyCheckbox">
<span class="myLovelyCheckbox">myLovelyCheckbox</span>
</label>
</form>
and an animation, which should start, if the form is dirty:
<div
id="myLovelyNotification"
class="myLovelyNotification"
[#notification]="myLovelyForm.form.dirty">
.....
.....
</div>
The animation works properly if I set [#notification] = true, but my myLovelyForm.dirty does not fire, if I touch the form and change an element.
If the #notification is false, the animation stops, i.e. if the checkbox was selected before and I unselect it mistakenly and select it again, the form is not pristine (touched) but not dirty anymore, therefore the animation should stop. If I set the #notification = false manually, it works properly.
The big question is: How can I detect/watch "dirty-status" of an angular2-form in the right way?
Simply -
#ViewChild('f') templateForm: any;
ngOnInit() {
this.templateForm.valueChanges.subscribe((value: any) => {
if (this.templateForm.dirty) {
console.log('template form dirty - yes: ', value);
} else {
console.log('template form dirty - no: ');
}
});
}
Where your template contains:
<form #f="ngForm" (ngSubmit)="save(f)>
...
</form>
However this is still using template forms which are really there to help bridge the gap with Angular1 apps. Model Driven forms are the Angular 2 way of doing it for anything but real basic applications. See for example:
https://blog.thoughtram.io/angular/2016/06/22/model-driven-forms-in-angular-2.html
http://blog.ng-book.com/the-ultimate-guide-to-forms-in-angular-2/
And use custom components to really extend and excell your app - https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html
You can subscribe to form changes:
this.physicalForm.valueChanges
.map((value) => {
return value;
})
.subscribe((value) => {
if(this.selectedPhysical.weight != this.physicalForm.value.weight) {
this.selectedPhysical.weight = this.physicalForm.value.weight;
}
this.isDirty == this.physicalForm.touched;
});
If this event fires, then you know your form is dirty.
this is an example from my actual app (nut.abbr is the formcontrolName):
ngOnInit() {
for (let nut of this.userSettings.nutrientData) {
this.foodSettingsForm.controls[nut.abbr].valueChanges
.subscribe(v => { console.log("value: ", v); this.completeValueChange(nut.abbr, v); });
}
}
completeValueChange(field: string, value: boolean) {
this.isChanged = true;
Nutrient.updateNutrient(field, value, this.userSettings.nutrientData);
}

How to change input value in redux

I am making a file manager app based on react-redux, and I meet problem with input.
For example, my code:
PathForm.js:
export default class PathForm extends Component {
render() {
const { currentPath, handleSubmit } = this.props;
console.log('PathFormPathFormPathForm', this.props)
return (
<div className="path-box">
<form onSubmit={handleSubmit}>
<div>
<input type="text" className="current-path-input" placeholder="input path" value={currentPath} />
</div>
<button className="go-btn" type="submit">Go</button>
</form>
</div>
);
}
}
Explorer.js:
class Explorer extends Component {
goPath(e) {
e.preventDefault()
// fake function here, because I have to solve the input problem first
console.log('PathForm goPath:',this.props)
let {targetPath , actions} = this.props
swal(targetPath)
}
render() {
const { node, currentPath , actions} = this.props
console.log('Explorer.render:',this.props)
return (
<div className='explorer-container'>
<PathForm currentPath={currentPath} handleSubmit={this.goPath.bind(this)}/>
<FileListOperator />
<FileListView fileList={node && node.childNodes} actions ={actions} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
node: state.tree[state.tree.currentPath],
currentPath: state.tree.currentPath
};
}
function mapDispatchToProps(dispatch) {
console.log('mapDispatchToProps')
return {
actions: bindActionCreators(NodeActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Explorer);
Feature I want:
I have a PathForm, it need show path from two way:
user click a file path from left tree view, Explorer get this path as currentPath, then pass to PathForm, and show currentPath in input
user directly type a path to the PathForm's input, PathForm call handleSubmit(Explorer's function) to change the currentPath
Additional:I want to keep PathForm as a stateless component
The problem:
I'd like use PathForm as a stateless form, so I don't want connect it to store, but I need it change input by currentPath. But if I set value={currentPath}, user can not type anything else.
change to <input type="text" onChange={this.changeValue} value={this.getValue()}/> allow user type string in this input, but can not use props currentPath passed by Explorer
The only way I can imagine is connect this form to store which I don't want. I'd like Explorer to dispatch all actions and pass props.
Tried with some package
I found the input not act as my thought, so I tried the two popular package:
redux-form
It create a form need so much code, and official doc not say how to render this form with parent props,
I try to pass props and handleSubmit to it, not work. After I see
React + Redux - What's the best way to handle CRUD in a form component?
and How to wire up redux-form bindings to the form's inputs
I found I can't do that, it define some function overwrite mine, this behave is not good for me(I have to change the handlerSubmit function name, but it still not work), and it connect to the store. So I turn to formsy-react
formsy-react
It still need so much code, though it provide some mixin, but I still have to write a custom text input with changeValue function myself(changeValue is no need in most situation when writing normal html jquery app).Then I found the problem that PathForm can not use props currentPath passed by Explorer...
Probably Worked solution(but I don't tend to use):
connect PathForm to store, add another state inputPathValue for this input. Use inputPathValue interact with currentPath
After above, I found use input/form is super in-convenient in react....
Does it mean I have to connect PathForm to stroe?
Any other way to solve my problem?
There are uncontrolled(not set value) and controlled(set value) input in reactjs.
controlled not allow user input, but uncontrolled does.
Solution:
Need use uncontrolled input(no value attribute).
Select input element and set the value when currentPath change.
Bad way:
code:
export default class PathForm extends Component {
changeCurrentPath(path) {
const pathInput = document.querySelector('.current-path-input')
if (pathInput){
pathInput.value = path
this.lastPath = path
}
}
render() {
const { currentPath, handleSubmit } = this.props;
console.log('PathFormPathFormPathForm', this.props)
this.changeCurrentPath(currentPath)
return (
<div className="path-box">
<form onSubmit={handleSubmit}>
<div>
<input type="text" className="current-path-input" placeholder="input path" />
</div>
<button className="go-btn" type="submit">Go</button>
</form>
</div>
);
}
}
Good way:
use componentWillReceiveProps to set props and rel to select element
1.use form submit
export default class PathForm extends Component {
constructor(props) {
super(props)
// can not find `this` if not bind
this.handleSubmit = this.handleSubmit.bind(this)
}
componentWillReceiveProps(nextProps) {
if (nextProps.currentPath !== this.props.currentPath) {
this.setInputValue(nextProps.currentPath)
}
}
getInputValue() {
return this.refs.pathInput.value
}
setInputValue(val) {
this.refs.pathInput.value = val
}
handleSubmit(e){
e.preventDefault()
this.props.handleSubmit(this.getInputValue())
}
render() {
return (
<div className="path-box">
<form onSubmit={this.handleSubmit}>
<input className="current-path-input"
defaultValue={this.props.currentPath}
ref="pathInput" />
<button className="waves-effect waves-light btn" type="submit">Go</button>
</form>
</div>
);
}
}
2.use button click
export default class PathForm extends Component {
constructor(props) {
super(props)
// can not find `this` if not bind
this.handleGoClick = this.handleGoClick.bind(this)
this.handleKeyUp = this.handleKeyUp.bind(this)
}
componentWillReceiveProps(nextProps) {
if (nextProps.currentPath !== this.props.currentPath) {
this.setInputValue(nextProps.currentPath)
}
}
getInputValue() {
return this.refs.pathInput.value
}
setInputValue(val) {
this.refs.pathInput.value = val
}
handleKeyUp(e) {
if (e.keyCode === 13) {
this.handleGoClick()
}
}
handleGoClick(e) {
e.preventDefault()
this.props.handleSubmit(this.getInputValue())
}
render() {
return (
<div className="path-box">
<form >
<input className="current-path-input"
defaultValue={this.props.currentPath}
onKeyUp={this.handleKeyUp}
ref="pathInput" />
<button className="waves-effect waves-light btn" type="submit" onClick={this.handleGoClick}>Go</button>
</form>
</div>
);
}
}
If you really don't want the state in Redux, you can instead store the state on the component with setState. Directly accessing the input is strongly discouraged. You should track the state of the input on the component. Add an onChange handler to the input, store the state and handle componentWillReceiveProps where you decide what to do with new incoming props.

CQ5/AEM6/Sightly - Return custom type from Java Use-Api

Using JavaScript Use-Api I am able to create a custom object and return it to a html file. This feature allows me to create a list of custom objects, which can be used to create a menu or other complex list-like component.
Let's assume that I have following content structure:
/content
/project
/homepage
/contentpage1
/contentpage1.1
/contentpage1.2
/contentpage1.3 (hidden)
/contentpage2
/contentpage1.1 (hidden)
/contentpage1.2 (hidden)
/contentpage1.3 (hidden)
/contentpage3
/contentpage4
Menu should contains only first-level contentpages. Each menu item should have dropdown list with second-level contentpages, if they exist and are not hidden. I can do it in JavaScript with the following code:
"use strict";
use(function() {
function getMenuItems() {
var currentPageDepth = currentPage.getDepth();
var menuObjects = [];
if(currentPageDepth >= 3) {
var homePage = currentPage.getAbsoluteParent(2);
var list = homePage.listChildren();
while(list.hasNext()) {
var tempPage = list.next()
var customPageObject = createMenuItemObject(tempPage);
menuObjects.push(customPageObject);
}
}
return menuObjects;
}
function createMenuItemObject(page) {
// ...
// looking for any other properties of page or its children
// ...
return {page: page,
visibleChildrenExists: visibleChildrenExists(page)};
}
function visibleChildrenExists(page) {
var list = page.listChildren();
var visibleChildrenExists = false;
while(list.hasNext()) {
var subPage = list.next();
if(!subPage.isHideInNav()) {
visibleChildrenExists = true;
break;
}
}
return visibleChildrenExists;
}
return {
menuObjectsList: getMenuItems(),
};
}
HTML:
<headerComponent data-sly-use.headerComponentJS="headerComponent.js" data-sly-unwrap />
<menuItems data-sly-list.menuItem="${headerComponentJS.menuObjectsList}" data-sly-unwrap >
<li class='${menuItem.visibleChildrenExists ? "" : "direct"}' data-sly-test="${!menuItem.page.hideInNav}">
${menuItem.page.title}
<ul data-sly-test="${menuItem.visibleChildrenExists}" data-sly-list.submenuItem="${menuItem.page.listChildren}">
<li data-sly-test="${!submenuItem.hideInNav}">
${submenuItem.title}
</li>
</ul>
</li>
</menuItems>
Why do I want to use Java Use-Api? It's easier to operate on interfaces like Resource or Node. It looks like it does not work pretty well in JavaScript, but I need to have possibility to return custom objects with multiple properties.
The question is: is it even possible to do something similar using Java Use-Api? What do I have to return? I can't return a map, because it won't be possible to access its elements since it's not possible to pass a parameter to Java Use-Api method... Any suggestion?
It is possible to return maps using the java-use api see an example below:
Method in the Java class
//Return a map
public Map<String, String> getTestMap() {
//TODO some coding
Map<String,String> testMap = new HasMap<String,String>();
testMap.put("IDA", "test value");
testMap.put("IDB", "test value 2");
return testMap;
}
HTML code to access each element of the map:
<div data-sly-use.param="JavaClass">
<div data-sly-test.map="${param.testMap}">
<div class="pos">
<span class="classA">${map['IDA']}</span><br>
<span class="classB">${map['IDB']}</span>
</div>
</div>
</div>

Mootools stop form submit method

I don't want to use an <input type=submit /> button to submit a form and I am instead using an <a> element. This is due to styling requirements. So I have this code:
myButton.addEvent('click', function() {
document.id('myForm').submit();
});
However, I have also written a class that improves and implements the placeholder attribute on inputs and textareas:
var FDPlaceholderText = new Class({
Implements: Events,
initialize: function() {
var _self = this;
var forms = document.getElements('form');
forms.each(function(form) { // All forms
var performInit = false;
var i = 0;
var ph = [];
form.getElements('input, textarea').each(function(el) { // Get form inputs and textareas
if (el.getProperty('placeholder') != null) { // Check for placeholder attribute
performInit = true;
ph[i] = _self.initPlaceholder(el); // Assign the placeholder replacement to the elements
}
i ++;
});
if (performInit) {
_self.clearOnSubmit(form, ph);
}
});
},
clearOnSubmit: function(form, ph) {
form.addEvent('submit', function(e) {
ph.each(function(el) {
if (el.value == el.defaultValue) {
el.value = '';
}
});
});
},
initPlaceholder: function(el) {
el.defaultValue = el.getProperty('placeholder');
el.value = el.getProperty('placeholder');
el.addEvents({
'focus': function() {
if (el.value == el.defaultValue) el.value = '';
},
'blur': function() {
if(el.value.clean() == ''){
el.value = el.defaultValue;
}
}
});
return el;
}
});
window.addEvent('domready', function() {
new FDPlaceholderText();
});
The above class works great if a form is submitted using an actual <input type=submit /> button: it listens for a submit and clears the inputs values if they are still the default ones therefore validating that they are essentially empty.
However, it seems that because I am submitting one of my forms by listening to a click event on an <a> tag the form.addEvent('submit', function(e) { isn't getting fired.
Any help is appreciated.
well you can change the click handler to fireEvent() instead of call the .submit() directly:
myButton.addEvent('click', function() {
document.id('myForm').fireEvent('submit');
});
keep in mind a couple of things (or more).
placeholder values to elements that lack placeholder= attribute is pointless
if you detect placeholder support, do so once and not on every element, it won't change suddenly midway through the loop. you can go something like var supportsPlaceholder = !!('placeholder' in document.createElement('input')); - remember, there is no need to do anything if the browser supports it and currently, near enough 60% do.
you can otherwise do !supportsPlaceholder && el.get('placeholder') && self.initPlaceholder(el); - which avoids checking attributes when no need
when the form is being submitted you really need to clear placeholder= values in older browser or validation for 'required' etc will fail. if validation still fails, you have to reinstate the placeholder, so you need a more flexible event pattern
avoid using direct references to object properties like el.value - use the accessors like el.get('value') instead (for 1.12 it's getProperty)
for more complex examples of how to deal with this in mootools, see my repo here: https://github.com/DimitarChristoff/mooPlaceholder
This is because the submit() method is not from MooTools but a native one.
Maybe you can use a <button type="submit"> for your styling requirements instead.