angularJS target specific json node - angularjs

Im working with a webservice and the returning JSON brings back some meta data that I use to define the layout of forms.
However, the JSON is continually evolving by the developers so currently where I target:
ng-repeat="cat in metaData[1].AcceptedValues"
To draw all the form structure...
Means that at some stage metaData Array element 1 may no longer hold the structure for this current form, it may be 2, 3 etc. JSON developer said he has now added additional data to identify each of the nodes under the value NAME
Under array element 1 I can now see Name : "ProductData" - and similarly under the other nodes different unique identifiers for Name
So basically I need to know how I can adjust my condition above to search for the metaData array element that contains the value Name = "ProductData" (or the value for the form I am rendering) and then any changes in array position is irrelevant.
Thanks

You can create a method to find the specified object:
$scope.getProductData = function() {
angular.forEach($scopem.metaData, function(item){
if(item.Name === "ProductData"){
return item.AcceptedValues;
}
});
return [];
};
Then use it in your markup:
ng-repeat="cat in getProductData()"

Related

Angular costum comparator getting object key

i am creating a rather complex directive that creates a table with both frontend pagination and searching.
Now since you should only be able to search on specific fields in the data array i will need to create a custom comparator.
<tr ng-repeat="data in dataArray | filter:search:costumComparator"> </tr>
And in my link function:
scope.customComparator = function (field,input) {
}
Problem is that i am unable to see which field in data i am currently inspecting.
The goal of this assignmen is to compare the field key with shown fieldkeys in the table and only allow the user to search on keys that are actually shown.
I have one part of the assignment done which is an array that displays the keys of fields that i am showing.
However i cant use this for anything untill i know which fieldkey i am currently looking at in my customComparator.
So my question is:
Is there a way i can get the field key inside my costumComparator function?
Hi You can set dataArray object then it will work.
$scope.dataArray = {};

AngularJs Select ng-model not working with foreign key value

Let's say I have a service that provides a list of objects that I want to load as options in a <select> element.
The service method is myReferenceList.getCachedList().
It returns an array of objects of the following structure:
[
{
ReferenceId: "guid-1234",
ReferenceName: "some string",
SomeRandomRefField: 5
},
// etc...
]
In my controller I have a $scope.dataRow variable defined like so:
$scope.dataRow = {
ReferenceIdFk: "guid-2345",
// etc...
}
I want to load the select option list with data from myReferenceList.getCachedList(), with the displayed text coming from ReferenceName and the non-visible value coming from ReferenceId.
I want to hook this select item up to $scope.dataRow.ReferenceIdFk something like this:
ng-model="dataRow.ReferenceIdFk"
When all is said and done, and the data is loaded into $scope.dataRow.ReferenceIdFk, the select element should hold the selected object's ReferenceId value. When a user selects a different item in the select list, the value in $scope.dataRow.ReferenceIdFk should automagically get changed to the correct value.
How can I get this to work?
Loading the values into the select list is easy. Getting the automagic binding to the underlying data value in $scope.dataRow.ReferenceIdFk just doesn't seem to work.
Obviously, I can hand-jam in some procedural code to make this binding work, but I'm trying to understand how to declaratively make it work using angular features. Is it possible?
ng-options provides a micro-syntax to declare how to map objects in an array to display value and the selected result:
Angular's documentation provides the full syntax, but for your case the following should work:
<select ng-model="dataRow.ReferenceIdFk"
ng-options="i.ReferenceId as i.ReferenceName for i in myReferenceList.getCachedList()">
</select>
In a nutshell, the microsyntax is:
"<valueExp> as <displayExp> for <itemAlias> in <arrayExp>"
where:
arrayExp: is the expression that returns an array of values
itemAlias: is the alias for each item in an array; can be whatever you want
displayExp: a string expression that is used for each label; uses the itemAlias
valueEx: an expression whose value is assigned to ngModel-bound variable; uses itemAlias, and can return either a property of the item (itemAlias.prop as) or the item itself (itemAlias as)

Angular ng-repeat with Search not working when search is empty

