dominar icon indicating copy to clipboard operation
dominar copied to clipboard

Removing controls from DOM after Dominar has been initialized

Open stocksp opened this issue 8 years ago • 19 comments

The framework I'm using (Meteor) can remove controls after Dominar has been told about them. Not hide them, but take them out of the DOM. I believe Angular and React do the same.

Dominar::validateAll is not happy about fields not being there.

I hacked in a

if(!field)
  continue

inside the for loop and everything appears to be working fine. Am I asking for trouble doing this?

stocksp avatar Aug 30 '15 03:08 stocksp

I should probably add a removeField and general destroy function to dominar, along with a general config option for ignoreMissingFields ? What do you recon?

garygreen avatar Aug 30 '15 11:08 garygreen

Those would be great. All of my views/templates (html fragments) have created, rendered and destroyed events (part of the framework) so being able to cleanly remove Dominar when the template is removed would be great.

Also, single page frameworks like Meteor and Angular NEVER do Form submissions: data maybe collected from forms or controls (very common for there to be no Form at all, just groups of controls, Dominar seems fine with this as long as the Selector is in the DOM when the validator is created), The control data is sent to the backend through api's built into the framework, Meteor relying solely on sockets and angular having its own restful api as well as socket framework. The classic form submittal just doesn't happen.

So, I have to include

$('#someDomId').bind('dominar.submit-passed', function(event) {
    event.preventDefault();
});

in every template, to keep Dominar from sending off the form data. It would be nice to be able to set an option like submit: false and be able to eliminate the need to repeat this code in every template.

I mention this last one as I was getting very unpleasant results testing a template ... caused by a 'copy and paste' error where I didn't change the selector for the bind.

stocksp avatar Aug 30 '15 16:08 stocksp

Just added a destroy method to dominar. Does that help with Meteor?

garygreen avatar Sep 03 '15 22:09 garygreen

I was going to pull down the latest when I noticed that the dist folder hasn't been updated?! I've been using dominar-standalone.js.... The destroy will obviously free up memory (which is always a good thing).... what I need to not keep using my hacked Dominar is for Dominar to be ok with controls being removed underneath it (see my comment at the top of this thread)

stocksp avatar Sep 03 '15 23:09 stocksp

Yeah I haven't released new version yet. I'm going to hold off until I get a chance to release validatorjs 2.0.

I was thinking when you remove the elements (or meteor) why not just re-initialize dominar on the form, would that not work for you?

garygreen avatar Sep 03 '15 23:09 garygreen

It just gets complicated quickly. The markup looks like

