How to set Ext.Form dirty state? - extjs

How to change the state dirty of a form to false?
Is there something like setDirty(false) method of Ext.Form.Panel?
UPD:
My task is to track Form dirty state for enabling docked SAVE button to become enabled only if there are any changes in the form.
I'm tracking dirtychange event with:
init: function() {
this.control({
'form': {
dirtychange: function(form) {
alert('change');
}
}
});
}
But when I'm loading a record through loadRecord() method into the Form I'm geting chage alert while loading record and Form becomes dirty after Record was loaded.
I'd like to reset dirty state of a Form right after Record loading and start tracking dirtychange event on record filled form.
Any ideas?

Correction
There is a property available which changes the original-value when a value get set.
trackResetOnLoad: true
See this JSFiddle
Old answer below might be quit inconvenient but I let it unchanged
First I have to say you will not find any such method
And that's because the form has not such a state, the fields have it. All what the form does is iterating over each field and check if it is dirty. If any field is dirty it will be checked if this value (the dirty mark) has changed since the last check and if so the dirtychange event is fired. Then this get saved as last check (wasDirty)
Now let's look at the fields cause they really trigger it:
They are checking onChange and onReset if the field is dirty by checking !me.isEqual(me.getValue(), me.originalValue) where originalValue is the value that was set the time initValue was called.
Now what's to do to 'set' the Form no longer 'dirty'
Iterate over all fields of the form by using getFields() on Ext.form.Basic now we will set the current value as original value (reset will now reset to this value) by calling initValue on each field and that's it.
wrong code removed
Edit
Well I've modified my last code from above to
var items = form.getForm().getFields().items,
i = 0,
len = items.length;
for(; i < len; i++) {
var c = items[i];
if(c.mixins && c.mixins.field && typeof c.mixins.field['initValue'] == 'function') {
c.mixins.field.initValue.apply(c);
c.wasDirty = false;
}
}
and forked a working example. The Button will stay inactive till any changes are detected and will again set inactive if you remove the changes. Play around with it I guess this is what you'Re looking for.

There is the method reset(). If used without an argument it retains the form data loaded with loadRecord().
So if you call reset() after loading the data, your form should not be dirty any more.
myForm.loadRecord(record);
myForm.reset();
I think, the dirtychange event will also be called by loading the data, but after the reset() you can check the form with isDirty().

A workaround here is to use a hidden field and set it's value to force the form dirty. We use this in widgets which do not contain form fields. To set the form dirty, we simply change the value of the hidden-field, to set it clean, we reset it to the original value.

Related

Angular - Prevent event looping?

I need your help as I am still having trouble with events handling.
Here is the concept :
To contextualize, let's say that I have visuals items, that I call "components".
A component is defined by 2 javascript objects. One is containing data (like position x, y, width, height, color, etc...). The other one is the view exploiting a 3rd party library named draw2d.
Draw2d uses it's own x y width height color etc... properties. Which mean that I need to "bind" values to always keep them equals.
For this purpose, I just use events. There are 'on' events in draw2d letting me watch theses modifications. For the angular side, I just use the ng-model directive directly on my data bindings. When a data is updated, I update draw2d view, and when the view is edited, I update my data.
This part is working correctly as this is not hard to handle it.
I then tried to implement multiple selection editing. For that, I store in an array every selected component. When a value from the view or from the data is edited, I trig an event which let every selected component to set the same data to the same value. Again, it's working great.
Where i'm starting to struggle hard, it's when I want to edit the position. I don't want every components to be on top of each other. For that, when I edit multiple x or y attributes, instead of positioning elements, I calculate the offset so that every components move with this offset only. Here is where it's not okay. Of course, when an offset is set, others components updates, also trigging their new value, so I'm stuck in a hell loop.
I would like to know if you know any pattern to handle events so that, when you have 2 view and a model, you can stop an event from propagate.
Here is my propagation snippet :
scope.canvas.on('figureEdited', function (canvas, event) {
if (scope.selectedComponents.length > 1) {
var key = event.key;
var value = event.value;
var originComponent = event.origin;
for (var i = 0; i < scope.selectedComponents.length; i++) {
var selectedComponent = scope.selectedComponents[i];
if (selectedComponent !== originComponent) {
if (key !== 'x' && key !== 'y') {
selectedComponent.setData(key, value);
} else {
var offset = value - event.oldValue;
selectedComponent.setData(key, selectedComponent.getData(key) + offset, true);
}
}
}
}
});
the issue is caused by
selectedComponent.setData(key, selectedComponent.getData(key) + offset, true);
It retriggers a figureEdited event, making every component move again by this offset, infinitely (stopped by 10 digest iterations).
I hope this is clear, sorry for the long text and thanks a lot for your help :)
Alann.
If the setData call triggers new events synchronously you can ignore events triggered while in your loop by setting an ignore flag and checking it at the start of the function.
var ignoreEvent = false;
scope.canvas.on('figureEdited', function (canvas, event) {
if (ignoreEvent) return;
ignoreEvent = true;
//...
ignoreEvent = false;
});

EXT JS Check any New record insert into Store or not