I have the following:
<div ng-repeat="foo in foos | filter:search | orderBy:'fooName'">
<input type="checkbox" id="cb-{{$index}}" ng-click="diseaseCheckboxClick($index)"/>
<input type="hidden" id="foo-{{$index}}" value="{{foo.id}}"/>
<label id="label-{{$index}}">{{foo.fooName}}</label>
</div>
The collection of 'foos' comes from a ReST call.
A person has a collection of 'foos', and I have a function that loops over each 'foos' collection, and the job of the function is to checkmark the 'foo' or not. If the person has that particular 'foo', checkmark it.
I use the a hidden text field to hold the fooIds, and I use it for comparison.
for (var i = 0; i < $scope.person.foos.length; i++) {
var outerFooId = $scope.person.foos[i].id;
for (var j = 0; j < $scope.foos.length; j++) {
var fooElem= '#foo-' + j;
var fooIdValue = $(fooElem).val();
if (outerFooId === fooIdValue) {
var cbId= '#cb-' + j;
$(cbId).attr('checked', true);
break;
}
}
}
The only problem I have is that when I enter a search item that brings back zero results (and that works), but then when I backspace back and remove the search items that brought back no results, the function that is used to checkmark the checkboxes does not work, it is due to the call '$(fooElem).val();' in the function returns undefined.
I see the list in the page, and when i view source i see all of the fields, including the hidden text field that holds the fooId, so I do not know why the function does not 'see' it.
Note that this only occurs when I enter a search that brings back no results, then backspace over to clear out the search field.
The search and marking the checkboxes work otherwise.
Any idea why this is happening, or how I can make the function 'see' the hidden text field. I'll note that I also tried to use the 'label' element, but same result, the text of the label is undefined.
This will not completely solve your problem, but I have to point out that you are making the classic mistake of mixing methodologies. In Angular, you should never, EVER have any code that manipulates the DOM outside of a directive, and even then it is rarely necessary.
There are two classic clues here - you are using $() (jquery), and you are using HTML DOM id attributes. The only real use for id's in Angular is to associate labels with input elements (which you are not doing here), and even then in most cases you can get away with encasing the input inside the label instead of using the "label for" syntax.
Having said all of that, and without seeing the rest of your code, here is how you "angularize" what you are doing:
I am going to make a few assumptions (that should probably be answered by your question):
1) foos is a list of all foos. Each foo has a unique id and name.
2) a person can have some or all foos.
3) The purpose of your code is to allow someone to see which foos they have, and filter the list by name.
So first, the model:
To make things fast (avoid looping when checking each foo, foos should be indexed by id. To when you load them from your for a person, you should create a map by id:
var fooMap = {};
foreach(var i in person.foos) {
fooMap[person.foos[i].id] = person.foos[i];
}
Note that this could be simplified if person.foos is already a map by id!
You also need a few methods in your controller:
$scope.hasFoo(foo) {
return fooMap[foo.id];
}
And your template becomes:
<div ng-repeat="foo in foos | filter:search | orderBy:'fooName'">
<label>{{foo.fooName}}<input type="checkbox" ng-click="diseaseCheckboxClick(foo)" ng-checked="hasFoo(foo)"/></label>
</div>
Note no hidden input, no id's - just a template that is driven by the state of your $scope.
This will solve the immediate problem (losing foos after a search).
The last part of the puzzle is implementing your click function. Without knowing more about your actual implementation, here is the general idea (you'll have to figure the details out yourself).
$scope.diseaseCheckboxClick = function(foo) {
if(fooMap[foo.id]) {
//remove the foo from $scope.person.foos
// and update fooMap
delete fooMap[foo.id];
}
else {
//add the foo to $scope.person.foos and update fooMap
$scope.person.foos.push(foo);
fooMap[foo.id] = foo;
}
}
Hopefully this points you in the right direction. I'm inferring a lot, so I'm sure I've got a few things wrong, but the key problem you have right now is mixing jQuery with Angular.

How do I keep results filtered with new results added dynamically?

I'm using an infinite scroller package which loads results dynamically. I have a set of filters which display the results I want depending on what filter is selected.
The results will not be returned to the front end in any sort of filtered order. What I want to achieve is get the unfiltered results being returned into filtered state that I may already have selected.
For example one filter is a star rating and lets say that I have 3 and 4 star selected in my filter. How would I get newly loaded results to be returned within that filter.
I suppose I could send the filters back to the backend to specifically request what I'm looking for but I'm looking for an angular solution.
Here's the ng-repeat that iterates over each hotel I want any newly loaded results to be subjected to any filters already selected:
<div ng-repeat="hotel in (filteredHotels = (hotelResults | hotelRatingsFilter:ratings)) | startFrom:(currentPage - 1)*10 | limitTo:10 " class="hotel-results-container">
<ul>
<li>{{hotel.starRating}}</li>
</ul>
</div>
This is the code that communicates with the backend, as far as I can tell (I didn't write it)
.constant('HOTEL_SEARCH_CONSTANTS',{
httpSettings:
{
url:'/hotel/hotelsearch/search',
method:'POST'
}
})
.service('hotelSearchService', ['CachedObservable','$http','HOTEL_SEARCH_CONSTANTS','HOTEL_CONSTANTS','baseSearchSettings','loadingScreen',
function(CachedObservable,$http,HOTEL_SEARCH_CONSTANTS,HOTEL_CONSTANTS,baseSearchSettings,loadingScreen){
CachedObservable.call(this)
var _this = this,
p
this.search = function(searchParameters){
var config = HOTEL_SEARCH_CONSTANTS.httpSettings
var currentSearch = angular.extend(searchParameters,baseSearchSettings)
config.data = {
basicSearch: baseSearchSettings,
hotelSearch: searchParameters
}
_this.update(p=$http(config), angular.extend(angular.copy(searchParameters),baseSearchSettings) )
//Alert subscribers with both the promise and the search parameters
//used
loadingScreen.newSearch.update(p,HOTEL_CONSTANTS.loadingTemplates.loadingMessageTemplate)
//Trigger a loading screen and specify the template to use
}
}])
Thank you
I found this article that says that angular will iterate over the array if there is any change to the data. Looks like its safe to keep adding to the data array and any filters already being used will be applied by angular to the updated data array.
Step 6 says:
"A watch is added on the array, which triggers step 1 again if the array undergoes any change"
ng-repeat in angular

AngularJS drop down (ng- options) not binding - string to object (initial selection)

I am having a problem binding data retrieved from the server to a drop down list. The main issue I think is the fact that the comparison is done on differing object types.
For example:
1. The object returned from the server contains a currency code string. we want this to be bound to an item in the dropdown list
"baseCurrencyCode":"GBP"
The view model returns the list of currencies.. These are returned as a list of currency objects with different properties
{"currencies":[{"id":1,"rateId":0,"abbreviation":"AFN","description":"Afghani","rate":0.0,"rateDescription":null,"languageCode":"en-gb","isDefault":true,"fullDescription":"AFN - Afghani - ","shortDescription":"AFN - Afghani"}}
etc.
Currently, I have got this working by writing a function to go through every property for every item in the list, find the correct property we wish to compare to - do the comparison and then return the initial selection.
When calling my save method I then need to manually bind the currency abbreviation to the object I wish to return to the server.
Surely there must be a better way to do this?
Some of my code for reference..
<select ng-model="selectedCurrency" ng-options="currency.shortDescription for currency in viewModel.currencies"></select>
// Call to my custom method..List, PropertyName, value to compare
$scope.selectedCurrency = InitialiseDropdown($scope.viewModel.currencies, "abbreviation", $scope.updatedObject.baseCurrencyCode);
// Code executed when saving - to bind the currency to the updated object
$scope.updatedObject.baseCurrencyCode = $scope.selectedCurrency.abbreviation;
Any help is appreciated!
EDIT
Sorry if I wasn't clear enough.. To summarise..
The main problem here is binding to the drop down and initial selection.
The object we are updating contains a parameter (string) of the currency abbreviation.
The list we select from is a list of currency objects. As these are two differing object types I have been unable to plug in angulars 2 way binding and have written some code to do this on initial retrieval and when saving.
The cleanest way to fix this would be to return a currency object in our retrieval instead of a simple string of the abbreviation, but this is not an option.
Is there a better way of enabling 2 way binding on different object types ?
Thanks again
It is not exactly clear what the problem is, but you can save yourself some work by binding the <select> to the currently selected currency object (so you don't have to look it up later).
select + ngOptions allow you to bind to one value while displaying another, with the following syntax:
<select ng-model="selectedCurrency"
ng-options="currency as currency.shortDescription
for currency in viewModel.currencies">
</select>
In the above example, $scope.selectedCurrency will be bound to the whole currency object, but currency.shortDescription will be displayed in the dropdown.
See, also, this short demo.
UPDATE:
In case you don't need to bind to the whole currency object, but just bind updatedObject's baseCurrencyCode property to the abbreviation of the selected (in dropdown) currency, you can do it like this:
<!-- In the VIEW -->
<select ng-model="updatedObject.baseCurrencyCode"
ng-options="c.abbreviation as c.shortDescription
for c in currencies">
</select>
// In the CONTROLLER
$scope.currencies = [...];
$scope.updatedObject = {
...
baseCurrencyCode: <baseCurrencyCodeFromServer>
};
See, also, that short demo.
I have had the same problem, ng-model and ng-option being from 2 different sources. My ng-model is bound to a value in a json object representing a chosen filename and my ng-option is a list of possible values taken from a csv file.
In the controller I am reading a directory via a Nodejs route, and creating a json array of filenames like this
var allCsvFiles = [{"name":"file1.csv"},{"name","file2.csv},etc..]
The current csv file, i.e. the selected one is stored in another json array
[{"date":"01-06-2017","csvfile":"file1.csv","colour":"red"},{...}, etc].
I was using the following code for the dropdown:
<select type="text" ng-model="file.csvfile"
ng-options="opt.name for opt in allCsvFiles track by opt.name"></select>
Which caused the current selection to be blank and if I selected an item from the dropdown it put [object],[object] as the current selection. If I stepped through the code I found that it seemed to be selecting {"name","file1.csv"} as the option and couldn't display it, this seemed odd as my ng-options selection looks like it should just return the value of "name" not the array entry. I tried many different ways to make this work but eventually I found that if I made the list of possible selections a plain javascript array:
var allCsvFiles = ["file1.csv","file2.csv", "file3,csv]
and changed the select to:
<select type="text" ng-model="file.csvfile" ng-options="opt for opt in allCsvFiles"></select>
then the dropdown selection worked as expected.
I may have missed some other obvious solution here, but as the array of json objects is one dimensional anyway it doesn't seem to be an issue.
It looks like the OPs question has been answered, I just thought I'd add this as it solved it for me.

Resources