Typehead displays wrong on certain input - angularjs

I am using a uiTypehead for input selection.
It works well except for a simple fact. My inputs always have the form like
{name: "Las Vegas McCarran International (LAS)<span
class="country">United States</span>", iata: "LAS"}
So I insert <span class="country">...</span> from my backend into the string.
When I then type in "veg" into the input field, that's all fine.
However, when typing in "las" instead, following happens:
What basically happens is, that my system assumes "class" to be part of my desired string does the following in the view:
<span c<strong="">lass="country">United States</span>
Basically, it recognizes "las" as the string and puts a strong into that part so that it is not displayed correctly.
My view, service and my controller look as follows:
Service:
angular.module('tripdeltaApp').service('airportService',['$http',function($http){
this.airport = function (entry) {
return $http({
method: 'POST',
url: '/airport',
data: {'search': entry}
}).then(function (response) {
return response.data.map(function (item) {
//console.log(item);
return item;
});
});
};
}]);
Controller:
$scope.onSelect = function ($item, $model, $label,to,index) {
$model = urlParser.cleanAirport($model);
if(to && $scope.flightArray[index+1] && !$scope.flightArray[index+1].from) {
$scope.flightArray[index+1].from = $model;
}
};
and view:
<input type="text"
ng-focus="inputFocus=false"
ng-blur="inputFocus=true"
ng-model="flight.to"
placeholder="{{'TO'|translate}}"
name="arrivalAirport"
typeahead="data as data.name for data in getAirport($viewValue)"
class="form-control"
typeahead-editable="false"
typeahead-on-select="onSelect($item, $model, $label,1,$index)"
autocomplete="off"
select-on-click
required>
Any idea how to solve that?

I think your best bet is to use a Custom Template, as described in the Typeahead example with the flags, instead of putting markup into your data.
You'd change your data to be like this:
{name: "Las Vegas McCarran International (LAS)", country: "United States", iata: "LAS"}
and use a template something like this:
<script type="text/ng-template" id="customTemplate.html">
<div>
{{match.model.name}}
<span class="country">{{match.model.country}}</span>
</div>
</script>
Update
If you can't change the data on the backend, you could preprocess your data as it gets loaded into JavaScript:
var re = new RegExp('^([^<]+)<span class="country">([^<]+)<\/span>');
angular.module('tripdeltaApp').service('airportService',['$http',function($http){
this.airport = function (entry) {
return $http({
method: 'POST',
url: '/airport',
data: {'search': entry}
}).then(function (response) {
return response.data.map(function (item) {
//console.log(item);
var matches = item.name.match(re);
item.name = matches[1];
item.country = matches[2];
return item;
});
});
};
}]);
The regular expression may need some tweaking, based on the specific data you have. If any airport "name" fields have a "<", that would obviously cause trouble. You may also need to mess with spacing, location of quotes, etc.

Related

Is this the correct way of binding factory to controller?

I have the following code in my module:
.controller('ModalInstanceCtrl', function($rootScope, $scope, emailService) {
$scope.emailService = emailService; // Good or not; if not, why?
$scope.showed = false;
$rootScope.$watch('showed', function () { $scope.showed = $rootScope.showed; }); // In case you wonder why I did this - I'm using this trick to prevent watch from firing twice, because that would happen if I remove the watch below and put its code here.
$scope.$watch('showed', function () {
if (!$rootScope.showed) return;
$scope.selected = 0;
$scope.primary = true;
$scope.verified = true;
if (emailService.emails.length == 0) emailService.load();
});
$scope.EmailSelected = function () {
emailService.setCurrent($scope.selected);
$scope.primary = emailService.emails[$scope.selected].primary;
$scope.verified = emailService.emails[$scope.selected].verified;
};
});
.factory('emailService', function($resource, $http) {
var emails = []; // [{email: 'sample#email.dom', verified: true, primary: false}, ...]
var selected = 0;
function sendreq(action, email){
$http({
method: 'POST',
url: '/email/',
data: "action_" + action + "=&email=" + email,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(response) {
console.log(response.data);
return true;
}, function(data){
return data;
});
}
return {
emails: emails,
selected: selected,
setCurrent: function(curr){
selected = curr;
},
load: function(){
$resource('/api/email/?format=json').query({},
function success(result) {
emails.push.apply(emails, result);
});
},
add: function(email) {
for (var e in emails) if (emails[e].email == email) return false;
return sendreq('add', email);
},
remove: function() {
sendreq('remove', emails[selected].email);
}
}
})
And this code in my HTML template:
<div ng-repeat="e in emailService.emails">
<input type="radio" ng-model="$parent.selected" ng-value="$index" ng-change="EmailSelected()" id="email_{{ $index }}" name="email">
<label for="email_{{ $index }}" ng-bind='e.email'></label> <span ng-show="e.verified">Verified</span> <span ng-show="e.primary">Primary</span>
</div>
<div><button ng-disabled="primary" ng-click="emailService.remove()">Remove</button></div>
<form novalidate>
<input class="form-control" type="email" name="email" ng-model="email" placeholder="Email">
<input type="submit" ng-disabled="email === undefined" ng-click="emailService.add(email)" value="Add Email Address">
</form>
And I want to ask, whether I've correctly assembled the module and template because I'm working with AngularJS for the first time. Specifically, I want to ask if it's correct to bind the whole factory to the scope? Also if someone has more time he can look at the other code to see if everything is right or not. Feel free to write any suggestions about my code.
Thanks in advance!
It always depends on particular case.
This way boilerplate wrapper methods
$scope.add = (...args) => emailService.add(...args);
can be omitted, as well as their tests in controller spec.
Another benefit is that it provides existing object for proper data binding and scope inheritance of scalar scope properties:
<parent-scope>
<p ng-init="emailService.selected = 0"></p>
<child-scope>
<p ng-init="emailService.selected = 1"></p>
{{ emailService.selected === $parent.emailService.selected }}
</child-scope>
</parent-scope>
This certainly would not work as expected if there's no emailService object. This is particularly useful when controllerAs syntax isn't used.
There's nothing wrong with exposing a service to scope - if its API matches the controller scope. And this may indicate an antipattern if it doesn't - or if there are too many services that are abused like that.
Why do you want to bind the entire service ? I do not see a need to that in your code. You are invoking parts of the service using the service handler, there is no specific need to put the entire service on scope.

How to do global search in Angularjs from json array?

Iam able to display and filter search results in the same view..But how can i create search box in header navbar which is common to all pages. On click of search button should open the separate page and should load the search results alphabetically from json array based on the value entered in search box. How to do this? Kindly help
This is one way of attacking your problem. If i understood your problem correctly you wanted to filter out a JSON array...
So lets say in mainCtrl we have this:
$http({
method: 'GET',
url: '/Users'
}).then(function successCallback(response) {
$scope.usersJson = response;
}, function errorCallback(response) {
console.error("Something Happened...");
});
So lets say that data above gives us a sample of Users that look like this:
"Username" : {
"name" : "Username",
"value" : "Hello World"
}
Then in our html if we want to go through the data:
<body ng-app="app" ng-controller="mainCtrl">
<input type="text" ng-model="search" />
<p ng-repeat="message in messages | filter: search">
Name: {{ message.name }}
Message : {{message.value}}
</p>
</body>
You have to learn more about the ngFilter Directive and Filtering in AngularJS In general.. Here is the docs about it.. Hope this helped!
EDIT
If you want to do this in multiple controllers, just make a service to serve as a communication beetween controllers...
app.service("jsonArray", function($scope){
this.getArray = function(){
$http({
method: 'GET',
url: '/Users'
}).then(function successCallback(response) {
return response;
}, function errorCallback(response) {
console.error("Something Happened...");
});
}
})
Now you can inject this service to a controller and simply do jsonArray.get() to get the JSON List..
How to inject service into controller?
Sample:
app.controller('mainCtrl', ["$scope", "jsonArray", function($scope, jsonArray){
}])
Hope this helped..

Want to get the array of form element's value in AngularJS

HTML Form code
<form ng-submit="mysubmit()" ng-controller="question_post" >
<textarea name="question" id="question" ng-model="question"></texarea>
<input type="text" name="opt[]" ng-model="opt[]" />
<input type="text" name="opt[]" ng-model="opt[]" />
</form>
Angular JS Code
var obj = angular.module('root', []);
obj.controller('question_post', ['$scope','$http', function ($scope,$http) {
$scope.mysubmit = function(){
$http({
url: "some_url",
method: "POST",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data:$.param({user_id:1,'question':$scope.question,'option':$scope.opt})
}).success(function(data, status, headers, config) {
console.log(data);
alert(data);
$scope.data = data;
}).error(function(data, status, headers, config) {
$scope.status = status;
});
}
}]);
The code above returns error when I write ng-model="opt[]", and ng-model="opt" returns a value.
I'm trying to add the dynamic text fields with name opt, and for this I want to get the opt value in an array.
If I'm using PHP then its okay ($option =$_POST['opt']) but how can I get the array value of opt in AngularJS.
You could define the options within your controller, pass it along to the view and use ng-repeat to iterate over your options. This way you could use $index and save your options into an array. Here is a Plunker as an example.
So basically you iterate over your options, as mentioned:
<div ng-repeat="option in vm.options">
<input type="text" name="{{ option.name }}" ng-model="vm.opt[$index]" />
As you can see I used ng-model like this:
ng-model="vm.opt[$index]"
This will save the value of the input field in an array at the position of $index, which is much like the iterator in a for-loop and starts with 0.

UnderscoreJS to find results in an Angular controller

I have a controller setup with an input that takes a Purchase Order number. At the moment to get results I am setting data[0] and always returns the first result. I want to set up a validation where the the number entered will return the correct result. Before I can do that though I need to iterate through the results to just return the data I need rather than hard-coding it to the first result. I am using underscore and have set up the following but not retrieving the result I want. Please could someone just advice how I use underscore to do this?
Underscore that did not work:
var test = _.map(data, function(data) {
return {
purchaseOrderNumber: data.purchaseOrderNumber
};
});
console.log(test);
Console output:
[Object, Object]
0: Object
1: Object
length: 2
__proto__: Array[0]
The html:
<form novalidate name="goodsIssueForm" class="form-group cnc-inline-field">
<input type="number" class="form-control" name="purchaseOrderInput" ng-model="purchaseOrderInput" ng-change="resetGoodsIssueValidity()" required></input>
<button data-auto="rpoBtn" ng-click="getPurchaseOrder()" class="btn btn-primary">Find</button>
</form>
The controller:
$scope.getPurchaseOrder = function() {
var purchaseOrderNumber = $scope.purchaseOrderInput;
GoodsIssueService.getPurchaseOrders(purchaseOrderNumber)
.then(function(data) {
if ($scope.goodsIssueForm.$valid) {
$scope.goodsIssueDocument = data;
$scope.goodsIssueDocument.purchaseOrderNumber = data[0].purchaseOrderNumber;
}
})
.catch(function() {
$scope.goodsIssueForm.$setValidity('notfound', false);
});
};

ngResource GET with filter values

I'm writing a small test application where I can retrieve my customers from a parse.com database.
I have the following form in html
...
<body ng-app="myApp">
<div ng-controller="CustomerController">
<button ng-click="getCustomers()">Get customers</button>
<ul>
<li ng-repeat="customer in customers">{{ customer.name }}</li>
</ul>
</div>
</body>
...
My angular app is the following:
Module
var app = angular
.module('myApp', ['ngResource'])
.constant('myConfig', {
'api_url': 'https://api.parse.com/1/classes/',
'parse_application_id': 'xxxxxxxxxxxxx',
'parse_rest_api_key': 'xxxxxxxxxxxxx'
});
Factory
app.factory('CustomersService', function($resource, myConfig) {
return $resource(myConfig.api_url + 'Customer', {}, {
query: {
method: 'GET',
isArray: false,
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
},
create: {
method: 'POST',
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
}
})
});
Controller:
app.controller('CustomerController', function($scope, CustomersService, CustomerService) {
$scope.getCustomers = function() {
CustomersService.query().$promise.then(function(result) {
$scope.customers = result.results;
});
};
});
So when I click my button, everything works like it should.
But I also want to add a filter by name when I want to retrieve customers from the database.
When I execute the following in Postman
https://api.parse.com/1/classes/Customer?where={"name":"aaaaa"}
this works and only gets the customer with the name "aaaaa". So I know that the syntax is OK.
So I will add a textbox where the user can enter a customername and after that I want to click on the search button.
But how can I manage the ?where={"name":"aaaaa"} into the angular stuff when I click the button? I also want to expand the filter with other columns from that customer.
Something like this should work (assuming everything goes in the where object)
Add some search fields that bind to a scoped object's properties. We'll call it search
<label for="search_name">Name</label>
<input type="text" ng-model="search.name" name="name" id="search_name">
<label for="search_city">City</label>
<input type="text" ng-model="search.city" name="city" id="search_city">
Then you can execute the query action with
CustomersService.query({where: $scope.search}).$promise...
That should create a query param like
?where=%7B%22name%22%3A%22aaaaa%22%2C%22city%22%3A%22London%22%7D
which is the URI encoded value
?where={"name":"aaaaa","city":"London"}

Resources