Backbone Validation with Backbone stickit - All attributes being validated when one is changed - backbone.js

I am attempting to use Backbone Validation with Backbone Stickit, I wish to validate one attribute at a time as the user enters them. However, when a user enters a value all attributes on the model get validated instead of just the one the user has changed. What am I doing wrong?
My View:
bindings:{
'#username' : {
observe:'username',
setOptions: {
validate:true
}
},
'#email' : {
observe:'email',
setOptions: {
validate:true
}
},
'#firstname' : {
observe:'firstName',
setOptions: {
validate:true
}
},
.......
onShow: function(){
Backbone.Validation.bind(this, {
valid: function(view, attr) {
alert('VALID - ' + attr);
},
invalid: function(view, attr, error) {
alert('INVALID - ' + attr);
}
});
this.stickit();
},

Everything you pass through setOptions is used when setting the value in the model (1). When you pass validate: true to the set function of a Backbone model it will validate the values in the model as well as the values passed to the set function (2) meaning it will validate the whole model every time you set a new value causing the problem you're seeing now. You're not doing anything wrong.
You could probably solve this by splitting up your validation into multiple separate functions and calling only the required ones on attribute change and then changing the validate function to call all those separate functions to validate the entire model.

This happened to me as well. In my case, I was setting default values in the model as '' (blank). Removed them and it worked

For this to work you should remove defaults (at least for the attributes your validating) values from your model

Try using the backbone.validation forceUpdate parameter on your backbone.stickit setOptions object in yout view bindings. That worked for me and I had a kind of similar problem.
Just like yousefcisco mentioned, backbone will validate all the attributes in the model on set or save, depending of your options passed, but in my case is not that I needed to validate each attribute separately, but the attributes didn't get set even if only one attribute was invalid, then I tried the forceUpdate: true, and it did its magic.
check it here: http://thedersen.com/projects/backbone-validation/#configuration/force-update

Related

Set the value of a different field when a field value changes

I'm using Angular Schema Form and I want to set a model property to null when a field value changes. I've tried using onChange in the form definition like so
{
key: '7_11',
type: 'radios',
titleMap: [{value: 'no', name: 'No'}, {value: 'yes', name: 'Yes'}],
onChange: function(modelValue,form) {
if (!modelValue) {
// model['8_1'] = null
}
}
}
Notice that the model property I'm trying to set is bound to a different field than the field that has changed
I can't do it in the manner indicated by the comment, because model is not in scope in the onChange listener.
Update
A second example is this Plunker demo. Say we want to clear the 2 checkboxes (by removing the corresponding properties from the model) whenever some text is entered in the Name field, how could this be achieved?
I should also point out that for reasons I won't bore you with, I can't implement this using a conditional and destroyStrategy.
Well, it is not uncommon (because it is kind of natural) to set the model and the form inside the scope of the same controller.
If that is the case, you can set X by $scope.model.X = modelValue.
Or perhaps you are in a position where you can't reach the scope of the model, in that case it is more complicated. On the other hand I would recommend to do it all in the same place.
(Sometimes that is not possible. For example, I have dynamic forms and schemas in my application, however in those cases I use generalized functionality like the filter functionality in ASFDS or ASF conditions and avoid hard-coding UI using onChange) .
For example, this would set foo2 if foo is changed in the plunkr:
onChange: function(modelValue,form) {
if (modelValue) {
$scope.model.foo2 = ["Yes"];
}
},

How can I get the view to update when I change the model in AngularJS?

I am asking this question after doing a lot research into similar problems where the view wasn't updating after a change to the model was made in an AngularJS app. The situation is somewhat complicated by the fact that I am using an external library angular-schema-form.
I have an object-valued variable attached to the scope called ediTradingPartner.outbound_uom_map that angular-schema-form uses to generate a form element. An example is as follows:
$scope.ediTradingPartner.outbound_uom_map = {
CHARGE: "EA",
DAY: "DA",
KIT: "KT",
MONTH: "MO",
SET: "ST"
}
When the form is loaded, each of the key-value pairs above generates an input field whose label is set to the key and whose text-value is set to the value. In addition, I add an input field and a button that allows the user to add a new key-value pair to the object. The code that does this is below:
$scope.setupUomMapping = function() {
var addNewUOMButton = $('<button>', { text: "+", 'ng-click': "addNewUOM(new_uom_key)"}),
newUomKey = $('<input>', { type: "text", 'ng-model': "new_uom_key", placeholder: "New key..."}).css('margin-right', '5px');
var $el = $('<div>', { id: "addNewUOM" })
.append(newUomKey)
.append(addNewUOMButton)
.insertAfter($('legend:contains("outbound_uom_map")'));
$compile($el)($scope);
}
When the user types something into the input field and presses the button, the method addNewUOM is called, with the new_uom_key being passed in as an argument. addNewUOM is defined as below:
$scope.addNewUOM = function(new_uom_key) {
if (!$scope.ediTradingPartnerModel.hasOwnProperty('outbound_uom_map')) {
$scope.ediTradingPartnerModel.outbound_uom_map = {};
}
$scope.ediTradingPartnerModel.outbound_uom_map[new_uom_key] = "";
console.log($scope.ediTradingPartnerModel.outbound_uom_map);
}
What I want to happen is the addition of the new key new_uom_key to the scope variable $scope.ediTradingPartnerModel.outbound_uom_map to result in the view having another input field with the label being the new_uom_key. As evidence of the model having changed, if I enter "YEAR" into the input field and press the add button, and then log the $scope.ediTradingPartnerModel.outbound_uom_map variable, I get the following value:
{
CHARGE: "EA",
DAY: "DA",
KIT: "KT",
MONTH: "MO",
SET: "ST",
YEAR: ""
}
The problem, of course, is that the view does not change in response to the changed model.
Some points:
The code was working before and it suddenly stopped working at some point when I hadn't made any changes to either of these functions. (I obviously made other changes elsewhere that broke this functionality).
I've tried using $scope.$apply() and $scope.$evalAsync() but to no avail. The former resulted in an error saying that $scope.$apply() was already running, and the second didn't really seem to make any difference (though there was no error).
I know that dynamically inserting elements as I'm doing in the setupUomMapping function is not the "Angular way" but I was forced to do this because the form within which all of this stuff is contained is generated by a third party library, something over which I have limited control. So I'd appreciate if the answers focused on how this particular problem can be solved.
Thank you for your time and help.
Is there an html object to which $scope.ediTradingPartnerModel.outbound_uom_map is bound? If so it should automatically update.
Otherwise, is the $scope.setupUomMapping function responsible for building the view? If so, then what is causing it to fire? If you only build the view on the load of the DOM then there's no reason the view would update. You need to call this function each time the view needs to update.
My suggestion: get rid of the procedural building of a view and create html templates that utilize ng-repeat.

Watch form model for changes

Assuming a given form such as <form name="myForm">, it's easy enough to watch for validity, error, dirty state, etc. using a simple watch:
$scope.$watch('myForm.$valid', function() {
console.log('form is valid? ', $scope.myForm.$valid);
});
However, there doesn't appear to be an easy way to watch if any given input in this form has changed. Deep watching like so, does not work:
$scope.$watch('myForm', function() {
console.log('an input has changed'); //this will never fire
}, true);
$watchCollection only goes one level deep, which means I would have to create a new watch for every input. Not ideal.
What is an elegant way to watch a form for changes on any input without having to resort to multiple watches, or placing ng-change on each input?
Concerning the possible duplicate and your comment:
The directive solution in that question works, but it's not what I had in mind (i.e. not elegant, since it requires blur in order to work).
It works if you add true as third parameter for your $watch:
$scope.$watch('myFormdata', function() {
console.log('form model has been changed');
}, true);
Further information see the docs.
Working Fiddle (check console log)
Another more angular way would be to use angular's $pristine. This boolean property will be set to false once you manipulate the form model:
Fiddle
Based on my experience with my forms (new dev, but working with Angular for a while now), the elegant way to watch a form for changes is actually not to use any type of watch statement at all actually.
Use the built-in Angular boolean $pristine or $dirty and those values will change automatically on any input field or checkbox.
The catch is: it will not change the value if you add or splice from an array which had me stumped for a while.
The best fix for me was to manually do $scope.MyForm.$setDirty(); whenever I was adding or removing from my different arrays.
Worked like a charm!

angularjs - streamline form (automatic) submission based on dirty scope

Problem space
I have a problem where I'm submitting a form based on criteria being fulfilled, rather than having a form submission button.
Let's say I have 3 drop downs, the first two are grouped but one needs to be selected, meaning I can select one or the other but I can't leave them empty, the 3rd one is a required field.
After that, the page automatically fetches in results.
Lets say I have checkboxes and a few more dropdowns. Any future selections on the 3 dropdowns mentioned, checkboxes, and dropdowns automatically filters the results.
What I know
Now after reading angular documentation, I was checking up on $dirty, $pristine and operations on both, like $setDirty and $setPristine; however, it seems that this is for a FormController
So I'm assuming this is useful for an entire scope. I didn't see any inclination that I can figure out for selected scopes.
What I have so far
So basically, I was hoping that I'd be making use of the scope's tracking features, but I don't know much about it. I created a single controller for my application and a single scope, since that's what seemed easiest for me. I have 3rd party plugins that play a role into the scope like:
$scope.3rdpartyConfig = {
prop1: [],
prop2: getData()
}
I don't think something like that would be useful in checking to see form submission if I was going to check the $dirty state of my form.
Then I thought about the old way I used to do things, but "angularlizing" it:
so I'd have something like:
<input type="checkbox" ng-model="state.Checked" ng-change="checkIfWeCanSubmitThenSubmit()" id="ng-change-example1" />
So I'd be having ng-changes and ng-clicks all over my html form, hitting that function, where the function would look like this pseudocode:
$scope.checkIfWeCanSubmitThenSubmit= function() {
var validated = false;
//check to see if dropdown1 or dropdown2 are selected
//check to see if dropdown3 is selected
// add more here per requirement
//if the above are true, then validated = true
if (validated)
{
//add dropdown4 and 5, and checkbox groups into filter
}
submit();
}
But I was thinking this isn't the angular way of doing things since this certainly isn't facilitated.
I was hoping that the scope would offer some kind of way, where I can check to see what pieces of my scope is dirty or not before I can submit and fetch data, or if there is a better way than appending this function to every html element; like having some kind of scope tracker that I can check up on and watch.
Which reminds me, I don't want to have a series of $scope.$watch either, its just that it'd be way too much work to bind to every piece of html code, unless there's way to watch the scope of a collection of specific scope variables, then, I wouldn't mind.
like (forgive the pseudocode):
$scope.$watch('dropdown1, dropdown2, dropdown4', function(dirty, pristine)
{
if (dirty)
{ blah blah blah }
});
Edit (2/28/2013):
I tried doing it this way:
$scope.masterCriteria =
[
{ DropDown1: $scope.AppModel.Dropdown1},
{ DropDown2: $scope.AppModel.Dropdown2 },
{ DropDown3: $scope.AppModel.Dropdown3 },
{ Checkbox1: $scope.AppModel.Checkbox1 },
{ Checkbox2: $scope.AppModel.Checkbox2 }
];
$scope.$watch('masterCriteria', function (newVal) {
if (newVal) { logger.info("did I change?"); }
}, true);
The watcher detected nothing, and any values I changed to the scope of AppModel wasn't being picked up in the $watch. Was worth a try, still trying to figure this out.
You can slightly change your model and group fields related to input form together. Put them into single object. Like this:
$scope.state = { checkbox1: false, checkbox2: true, ... }
Later bind input boxes to field of state object:
<input ng-model="state.checkbox1" ... >
And watch state object to catch all updates of nested fields:
$scope.$watch('state', ...
JsFiddle example here

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