Hi,
I'm using https://github.com/webrouse/n-sandbox/ with the nittro builder. I would like to change some default package's behaviour time to time. I would like to change the form validation way at this moment for example... I would like to show errors beside all form inputs when the form is submitted. I'm just not sure, how can I do if or if is that actually possible at all.
Hi, of course you can do it :-) the core of Nittro is the DI container which wires up different parts of Nittro to do their magic. You can easily implement your own version of any service, or just extend it and overload some of its methods, and then override the appropriate service in the Container.
For example, form validation is triggered from the Nittro.Forms.Form
class' validate()
method. This method calls netteForms.js
's Nette.validateForm()
. The netteForms.js
validator calls the Nette.addError()
method when a field's validation fails. This method is overridden by Nittro's formLocator
service, implemented by the Nittro.Forms.Locator
class; the class forwards errors from the netteForms.js
implementation to the appropriate Nittro.Forms.Form
instance which takes care of displaying the error via an error renderer.
The Nette.validateForm()
method returns early, i.e. it returns false
as soon as it encounters the first invalid field (at least if I'm reading the code correctly). So I think that to solve the specific issue you're describing you'd need to fix the netteForms.js
side of things. But for the sake of the example, here's a couple of ways you can override / extend some of the functionality described above:
- It's Javascript, which means you can mostly do just whatever - including directly overriding a class's method. So if you want to implement your own custom validation, you can just do this:
_context.invoke(function(Form) { Form.prototype.validate = function() { // do whatever you want and return a boolean }; }, { Form: 'Nittro.Forms.Form' });
If you include a script like this in the
libraries.js
section of your Nittro builder config, you're done - all your forms will now use your own custom method to validate on submit. -
You can extend the
Form
class just as you would in PHP. In this case you'd also need to extend theLocator
class, because it's responsible for creating and managing instances of theForm
class and since it's written by an idiot, there's no easier way to tell it to use a differentForm
implementation. Then you'll need to tell the DI container about your newLocator
implementation.// assets/forms/myform.js _context.invoke('App.Forms', function() { var MyForm = _context.extend('Nittro.Forms.Form', function (form) { MyForm.Super.call(this, form); // call parent constructor }, { validate: function(sender) { // call the parent method if you need to: MyForm.Super.prototype.validate.call(this, sender); // or do whatever } }) _context.register(MyForm, 'MyForm'); });
// assets/forms/mylocator.js _context.invoke('App.Forms', function(MyForm) { var MyLocator = _context.extend('Nittro.Forms.Locator', function (formErrorRenderer) { MyLocator.Super.call(this, formErrorRenderer); }, { getForm: function(id) { var elem; if (typeof id !== 'string') { elem = id; if (!elem.getAttribute('id')) { elem.setAttribute('id', 'frm-anonymous' + (++anonId)); } id = elem.getAttribute('id'); } if (!(id in this._.registry)) { // this is the line we need all this for: this._.registry[id] = new MyForm(elem || id); this._.registry[id].setErrorRenderer(this._.errorRenderer); this.trigger('form-added', { form: this._.registry[id] }); } return this._.registry[id]; } }); _context.register(MyLocator, 'MyLocator'); });
And finally in the Nittro builder configuration you'd register your new
formLocator
implementation inservices
:const builder = new nittro.Builder({ // ... bootstrap: { services: { formLocator: 'App.Forms.MyLocator()' } } });
Sign in to post a reply