How to watch on string changed - backspace added, in angular? - javascript

I'm looking workaround to catch user presses backspace (aka keytype 32) on Android. It works well in chrome but not on devices.
The one of my tries is to use simple watch.
However the $watch doesn't catch backspace pressed.
Here is what I did so far:
<div class='form-group'>
<label>Field 1</label>
<input type='text' ng-model='f1' required class="form-control">
</div>
<div ng-repeat="line in lines">
{{line}}
</div>
and:
$scope.lines = [];
$scope.$watch(function () {
return $scope.f1;
},
function (newValue, oldValue) {
$scope.lines.push(newValue);
}, true);
This is a demo in Plunker
[EDIT]
I tried ng-change - get the same result. When I press backspace nothing happens
Demo with ng-change Plunker
Please help,

It helps to check your console for errors. Basically the problem is not with backspace, but with the ng-repeat trying to have duplicate entries. So when you hit backspace, the array is trying to have the exact same item pushed.
Instead, use objects with the desired text:
$scope.lines.push({ text: newValue });
Also, use ng-change instead of $watch. It is there for this exact use case.
Here is a working plunk: http://plnkr.co/edit/pMyvHXDEaCSmvyM8N5GH?p=preview
It looks like fessy actually wants to preserve trailing spaces that are trimmed by default in angular. In that case, look at ng-trim: https://docs.angularjs.org/api/ng/input/input%5Btext%5D
Basically setting ng-trim="false" inside the input declaration will disable auto trimming.

As #Matt has pointed out, this is because of dupliates in the array. Angular repeat tracks ng-repeat by an unique id. If not specified this will be taken as the repeated value as in this case line and if duplicates are there this value will be rejected.
But ng-repeat provides an option for duplicates, you can optionally provide track by some unique parameter. For your case, just replace the ng-repeat with
ng-repeat="line in lines track by $index"
Let me know if this works.
I guess the above logic works fine. PLUNKER

Related

Angularjs minlength validation stop characters counter

I'm facing a weird "bug". I have a form with a textarea with a minlength and a maxlength validation on it.
Also, there is a super simple character left counter:
<textarea ng-trim="false" ng-model="form.text" minlength="20" maxlength="240"></textarea>
<p>{{240 - form.text.length}} left</p>
I don't know why my counter start working only after the characters reach the minlength value... While typing the counter stuck to 240. After I reach 20 characters the counter jump to 220... What is going wrong here?
Pen example
The basic validation directives such as ng-minlength will block ngModel from being updated until validation is passed. It's not a bug as such, and it's useful in many cases, but it can also be less than useful in some cases. Such as this.
The way around this while still keeping the benefits of using ngModel based validation (including form validity etc) is to set the validation step 'manually' by using $setValidity. To do this you would remove the ng-minlength attribute and use an ng-change callback.
You will also need to make sure that your form element is named and part of a named form, in order to get access to the relevant ngModelController.
<form name="formCtrl">
<textarea ng-trim="false"
ng-model="form.text"
ng-change="checkTextLength()"
name="formText">
</textarea>
<p>{{240 - form.text.length}} left</p>
</form>
And then in your controller, assuming you are just using $scope here:
$scope.checkTextLength = function(){
if($scope.form.text.length < 20){
$scope.formCtrl.formText.$setValidity('minlength', false);
} else{
$scope.formCtrl.formText.$setValidity('minlength', true);
}
if($scope.form.text.length > 240){
$scope.formCtrl.formText.$setValidity('maxlength', false);
} else{
$scope.formCtrl.formText.$setValidity('maxlength', true);
}
}
I forked your codepen to create a working example here. There I also added a bit to the template to show the validity state.
This is probably because angular isn't saving the the internal state of the textarea to its form.text variable until you hit the minlength. It should be very easy to validate this by changing that <p> tag's inner text from form.text.length to form.text.
If at all possible, I would remove the minlength attribute from your textarea and add that as a validation step later on -- Definitely inform the user somehow with a label on-screen though by keeping track of it.

matching values of two textboxes

I've two text boxes with different ng-models. They fill up using a $http.get request on click of a button.
Lets say:
<input type="text" ng-model="name.title" />
<input type="text" ng-model="name.surname" />
These get filled up just fine from my JSON data.
The value of my name.title can sometimes be like "abc (123)". If a user edits this, I want the name.surname to become the part inside the (). If the user removes "abc (123)" and just types in 123, then 123 should reflect in name.surname.
I've tried various combinations using ng-blur and ng-change but nothing so far has worked for me.
Its just one two textboxes so no point writing a new directive.
How do I do this?
Any help will be appreciated. :)
I asume that your using angular 1.x.
You could have a watch over name.
something like in you
$scope.$watch('[name.title, name.surname]', function(newValue, prevValue) {
if (newValue === prevValue) {
return;
}
//Here you put your logic and update the models.
});

clear selected checkbox scope value