{{#if  thisBeTrue}}
// markup here
{{/if}}

where thisBeTrue is a 'reactive' function that will be called if anything inside it changes. There can be many of these on a page and they can be nested, when exactly the DOM is ready is a guess so I was hacking in setTimeouts to be able to call validateAll() and be certain Meteor had 'really' rendered the controls. I found (and it's working) it to be much simpler to initialize all the controls you will be needing (or possibly needing) in a single event (onRendered) and just 'help' Dominar with the little hack above.

As i mentioned this is not peculiar to Meteor, Angular and React do the same thing.

stocksp avatar Sep 03 '15 23:09 stocksp

I'm definitely interested in supporting, just need to find a decent way of doing it. :smile:

When the view is rendered in Meteor onRendered could you .destroy and then re-initialize with the newly rendered view? I haven't any understanding of Angular / Meteor / React but I am fairly confident that they will produce some form of events that can be used to know when to re-initialize dominar on the form. That will free up any existing initialized fields, events, etc.

This is pretty common in a lot of plugins like date pickers, custom selects / checkboxes, etc. You need to reinitialize them once the view changes, I don't see why it would be much different from Dominar.

garygreen avatar Sep 04 '15 08:09 garygreen

Just to be clear: I'm talking about 'Blaze' Meteor's original renderer. Any day now there will be a new release of Meteor. Both React and Angular will be able to coexist with or replace Blaze. I'm very familiar with Angular (it has its own validation framework) and know little about React (it does not have a validation framework). If for no other reason than Facebook's muscle, React is and will be a big player in Meteor.

The problem with OnRendered is that it is only called once and it only tells you that the template has been rendered, it doesn't tell you if 'markup here' (see above) has been rendered. The markup (controls) may be created or destroyed by any number of 'events'. This is what makes Meteor so popular:

The thisBeTrue function above could be as simple as return col.someProp === true; Where col.someProp is a remote database property. If someProp changes in the database (doesn't have to be my app that changes it) controls will appear or disappear in my UI. Even if you try and keep track of the state yourself, you may be an 'event loop' behind and the controls haven't yet been put in the DOM and your call to validateAll will fail. So I either ended up hacking Dominar or putting in setTimeouts

stocksp avatar Sep 04 '15 15:09 stocksp

How does Meteor recommend to handle initializing e.g. date pickers, custom selects, star ratings, wysiwyg editors, sortable elements, etc when the view is constantly being re-rendered / changed with new elements? There must be a way it recommends when you should rebind stuff. I'm still not sure what the actual problem is with .destroy and re-initializing?

garygreen avatar Sep 04 '15 15:09 garygreen

I suspect (but I'm on thin ice here as I know very little about React) that Reacts popularity in the Meteor community has to do with this very issue at least in part. Your example of the datePicker is good. If its in its own child template it will get events for being created and destroyed ... but now you have a Dominar with controls spread across multiple templates, which I don't want to even think about. Just to be clear: Meteor only rerenders what you tell it to. You can just 'hide' elements the old fashion way. I do have complete groups of controls that are added and removed. The rest of the form is not rerendered. Dominar validateAll works fine (controls missing) with the small 'fix' above.

stocksp avatar Sep 04 '15 16:09 stocksp

What I'm trying to suggest is when you know your view has been re-rendered (thru 'done rendered' events, or whatever) THEN do dominar.destroy() and var dominar = new Dominar(..., ...); that way it will clear the whole instance and re-attach the events that it needs, etc. That would fix the submit issue.

Your not really answering the question if the above will work (sorry I don't know Meteor at all, but in all frameworks I've used you need to re-initialize stuff at some point exactly as I described above). Have you tried the new code for dominar with .destroy and see if it works?

garygreen avatar Sep 04 '15 16:09 garygreen

I think we are talking about different things here: There is no re-render event. Just a single rendered event There is no render event for this markup

{{#if  thisBeTrue}}
// show these controls
{{/if}}

'show these controls' may come and go. No events are fired when these controls are added or removed from the DOM. 'thisBeTrue' is called by Meteor every time something inside the function changes (this is the 'magic' of Meteor). The template that contains this markup will fire an onRendered one time when it is first added to the DOM. This does NOT tell me if the 'show these controls' are present (were rendered).
So, yes I am doing new Dominar in the onRendered event. But I can't know which controls I'll need so I give Dominar all that can possibly appear. As to your question 'How does Meteor recommend..' Meteor is still the Wild West .... hopefully it will settle down this year ...

stocksp avatar Sep 04 '15 17:09 stocksp

In all honesty, I think that's a pretty stupid design by Meteor. How come you can't tell when a control is updated? I really don't see how on earth you can confidently know when you should re-initialize date pickers, etc when it doesn't fire any form of event, so this problem isn't specific to dominar. Maybe you could take it up with them about why no events are fired when the UI is updated? How do they recommend to reinitialize stuff etc.

garygreen avatar Sep 04 '15 18:09 garygreen

I think the answer in the Meteor community has been React and Angular ... and I assume that the to-be- -released-any-minute-now update to Meteor with core support for different rendering engines confirms it. Blaze ain't going away, very easy to use, just gets difficult with component based UI's. Just to be clear: Meteor doesn't 'event' the control being updated ... I could set my own 'global' in 'thisBeTrue' which would function as an event. Its ugly as I mentioned because it just means the controls will be shown not that they are there now (event loop woes)

stocksp avatar Sep 04 '15 18:09 stocksp

I'm working with a highly dynamic form, without a framework like Meteor, React and Angular and I've added the following to help with debugging.

if (!field) {
    throw new ReferenceError(name);
}

I think we need a by field destroy method.

RichAyotte avatar Sep 22 '15 16:09 RichAyotte

Just trying to think of the best way to fix this. It is easy to add a way of skipping the fields that are missing, but I think there is some value in knowing when you've added validation to a field that is required and the field doesn't even exist in the DOM (a developer issue, but it will fail silently which isn't particularly nice).

Also simply skipping the missing fields and having one monolithic instance of dominar throughout the life of the app might get sluggish over time (I believe that's how @stocksp is using it?).

Would there be any considerations to automatically removing the field from dominar when it comes across a missing field in validateAll()? Kind of like a self cleanup option.

I also toyed with an idea of a missingField option which will determine what dominar should do once it stumbles across a missing field. This would only be used in validateAll() -- doing .getField('blahh missing field').validate() will always error regardless.

missingField: 'ignore'
missingField: 'cleanup'
missingField: 'error'

garygreen avatar Oct 01 '15 10:10 garygreen

I like the missingField option or maybe an environment option with some defined behaviours.

environment: 'dev' | 'test' | 'live'

dev would throw test would console.error() live could cleanup or ignore, whichever is safest.

RichAyotte avatar Oct 01 '15 11:10 RichAyotte

Not too keen on the environment option, I think that's probably a bit too complicated. If you needed specific settings for an environment make a wrapper in your app something like var dominar = new Dominar($('form'), buildDominarSettings('dev', { ... }));

I'm correct in thinking it's only .validateAll your having the problem with?

On 1 October 2015 at 12:42, Richard Ayotte [email protected] wrote:

I like the missingField option or maybe an environment option with some defined behaviours.

environment: 'dev' | 'test' | 'live'

dev would throw test would console.error() live could cleanup or ignore, whichever is safest.

— Reply to this email directly or view it on GitHub https://github.com/garygreen/dominar/issues/17#issuecomment-144704175.

garygreen avatar Oct 01 '15 11:10 garygreen

Yes, .validateAll is the culprit.

RichAyotte avatar Oct 01 '15 14:10 RichAyotte