Avoiding another API call with Angular and JSON - javascript

Probably an easy question, but I am making a call to an API that returns a full list of products in JSON. The products are listed under 4 categories in the JSON - 'connectivity','cables','routers' and 'servers'. Using the getProducts() function below, I assign the list of 'connectivity' products to a variable called $scope.connectivitylistOfProducts - and this is what I display to the user in the UI as a default.
productsServices.getProducts()
.then(function (allProducts) {
$scope.listOfProducts = allProducts.data.category[0];
$scope.connectivitylistOfProducts = allProducts.data.category[0].connectivity;
})
.finally(function () {
});
In the UI, I have a select box that's contains a list of the categories where the user can change to view the products under the category they choose. changeProduct() is what is called to change the category
$scope.changeProduct = function () {
// change the product
};
I am already loading the full list of categories and products into $scope.listOfProducts and I dont want to make another API call by calling getProducts again. I'm not sure how to set up another variable (for example $scope.routerslistOfProducts) and assing the correct products to it. Could anyone tell me the best way to handle this? Many thanks

Could you try to access lists by array notation:
you have:
$scope.listOfProducts = allProducts.data.category[0];
you could create a category variable:
$scope.category = 'connectivity';
and to access using, for example:
<div ng-repeat="product in listOfProducts[category]">

If your payload has all the arrays then on API call back assign to scope variable say $scope.mylist, now you bind $scope.mylist.categories to drop down and on drop down change send category Id to change function () and filter using for loop and equal operator , while running through loop I.e filtering , which ever product matches category push to a variable say $scope.filteredproducts ....and here you got your filtered products.
This is simple way to understand , there are better ways to maintain filtered arrays too.

Related

Adding attributes to search hits after query and dynamically re-render in InstantSearch.js

I am setting up InstantSearch icw Algolia for products of a webshop with the plain JavaScript implementation.
I am able to follow all the documentation, but I am running into the problem that we have prices specific to customer groups, and things like live stock information (need to do another API call for that).
These attributes I would like to ideally load after getting search results, from our own back-end.
I thought it would simply be a matter of manipulating the search results after receiving them and re-rendering only the front-end (without calling the Algolia search API again for new results).
This is a bit tricky. The transformItems functionality is possible, but I want to already display the results and load the other data into the hit templates after, not before displaying the hit results.
So I end up with a custom widget, and I can access and manipulate the results there, but here the problem is that I don’t know how to reflect these changes into the rendered templates.
The code of my widget (trying to set each stock number to 9) is as follows:
{
render: function(data) {
const hits = data.results.hits;
hits.forEach(hit => {
hit.stock = 9
});
}
}
The data is changed, but the generated html from the templates does not reflect any changes to the hit objects.
So how could I trigger a re-render after altering the hits data, without triggering a new search query?
I could not find a function to do this anywhere in the documentation.
Thanks!
There is the transformItems function in the hits widget that allows you to transform, remove or reorder items as you wish.
It is called before items displaying.
If I use your example, it would be something like this :
transformItems(items) {
return items.map(item => ({
...item,
stock: 9,
}));
}
Of course you can add you API call somewhere in that.
The documentation for the vanilla JS library :
https://www.algolia.com/doc/api-reference/widgets/hits/js/#widget-param-transformitems

Result is not display in autocomplete (Angular)

I use ng-prime <p-autocomplete> for display values via search in the back-end
here is my html
<p-autoComplete [(ngModel)]="agent" [suggestions]="filteredAgents" name="agents" (completeMethod)="filterAgents($event)" [size]="10"
placeholder="Agents" [minLength]="3"></p-autoComplete>
At the component.ts I initialize array like this at start of the component
filteredAgents: string[] = [];
and I have a method to send query to back end and push it to array
filterAgents(event) {
let query = event.query;
this._agentsService.getAgentSearch(query).subscribe(result => {
result.items.forEach((value) => {
this.filteredAgents.push(value.name);
console.log(this.filteredAgents);
});
});
}
I see filtered value in console, but I don't see it in suggestions.
Where can be my problem?
AutoComplete either uses setter based checking or ngDoCheck to realize if the suggestions has changed to update the UI. This is configured using the immutable property, when enabled (default) setter based detection is utilized so your changes such as adding or removing a record should always create a new array reference instead of manipulating an existing array as Angular does not trigger setters if the reference does not change. ( Angular documentation )
Array.prototype.push doesnt create a new reference it rather mutates the original array. So you need to make a new one.
filterAgents(event) {
let query = event.query;
this._agentsService.getAgentSearch(query).subscribe(result => {
this.filteredAgents = [...result.items.map(e => e.name)]
});
}
I maped the result to extract the names.
If filtered agents is an object array try adding field="name" to the directive attributes.
Here name is a field in the object. The directive uses this field to display in suggestions

Angular.copy keeps giving me the same object