I have created a function to check whether the store record is changed or not, if record are dirty then when user pressed save button setLoading is true, is not dirty then do nothing.
function isDirtyStore(theStore) {
var isDirty = false;
theStore.each(function(item){
if(item.dirty == true){
isDirty = true;
}
});
if (!isDirty){
isDirty = (theStore.removed.length > 0);
}
return isDirty;
};
var BtnRouteFareSave = Ext.getCmp('BtnRouteFareSave');
var grid = Ext.getCmp('MyGridPanelRouteFare');
BtnRouteFareSave.on('click', function(){
if (isDirtyStore(RouteFareStore)){
grid.setLoading(true);
}
})
But dirty is only checking for Edited record only, how about new record are inserted into the store? how to check ?
Ext.data.Store object have getModifiedRecords() method which returns all records added or updated since the last commit.
So you do not need to implement your own function, just check if store.getModifiedRecords() returns non empty array.
If you want also check if any records were removed from store, you can add to check store.getRemovedRecords( ). This method returns any records that have been removed from the store but not yet destroyed on the proxy.
Is kinda weird but you could add a listener to your store to listen for adding new reccords and inside that listener set that specific reccord dirty in order to be able to detect it in your validation.
Best regards.
You could create an instance of the store with the original items in it, and compare it to a new instance, which has all of the items of the first one, plus the newly inserted one and compare both or use the add and datachanged events.
If the add event is fired, flag a boolean that'll indicate that your store has new data and thus has dirty new) records.

Reset Form Record Not Clearing Values - ExtJS 4.2

I have a Grid panel containing records which, on-click, will be loaded into a Form panel for editing.
On "close" of our form panel, we're calling myForm.getForm.reset(), which seems to reset the record but the values in the form fields themselves persist.
// Load record
me.down('form').loadRecord(record);
// Close
me.down('form').getForm().reset() or me.down('form').reset()
Please advise how to also clear values in the form upon resetting our record.
Do you have trackResetOnLoad set to true for the form? If so, what you really want is it set to false.
Maybe you need set 'resetRecord' parameter into 'reset()' method for unbind any record set by 'loadRecord' method.
Example:
me.down('form').getForm().reset(true)
You can override the default form panel to add this functionality. Add the following to your code:
Ext.override(Ext.form.Panel, {
clearForm:function(){
Ext.each(this.getForm().getFields().items, function(field){
field.setValue('');
});
}
});
You can then clear a form using:
myForm.clearForm()
Where myForm is your form panel.
The reset() method just resets the form back to the last record loaded.
If you want to maintain trackResetOnLoad=true (e.g. so you can use the form's "dirtychange" event) another approach is to take a copy of the values just after the form is created like var originalValues = myForm.getFieldValues(); then simply restore those values using myForm.setValues(originalValues); instead of calling myForm.reset(...);
You can try this...
this.up('form').getForm().reset();

AngularJS: Set the model to be = {} again doesn't clear out input type='url'

I have a fiddler setup, when i click a reset button it should clear out the input controls, this seems to work but not when the input type='url'
Here is the fiddler
Is there an issue or something that I am not understanding.
When I set
$scope.myform = {};
This seems to clear out the other input type but the input type='url' isn't being cleared.
Anyone know why?
The issue you see happens when you don't have a valid value inside the input[type="url"]. An invalid value just stays in the view (the input field) and doesn't get pushed to the scope variable inside ng-model. The variable will be updated only when and if the value is correct.
You can test it by entering a valid value. The reset button will work. If you enter an invalid value it won't.
You can fix it by setting $scope.myform = null instead of $scope.myform = {}. This will empty the field because the scope variable (expression) will be undefined. It will be automatically created by Angular once you enter a valid value inside any of the fields.
Because you need to put a valid url in the 2nd box like http://www.abc.com, then the reset button will work.
In order to correctly update the view/model, I would suggest that you explicitly reset the model's properties like so:
$scope.reset = function() {
$scope.myform = {
foo: '',
bar: ''
};
$scope.formName.$setPristine();
};
Setting 'myform' to an empty object deletes its fields, it doesn't set them to a blank string. It's quite likely angular's cleanup may not be deleting the value the view is referencing, thus the confusion between the application's model and view states.
Hope it helped.

Understanding Backbone Model set, validate and change callbacks

The Backbone documentation says:
Model.set will fail if validation fails - it won't set the value therefore it won't trigger any callback. We can pass { silent: true } to Model.set - then it will set the value but won't trigger any callback neither.
So,
Why does Backbone Model require a valid state to simply set an attribute value? What if we want to set attributes as the user interacts with the UI, but the model is not valid yet? It means change callbacks are unavailable unless we pass { silent: true } then manually trigger the change?!
Please say you know a better way of handling this :)
I'm not sure how to answer the Why questions but you could say that there are arguments for why it is good that set runs validations. For instance, it makes it dead simple to do client side validation in real time.
If your problem could be solved by only validating the value that is currently being changed by the user, you can do that by combining your validate method with the hasChanged method.
For example something like this:
Backbone.Model.extend({
defaults : { name : "" },
validate : function (attrs) {
var errors = {};
if(this.hasChanged("name") && attr.name.length == 0) {
errors.name = "Need a name yo!";
}
//...
if(_.keys(errors).length > 0) {
return errors;
}
}
})
In Backbone whenever you call set on model , It keeps track of what attributes of model has been changed and what attributes are newly added.Calling validate allows it be more efficient in doing it .Passing {silent:true} as options in set function causes validate and change to not execute so if doesnt fire any change events.
If you want to set attributes as the user interacts with the UI, but the model is not valid yet
In this case you can set the change in a plain object make sure object keys are sames as model's attribute and then at some point just set in your model.
var uiChanges = {name:'x'}; //just fill it with your changes
ur_model.set(uiModel); //then set it ,this way it fires change events only once
To check the diff between your plain object and model you can use the
ur_model.changedAttributes(uiChanges);
changedAttributes -
Return an object containing all the attributes that have changed, or false if there are no changed attributes.
You can further use it save only those attributes that have changed rather than saving entire model again.

Resources