I'm using typeahead through in my AngularJS project and I would like to have it select the entry if I type the full value and click out of the field.
I've put together an example of what I mean
http://plnkr.co/edit/NI4DZSXofZWdQvz0Y0z0?p=preview
<input class='typeahead' type="text" sf-typeahead options="exampleOptions" datasets="numbersDataset" ng-model="selectedNumber">
If I type in 'two' and click on 'two' from the drop down then I get the full object {id: 2, name: 'two'}. This is good, if however I type 'two' and click to the next field without selecting is there a way to accept the top of the list on loss of focus on a text field?
I'm not sure if I'd want to have that sort of functionality in my app. The user hasn't actually selected anything. So selecting something for them would introduce frustrations.
But I do understand that often odd requirements are needed. In this case, I'd attack it using ngBlur. Assign a function to be called on blur. You can grab the contents of ng-model and then loop through your data (assuming static & not being sent via server) to find a match.
You can most likely just look at the source code of your typeahead directive and strip out the part does the comparison and then choose the first item in the array.
Unfortunately the underlying component does not emit any events for this condition. This will make the solution more complex. However when the value is being entered and the Typehead magic has happened you can supplement those events and catch them to update your ngModel.
I have created a plnkr based on your plnkr and although have not cleaned up but it is a working plnkr doing by far what you need.
The gist of this is following code however you can put this code wherever best suited
The explanation below:
//Crux - this gets you the Typeahead object
var typeahead = element.data('ttTypeahead');
//This gets you the first
var datum = typeahead.dropdown.getDatumForTopSuggestion();
if (datum){
//you can do lot of things here however
//..I tried to - fill in the functionality best suited to be provided by Typeahead
//for your use case. In future if Typeahead gets this
//..feature you could remove this code
typeahead.eventBus.trigger("hasselections", datum.raw, datum.datasetName);
}
In the above code you can also save the datum somewhere in the scope for doing whatever you like with it later. This is essentially your object {num: 'Six'} Then you may also use ngBlur to set it somewhere (however the plnkr I created doe snot need these gimmicks.)
Then further down - ngModel's value is set as below
element.bind('typeahead:hasselections', function(object, suggestion, dataset) {
$timeout(function(){
ngModel.$setViewValue(suggestion);
}, 1);
//scope.$emit('typeahead:hasselections', suggestion, dataset);
});
I'm with EnigmaRM in that ngBlur seems to be the way to do what you want. However, I agree with the others that this could be somewhat strange for the end users. My implementation is below (and in plnkr). Note that I trigger on ngBlur, but only apply the model if and only if there is only one match from Bloodhound and the match is exact. I think this is probably the best of both worlds, and hope it should give you enough to go on.
$scope.validateValue = function() {
typedValue = $scope.selectedNumber;
if(typedValue.num !== undefined && typedValue.num !== null)
{
return;
}
numbers.get(typedValue, function(suggestions) {
if(suggestions.length == 1 && suggestions[0].num === typedValue) {
$scope.selectedNumber = suggestions[0];
}
});
};
Related
I am working on a project with a PHP backend and Angular 1.x on the front end. I have a Listings model and I use a common template to create (add) and update (edit) a Listing.
Since eidt and add both use the same front end template I need to be able to detect when a user is creating and when they are editing. It seems there are several ways to do this:
I could pass a paramater in the ng-submit:
ng-submit="saveListing({{isNewListing}}"
Then I could read the value of the paramter in my controller, but I think this is overkill?
When editing a Listing some variables set for the form auto-fill
$scope.listing.id = x;
Therfore I could just check for a value in the above:
$scope.saveListing = function() {
if(listing.id) {
// update action
} else {
// save action
}
};
Is the second option a sound and non-hacky approach. I am not an Angular pro so although it seems the logical approach to me I want to ensure that I am not hot woring this.
I usually do something similar to the second approach. Since editing means you have to "get" the original record in most cases, the record should exist somewhere in the scope. I use ui-router and have a resolve for the record, which means I can check right at the top of the controller:
$scope.isEdit = record != null;
With a scope variable or similar (e.g. controllerAs vm) you can leverage the fact that you're in "edit mode" and change the UI up a bit. Instead of "+ New" on a button you can have "+ Save".
Hope that helps!
We have a large ERP system with angularJs as a front-end framework, and we are using the "check id" approach.
When updating/edit an item there would be existing id for that item.
I think the second approach is good and I don't see any drawbacks.
We have a simple control (textarea), where we would like to detect user input of 'proper' urls and:
1. Visually convert these 'proper' urls to links and
2. Trigger an event we can hook on to detect a 'proper' link has been entered.
Of course the contents of textareas cannot be styled. As far as we see, though, TinyMCE (via AngularUI) might be the solution along with its 'autolink' plugin. However, other than whitelisting, it is not clear how 1. we can control/manage what a 'proper' link is (for example bit.ly or deliciou.us, etc are common domains now) or 2. how we can detect an event that a proper url has been entered other than using TinyMCE's 'onChange' and using our own custom regular expressions.
Although we can imagine solutions, this is a common requirement nowadays and we're having difficulty finding pre-built solutions to achieve this. BTW, we are using AngularJS.
Any help or pointers greatly appreciated!
Thanks,
Mo
We have a very similar situation where we have a textArea where the user can enter anything and any urls are supposed to be converted and displayed as clickable links. We solved it as follows.
Display the textarea value in a div. Prior to display we translate the value with a small function like this:
$scope.transformHrefsInAnswer = function(value) {
var retval = value;
if(retval != null) {
var urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
retval = value.replace(urlRegex, function (url) {
return '' + url + '';
});
}
return retval;
};
When the user clicks on the div we switch it to be a editable textarea with the actual value bound to the proper ng-model. They can then put in anything they want. When the area loses focus it switches back to a DIV and we call the transform function again so it displays as a valid, clickable link.
If all you want to do is validate you can use the transform function with minor mods to check for matches.
Hope this helps.
after some research and study of examples I implemented a pagniation with a filter function.
Im very new to angular, so I need your help if this application is ok or it has some bugs/logical errors.
The target is to select a collection (in this application load1 or load2) and create new objects, manipulate existing, or delete some of them. On every update of the data, it has to be checked if the pagination is synchronous to the collection size.
If the user enters something into the search field, a watcher in the controller is fired for updating the filtered data:
$scope.$watch('search.name', function (newVal, oldVal) {
$scope.filtered = filterFilter($scope.items, {name: newVal});
}, true);
I would be very happy if some of you angular pros can look into this code and give me some feedback. I want to use this in a productive system, so every answer would be great!
Here is a working plunkr: http://plnkr.co/edit/j9DVahEm7y1j5MfsRk1F?p=preview
Thank you!
Watchers are heavy if you use them explicitly throughout your large application.
Use ng-change instead. Also, by passing true to that watcher means you're deep watching which is really a bad thing to do, since it will check each property of the object in the array which is performance intensive.
Since I can't see that you need old and new value for a reason, you can simply use $scope.search.name. Whenever you type in something, $scope.search.name has the updated value. Just need to call a function on ng-change.
DEMO: http://plnkr.co/edit/TWjEoM3oPdfrHfcru7LH?p=preview
Remove watch and use:
$scope.updateSearch = function () {
$scope.filtered = filterFilter($scope.items, {name: $scope.search.name});
};
In HTML:
<label>Search:</label> <input type="text" ng-model="search.name" placeholder="Search" ng-change="updateSearch()" />
Previous answer is still the correct, but you will have to make sure to replace the "page" inside the pagination tag and change it to ng-model.
From the changelog (https://github.com/angular-ui/bootstrap/blob/master/CHANGELOG.md)
Since 0.11.0:
Both pagination and pager are now integrated with ngModelController.
page is replaced from ng-model.
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!
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