UnderscoreJS to find results in an Angular controller - angularjs

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);
});
};

Related

Angular 1 dynamic form object

I'm using Angular 1 and creating a dynamic form. It works by looping through some objects and rendering dynamically binded input fields like:
<div class="quest-form form-group" ng-repeat="task in tasks">
<div ng-if="task.Class == 'TaskText'" ng-class="'task ' + task.Class">
<input ng-model="questForm.Task[task.ID].Value" ng-name="task_{{task.ID}}" ng-required="task.Required == 1" type="text" class="form-control" placeholder="{{task.Title}}" />
</div>
...
...
</div>
I also have a upload field in the loop:
<div ng-if="task.Class == 'TaskUpload'" ng-class="'task ' + task.Class">
<input class="btn btn-primary upload-btn" ngf-max-size="10MB" type="file" ng-model="upload" ngf-multiple="false" ngf-select="uploadFile(upload, task.ID, $invalidFiles)" />
<input class="" ng-model="questForm.Task[task.ID].FileUploadID" ng-required="task.Required == 1" ng-name="task_{{task.ID}}" type="text" />
</div>
When the file uploaded event is called I'm trying to set the value of the hidden field which is ng-model="questForm.Task[task.ID].FileUploadID" like this:
$scope.uploadFile = function(file,taskID) {
file.upload = Upload.upload({
url: assetsURL+'/home/UploadFile',
data: {file: file}
});
file.upload.then(function (response) {
$scope.questForm.Task[taskID].FileUploadID = response.data; // THIS MESSES UP
}, function (response) {
...
});
};
I get the following error, it's like $scope.questForm.Task[128] does not exist even though the hidden input looks correct and is binded to the $scope.questForm.Task[128].
angular.js:14362 TypeError: Cannot read property '128' of undefined
at file.upload.then.$scope.errorMsg (http://localhost/carl-mygps-app/js/controllers/quest-details-controller.js:120:26)
at processQueue (http://localhost/carl-mygps-app/bower_components/angular/angular.js:16689:37)
at http://localhost/carl-mygps-app/bower_components/angular/angular.js:16733:27
at Scope.$eval (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18017:28)
at Scope.$digest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:17827:31)
at Scope.$apply (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18125:24)
at done (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12233:47)
at completeRequest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12459:7)
at XMLHttpRequest.requestLoaded (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12387:9) Possibly unhandled rejection: {}
I have tried defining blank objects in the scope like:
$scope.questForm = [];
$scope.questForm.Task = {};
But I should not need to because they are created in the template? confused. Thanks all.
Actually nope. Having your template compiled does not mean all the ng-models are initialized. While ng-model is smart enough to create all the intermediate objects, if they don't exist, it doesn't do so until $viewValue is changed. In your case if you upload a file without editing any other input first, $viewValue for inputs has never changed, and thus you have to initialize questForm, questForm.Task, and questForm.Task[taksID] yourself.
if (!$scope.questForm) {
$scope.questForm = {};
}
if (!$scope.questForm.Task) {
$scope.questForm.Task = {};
}
if (!$scope.questForm.Task[taskID]) {
$scope.questForm.Task[taskID] = {};
}
$scope.questForm.Task[taskID].FileUploadID = response.data;
Or you can initialize questForm and questForm.Task at the beginning. And only check if questForm.Task[taskID] exists before initializing it.

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.

Typehead displays wrong on certain input

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.

Angularjs input text value from json

I am trying to load json data on the form which has two text fields. TextFields display correct price and qty from json. But, the problem is when user updates the price and qty, updated price and qty are not reflecting in controller method - $scope.total(). Here is my code
HTML file
<div ng-repeat="row in myData">
<input type="text" ng-model="row.price">
<input type="text" ng-model="row.qty">
</div>
JS file
$http.get('http call’).success(function(data){
$scope. myData = data
})
$scope.row = {
price : 10.0,
qty : 2;
};
$scope.total = function() {
console.log($scope.row.price);
console.log($scope.row.qty);
}
JSON - [{"price":10.50,"qty":3}]
Not sure what I am missing here? why updated values are not reflecting in controller?
Your $scope.total function needs to be called somewhere, and as mentioned by #pankajparkar, $scope.row is different than the row in your directive (which belongs to the ng-repeat's scope only).
Do this:
<div ng-repeat="row in myData">
<input type="text" ng-model="row.price" ng-change="total()">
<input type="text" ng-model="row.qty" ng-change="total()">
</div>
JS:
$http.get('http call').success(function(data){
$scope.myData = data;
});
$scope.total = function() {
console.log($scope.myData[0].price * $scope.myData[0].qty);
};
As your data is an array you loop througt is to get the total of all your objects:
$scope.total = function() {
var sum = 0;
myData.forEach(function(obj){
sum += obj.qty * obj.price
});
return sum;
}
and you need to call this function when values change.
Fiddle
With only one element in your array, your function total should be like #Joao answer:
$scope.total = function() {
return $scope.myData[0].price * $scope.myData[0].qty;
};
Fiddle
First thing if response is only one item then it should not be an array
It should be Single JSON such as {"price":10.50,"qty":3}
HTML Code
<input type="text" ng-model="myData.price">
<input type="text" ng-model="myData.qty">
<button type="button" ng-click="updateTotal()">
JS CODE
$http.get('http call').success(function(data){
$scope.myData = data;
});
$scope.total = function(){
//now here you will get updated scope values
console.log($scope.myData.price * $scope.myData.qty);
}
Hope this will be help full to you.
$scope.total is a function, but it is not returning anything.
instead of:
var total = $scope.row.price * $scope.row.qty
try:
return $scope.row.price * $scope.row.qty

Why isn't updated model's value, updated via service, changing in UI?

Below, summary and test are set via MyCtrl. I'm seeing "my test" display properly, but not "summary.name". I'm seeing that MySvc.get()'s callback executes as expected, but summary.name's updated value doesn't appear on the UI.
Any suggestions on why summary.name's updated value isn't appearing on the UI ?
app.js:
...
.state('tab.edit', {
url: '/edit',
views: {
'tab-dash': {
templateUrl: 'templates/MyTemplate.html',
controller: 'MyCtrl'
}
}
})
...
MyTemplate.html:
<label class="item item-input item-stacked-label">
<span class="input-label">Name</span>
<textarea rows="8" placeholder="Nothing yet." ng-model="summary.name"></textarea>
</label>
<label class="item item-input item-stacked-label">
<span class="input-label">TEST</span>
<textarea rows="8" placeholder="Nothing yet." ng-model="test"></textarea>
</label>
controllers.js:
...
.controller('MyCtrl', function($scope, MySvc) {
console.log("MyCtrl: entered");
$scope.summary = MySvc.get(0);
$scope.test = "my test";
...
MySvc.get(0) returns ( and I see this callback execute and change ret.name ):
return $http.get(url).then(function(response) {
console.log("MySvc callback: response.data = %o", response.data);
console.log("MySvc callback: response.data.name = " + response.data.name);
ret = new MySvc(response.data);
console.log("MySvc callback: ret.name = " + ret.name);
return ret;
});
You treated the return value of MySvc.get as a synchronous (immediately available) value. What is received via the $http service is not available immediately, so you can't treat it as a simple return value.
What you'll really want to do is use it as a promise, which it (most likely) is:
MySvc.get(0)
.then(function(summary) {
$scope.summary = summary;
});
And you'll probably want to read up on promises, Chapters 1 to 3 of "You Don't Know JS - Async & Performance" are an excellent starting point.

Resources