My HTML:
<div class="check" ng-repeat="point in dbs">
<input
name="db"
id="{{point.id}}"
ng-model="point.select"
ng-click="update($index, dbs)"
ng-checked="false"
type="checkbox"
ng-required="point.select" />
</div>
Whilst my update() function looks like:
$scope.update = function(position, dbs) {
angular.forEach(dbs, function(point, index) {
if (position != index)
point.select = false;
});
}
This works as with regards to tracking what the selected checkbox is, and sending into another controller that expects the value, all is working good.
However, when I go back from the resulting page, back to this search form again, somehow the checkbox I selected before, is preselected, and I don't want anything to appear, rather just have everything blank.
Would it be as easy as simply stating:
$scope.point.select = null;
as I can't seem to find a good solution for this, so that the checkbox is always blank / not pre selected when you arrive on this form.
Let me see if I get what you are doing. It looks like you are trying to make your list of checkboxes mutually exclusive. I might look at using a radio button list (a set of radio buttons with the same name attribute, HTML interprets this as a mutually exclusive list). If you create a variable which will hold the value of the selected option and pass that value around, you probably can achieve the same result with less code. Take a look here: https://jsbin.com/sunusihuse/edit?html,js,output
As for clearing the list of controls when you revisit the page, what I have described will do that too because the value of the variable which will hold the selected value is initialized to an empty string. Hope this helps.

angular-ui bootstrap typeahead escape key causing input field value become undefined

I'm having trouble solving this which seems to be an edge case.
When I select(using keyboard or mouse) a value from typeahead's dropdown, the ng-model in the input field gets populated just fine.
However, if I type a few letters then hit 'escape' button, the 'textFieldValue' ng-model seems to be overwritten with 'undefined' by typeahead because I didn't choose a value.
What's the best way to solve this problem? Or is it too much an edge case?
<input type="text" ng-model="textFieldValue"
typeahead="list for list in getList($viewValue)" typeahead-editable="false">
You can do it by using a temporarily value.
Assign the temp value to typeahead
<input type="text" ng-model="tempValue" typeahead="list for list in getList($viewValue) | filter:$viewValue" typeahead-editable="false" >
Watch on tempValue and decide whether to update the target value or not.
$scope.$watch('tempValue', function(tempValue){
if(shouldUpdateTheTargetValue(tempValue)) {
$scope.targetValue = tempValue;
}
});
I created a plunker for that. - http://plnkr.co/edit/r63027iRobX4skV9YK60?p=preview

Remove blank option ng-repeat and ng-options after filter

There are several questions very similar to this one yet I have been unable to come up with a solution.
I have a select list using angularJS. I need to use the title attribute so I have an ng-repeat to create the options, there is always a blank option even if I use ng-selected to always select the first option.
Even if I make a selection and the blank option goes away, if I then filter out that selected value the blank will reappear.
I have included a select list using ng-option (which does not include my needed tittle attribute) and a default value to show that the blank will appear after filter.
The behavior I desire would be to never have a blank option (always selecting first option would be fine) and to possibly have a directive per option for special handling of click events.
Thanks in Advance!
JS Fiddle: http://jsfiddle.net/32DFM/3/
<select size="3" ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm"
ng-selected="$first"
value="{{p}}"
title="{{p.name}}">
{{p.name}}
</option>
</select>
I forked your fiddle (if I may be so blunt): http://jsfiddle.net/XsFe8/2/
This fixes it somewhat. Although I haven't gotten it to work properly together with the filter.
Anyway, what I do here, is to use the person.id as the value on each option.
<select ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm" ng-selected="$first" value="{{p.id}}" title="{{p.name}}">
{{p.name}}
</option>
</select>
And set the initial calue on the person.current model:
$scope.person.current = $scope.people[1].id;
But it's still not 100% though. I'm a bit stumped to why the blank spaces appear when you filter the select....
An alternative that might or might not work, would be to use something like ng-repeat="p in filterPeople() and filter your array in a filterPeople function. But I'm not sure if this will change anything.
UPDATE: I tested out my suggestion above, here: http://jsfiddle.net/XsFe8/2/
If you set the selected object to be the first object in the filtered array, it works. I do this each time a new filtered array is created:
$scope.filterPeople = function () {
var array = filterFilter($scope.people, $scope.person.SearchTerm);
$scope.person.current = array[0].id;
return array;
};
It looks like things get hairy when another object than what is visible in the select is actually selected. This is kind of understandable :)
Your actual problem is the value in ngModel is referencing a value which doesn't exist in the select anymore.
A solution is to whenever you alter the select options, you also check the person.current to ensure that it points to a valid entry.
This also implies that you might want to move your filter into the controller, and set the options in the scope (you can use the $filter service in your code to get same behaviour there, https://docs.angularjs.org/api/ng/filter/filter). This way you can have a function in your controller checking if person.current is valid, and if not, set it to desired options (e.g. the first one).
the hairyness cited above is due to an empty array when all items are filtered out and is fixed by:
if(array.length>0)
$scope.person.current = array[0].id;
http://jsfiddle.net/b0z6vpr8/
Hope this helps

Categories