I have an AngularJS application that manages badges. In the application is a form to set the badge # and the name of the person it is assigned to, etc. This gets stored in $scope.badge.
When the user submits the form, I want to add the new badge to a list of badges, which is displayed below the form.
Partial code looks like this:
var badge = angular.copy($scope.badge); // make a copy so we don't keep adding the same object
$scope.badgeList.push(badge);
The first time I run this code, it adds the badge as expected.
Any subsequent time I run this code, the next badge REPLACES the previous badge in the badgeList. In other words, if I add 5 badges, the badgeList still only has 1 object in it because it just keeps getting replaced.
I'm thinking that this may be happening because the same object keeps getting added? Maybe I'm wrong? I am using angular.copy to try and avoid that happening, but it doesn't seem to be working.
Any thoughts on this?
$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
no need to use angular.copy since you are ultimately storing all the badges in an array
angular.copy is used when you want to make a clone of object and not update the existing object and the clone's change are not reflected in main object.
If you just want to maintain a list of badges you can execute this block of code
like this
function addBadges(){
$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
}
If you are refreshing the controller then obviously the variable will be reset and for such a case you need to make use of angular services.
Create a service and inside the service you need to define getter and setter method that will help in data persistence
and your bages array if saved in service will persist till the application is in foreground.
You could do something like this.
function addBadges(){
//initialize if undefined or null
if(!$scope.badgeList){
$scope.badgeList = [];
}
//Check if badge does not exists in the list
if ($scope.badgeList.indexOf($scope.badge) === -1) {
//Add to badge list
$scope.badgeList.push($scope.badge);
}
}

Async Angular/Node $scope calls

really new to Angular here and I have a problem that I need some help with. Basically, I need to have a selection of items that user can click on. When an item is clicked, the page needs to show some of the properties that the item has like it's description, etc. The first part is not a problem, but I'm having trouble with the second part, which is displaying the data. So here is what I have:
On the front end, I have an angular ng-click chooseItem(item) function that takes the clicked item as its paramater:
<div ng-repeat="item in items" class="col-xs-2 col-md-1">
<div ng-click="chooseItem(item)" class="thumbnail">
<img src="/images/items/{{item.name}}.png"/>
</div>
</div>
This is then passed on to the items factory through items.getChosenItemData(item) function. Since the real item data is stored in Mongo and not the factory, this function queries the db to retrieve the item data. This retrieved data is stored into the chosenItem object, which is then passed back to the controller as $scope.chosenItem.
app.factory('items', ['$http', function($http){
var objects = {
items: [
// ... more items before these
{name: "Pencil"},
{name: "Pen"}
/* I use these item objects as keys to the items themselves.
The ng-repeat iterates through all of the names for each item
which allows me to display static images for each item to the page.
There aren't many items, about 100, but they have tons of json information
so to hardcode it all into here is not an option
A way to do this without any hardcoding would be nice! */
// more items after these
],
// this is used to store a currently clicked item's values
chosenItem: null
}
objects.getChosenItemData = function(name){
return $http.get('/items/' + name).success(function(data){
// console.log(data);
angular.copy(data, objects.chosenItem);
console.log("Chosen Item: ", objects.chosenItem);
})
}
return objects
}]);
app.controller('MainCtrl', [
'$scope',
'items',
function($scope, items){
$scope.items = items.items;
$scope.chosenItem = null;
$scope.chooseItem = function(item){
items.getChosenItemData(item.name);
$scope.chosenItem = items.chosenItem; //chosen item object attribute in factory
console.log("$scope item: ", $scope.chosenItem);
}
}
});
This almost all works. I can query the data of the clicked item successfully, but returning it is another story. Upon first click, the value of $scope.chosenItem is null. Then upon second click, it stores the value of the click item. This also causes the problem where if I click on n amount of items, the value stored is always the value of the n-1 item, not the current item. I need it to store the value of the clicked item on the first click, not the second.
I have a feeling I need to add a callback somewhere in here to make it work, but I'm new to Angular/JS so I'm not sure where it should even go.
Thanks for any help! Also any tips or leads on Angular design patterns would be much appreciated, since I have the feeling that this is a terrible implementation of something that seems rather simple.
I suggest you to expose the service directly:
$scope.serviceItem = items;
and then you can call it in the view like that:
{{serviceItem.chosenItem}}
It will be always updated to the latest clicked value.
I hope it helps.

Angular text input filter initializes with no results

I have a basic angular app that sorts and filters a list of species from a local json file. When the app initializes none of the species are returned. When you begin to type some species are returned, and if you delete your search terms all species are returned.
I would like all species to be returned when the app initializes.
http://plnkr.co/edit/0DOwSvtaepSfFWKR8UGS?p=info
I think it's neater to use the inline notation for filter, which avoids this problem:
<div ng-repeat="animal in species | filter:query | orderBy:orderProp")>
NOTE: you can then remove the code that sets up the $watch on query
EDIT: as you specifically want it in the controller:
The reason your filter is initialising to blank, is because your $scope.species data is being populated asynchronously. When the first $watch is triggered, there is no data to filter. This stays the case until you input a query.
To solve this, set up the $watch after the data has arrived, like so:
$http.get('species.json').success(function(data) {
$scope.species = data;
$scope.$watch('query', function (query) {
$scope.filteredData = $filter('filter')($scope.species, query);
});
});
Alternatively you could manually run the filter function once, inside the success callback.
Have you tried setting filteredData to the data returned from the get?
Example

Categories

Resources