How to use ng-repeat for dictionaries in AngularJs? - angularjs

I know that we can easily use ng-repeat for json objects or arrays like:
<div ng-repeat="user in users"></div>
but how can we use the ng-repeat for dictionaries, for example:
var users = null;
users["182982"] = "{...json-object...}";
users["198784"] = "{...json-object...}";
users["119827"] = "{...json-object...}";
I want to use that with users dictionary:
<div ng-repeat="user in users"></div>
Is it possible?. If yes, how can I do it in AngularJs?
Example for my question:
In C# we define dictionaries like:
Dictionary<key,value> dict = new Dictionary<key,value>();
//and then we can search for values, without knowing the keys
foreach(var val in dict.Values)
{
}
Is there a build-in function that returns the values from a dictionary like in c#?

You can use
<li ng-repeat="(name, age) in items">{{name}}: {{age}}</li>
See ngRepeat documentation. Example: http://jsfiddle.net/WRtqV/1/

I would also like to mention a new functionality of AngularJS ng-repeat, namely, special repeat start and end points. That functionality was added in order to repeat a series of HTML elements instead of just a single parent HTML element.
In order to use repeater start and end points you have to define them by using ng-repeat-start and ng-repeat-end directives respectively.
The ng-repeat-start directive works very similar to ng-repeat directive. The difference is that is will repeat all the HTML elements (including the tag it's defined on) up to the ending HTML tag where ng-repeat-end is placed (including the tag with ng-repeat-end).
Sample code (from a controller):
// ...
$scope.users = {};
$scope.users["182982"] = {name:"John", age: 30};
$scope.users["198784"] = {name:"Antonio", age: 32};
$scope.users["119827"] = {name:"Stephan", age: 18};
// ...
Sample HTML template:
<div ng-repeat-start="(id, user) in users">
==== User details ====
</div>
<div>
<span>{{$index+1}}. </span>
<strong>{{id}} </strong>
<span class="name">{{user.name}} </span>
<span class="age">({{user.age}})</span>
</div>
<div ng-if="!$first">
<img src="/some_image.jpg" alt="some img" title="some img" />
</div>
<div ng-repeat-end>
======================
</div>
Output would look similar to the following (depending on HTML styling):
==== User details ====
1. 119827 Stephan (18)
======================
==== User details ====
2. 182982 John (30)
[sample image goes here]
======================
==== User details ====
3. 198784 Antonio (32)
[sample image goes here]
======================
As you can see, ng-repeat-start repeats all HTML elements (including the element with ng-repeat-start). All ng-repeat special properties (in this case $first and $index) also work as expected.

JavaScript developers tend to refer to the above data-structure as either an object or hash instead of a Dictionary.
Your syntax above is wrong as you are initializing the users object as null. I presume this is a typo, as the code should read:
// Initialize users as a new hash.
var users = {};
users["182982"] = "...";
To retrieve all the values from a hash, you need to iterate over it using a for loop:
function getValues (hash) {
var values = [];
for (var key in hash) {
// Ensure that the `key` is actually a member of the hash and not
// a member of the `prototype`.
// see: http://javascript.crockford.com/code.html#for%20statement
if (hash.hasOwnProperty(key)) {
values.push(key);
}
}
return values;
};
If you plan on doing a lot of work with data-structures in JavaScript then the underscore.js library is definitely worth a look. Underscore comes with a values method which will perform the above task for you:
var values = _.values(users);
I don't use Angular myself, but I'm pretty sure there will be a convenience method build in for iterating over a hash's values (ah, there we go, Artem Andreev provides the answer above :))

In Angular 7, the following simple example would work (assuming dictionary is in a variable called d):
my.component.ts:
keys: string[] = []; // declaration of class member 'keys'
// component code ...
this.keys = Object.keys(d);
my.component.html: (will display list of key:value pairs)
<ul *ngFor="let key of keys">
{{key}}: {{d[key]}}
</ul>

Related

how do I take value from this $scope

so I have this code in my chatting application, its for send message to the partner.
$scope.messages = {
from : $scope.datauser['data']['_id'],
fromname : $scope.datauser['data']['nama'],
to : $scope.tmpuserid,
message : $scope.tmp['sendmessage'],
time : moment()
};
I want to add text-to-speech features in my application, the question is how I take the value from $scope.messages but just the message because if I just write $scope.messages, TTS will read all data from from until time
Not sure exactly what you want, but it would be something like:
<div ng-repeat="message in messages">
<p>{{message.message}}</p>
</div>
EDIT: This answer is for iterating through an array of messages.
If it were just one messages object it would be:
<p>{{messages.message}}</p>
Maybe I'm missing something in your question, but if all you want to extract just the message from the messages $scope object you would use JS object dot notation e.g.
var justMessage = $scope.messages.message;
// justMessage = $scope.tmp['send message'];
// OR parse an array
var myPartnerMessages = [];
angular.forEach($scope.messages, function(msg, key) {
this.push(msg.message);
}, myPartnerMessages);
You can get the value from $scope as follows:
View.html
<div ng repeat="item in messages">
<div>{{item.message}}</div>
</div>
You just call the property as $scope.messages.message. Or since you already had it on another scope variable, you can call it as $scope.tmp['sendmessage'].
If you are trying to access from the HTML side, you would use it as this:
<p>{{messages.message}}</p>
Not exactly sure if this is what you need from reading your question, though.
if it is a single object not an array if is an array you should use ng-repeat
On js end you can get it by
$scope.messages.message
On html end you can get it by
{{messages.message}}

Strange bug with ng-repeat and filter

I'm using NodeJS, ANgularJS, and MongoDB with mongoose
Here is my model :
var PostSchema = new mongoose.Schema({
nomReseau : String,
corps : String,
etat : String,
section : String
});
I got a function that change the attribute etat:
$scope.passer = function(index){
var post = $scope.posts[index];
post.etat = "enCours";
Posts.update({id: post._id}, post);
$scope.editing[index] = false;
}
I'm using a ng-repeat for show object in my database :
<ul>
<li ng-repeat="post in posts ">
<p>
<a ng-show="!editing[$index]" href="#/{{post._id}}">{{post.corps}}</a>
</p>
<button ng-show="!editing[$index]" ng-click="passer($index)">Passer</button>
</li>
</ul>
I can see all post in my database and when I click on the button this works perfectly the attribute etat change and all is fine.
But when I add a filter in the ng-repeat like this :
<li ng-repeat="post in posts | filter:{ etat:'aTraiter'} ">
The filter works great I have all post with the attribute etat:'aTraiter'
But if I click on my previous button and change the attribute etat nothing change and I try with other functions they all work wihout the filter but when I put it nothing work.
The problem is that $index will change if less data is shown (because you're filtering). you could use directly post variable
ng-click="passer(post)"
and your function should be something like
$scope.passer = function(post){
post.etat = "enCours";
Posts.update({id: post._id}, post);
var index = $scope.posts.findIndex(function(p) { /* comparison to get original index */ }); /* keep in mind findIndex is not supported on IE, you might want to use filter or for loop instead) */
$scope.editing[index] = false;
}
you could handle editing in the post variable directly. So in your passer function you can do this
post.editing = false;
and in your view
ng-show="!post.editing"
this way you won't use $index and you will prevent all issues with being updated by filters
There are bugs in AngularJS v1.4 where in certain situations the ng-repeat breaks. I upgraded to v1.6 and it went away.
Do you have any controllers/services that access $scope.editing? If so, you might be setting the $scope.editing[$index] equal a previous state where it wasn't equal to false. You may also want to consider that you are assuming $scope.editing[$index] is going to be a boolean. if it has any other type such as string or number then it will evaluate to true.
Otherwise none of your results have the attribute etat equal to 'aTraiter' so they aren't showing. Have you verified that any of them actually do have etat equal to 'aTraiter'. You might be changing that value somewhere else. Possibly from the Passer function

Ruby/Selenium value into array

I need some help in Ruby/Selenium
i have a few elements on page like this:
<a class="Name" title="Migel" href="/profiles/users/_1324567980000000c/">Migel</a>
i'd like give HREF string value into array.
when i use "find_element" - ALL OK:
profile = driver.find_element(:xpath, "****").attribute ('href')
but page contains a lot of such elements, and i try to use
profile_array = driver.find_elements(:xpath, "lbuh-bluh").attribute ('href')
naturally that attribute ('href') dont work with array.
how can I do in this case?
in general, my task in implementing "random click" on page and i want collect all "href" elements which i need into array, and then realize something like this:
array[srtring1, string2....stringN].sample.click (schematically)
would just mapping over elements and get href attribute be sufficient?
profile_array = driver
.find_elements(:xpath, "lbuh-bluh")
.map { |el| el.attribute('href') }
I see that this is some sort of parsing html with Ruby, if this is the case why dont you use the Nokogiri gem, lets say you have something like:
<div id="funstuff">
<p>Here are some entertaining links:</p>
<ul>
<li>YouTube</li>
<li><a data-category="news" href="http://reddit.com">Reddit</a></li>
<li>Kathack</li>
<li><a data-category="news" href="http://www.nytimes.com">New York Times</a></li>
</ul>
</div>
Which is the content of a file called lets say: selenium.html located at your Desktop: C:\Desktop\selenium.html
here is the ruby code that handels that:
page = Nokogiri::HTML(open("C:\Desktop\selenium.html"))
links = page.css("a")
profile_array = []
links.each{|ref| profile_array << ref["href"] }
puts profile_array
=>http://youtube.com
=>http://reddit.com
=>http://kathack.com/
=>http://www.nytimes.com

Filter ng-repeat more times

I'm creating an application to manage restaurant orders.
I create the menu from $http so I've this list:
<div class="row vertical" style="background-image: url(/gest/images/etichette/ANTIPASTI.png);border-color: #0CF">
<div class="card piatti col s2" ng-repeat="anti in antis | filter:{tipo:'ANTIPASTI'}">
<div class="card-content"> <span class="card-title truncate red darken-3">{{anti.piatto}}</span> </div>
<div class="card-action"> {{n}}</div>
</div>
</div>
The div with class "row vertical" contain one time starters, then pasta, then beef ecc.
So I use ng-repeat each time, and filter by tipo.
My question is: is there any way to make ng-repeat only one time to show all menu (orderer before by starters, then pasta, beef ecc)?
I have this data (is a restaurant menu):
piatto: name of the the dish
tipo: category of the dish (like pasta, beef, fish, starters ecc)
I would show with only one repeat all the dishes ordered so:
starters, pasta, beef, fish, dessert etc.
And I would create each time a new row
From what I understand you already have all your date on the antis and you just want to filter it by type or do you want to OrderIt by a certain type?
This fiddle for example would order by name, but you can also provide an array with functions to retrieve each type in the way that you like, you can read about it here.
But basically you'd do
anti in antis | orderBy:'+tipo'
or
anti in antis | orderBy: [ function(){}, function(){} ]
EDIT:
As #yarons mentioned you can also chain strings to filter even further. I've updated the Fiddle so now the filter would be anti in antis | orderBy:['+tipo', '+piato']" which indicates that first the tipo would be alphabetically ordered ascending (+ indication) and after that the piato would also be alphabetically ascending.
If you'd want to define a different order than the alphabetical one I think you can use a sort of ENUM for the tipo as in:
var tipoENUM = {};
tipoENUM['ANIPASTI'] = 0;
tipoENUM['PASTA'] = 1;
tipoENUM['PIZZA'] = 2;
tipoENUM['BEEF'] = 3;
tipoENUM['DESERT'] = 4;
So that way you'd avoid using the string for the order, see following fiddle for the example.
EDIT 2:
Ok, so if you receive the data via the HTTP request it's better if you create a order function to help you, check this updated fiddle, like so:
// The enum would be defined as before but:
$scope.orderTipo = function (dish) {
return tipoENUM[dish.tipo];
}
On the HTMl you'll do:
ng-repeat="anti in antis | orderBy:[orderTipo, '+piato']"
Ok your example is perfect but I would repeat each time the "tipo" and then the relative "piato" in a list....something like this:
ANTIPASTI
- bruschetta
- suppli
- fritto
PRIMI
- caqrbonara
- amatriciana
etc.
Is it possible?

How to display xml2json data

I am using xml2json to translate a xml file from medline. I am stuck on using ng-repeat to display the information I need. I need to display the title,url and see-reference. When i have one health-topic in the xml file The title and url display fine. When I add more health-topics it does not work. any help is appreciated, thanks
plunkr
<table>
<tr ng-repeat="topic in topics">
<td>{{topic._title}}</td>
<td>{{topic._url}}</td>
<td>{{topic.see-reference}}</td>
</tr>
</table>
<pre>{{topics | json}}</pre>
The structure of your XML is like this:
<health-topics total="1909" date-generated="01/03/2015 02:30:35">
<health-topic title="Abdominal Pain">
</health-topic>
<health-topic title="Abdominal Pain">
</health-topic>
<health-topic title="Abdominal Pain">
</health-topic>
xml2json creates an object for every element. The properties of the object are the element's attributes (starting with an underscore) and the child elements. In your case:
//health-topics
{
_total: "1909",
_date-generated: "01/03/2015 02:30:35",
health-topic: [
{
_title: Abdominal Pain"
...
health-topic is an array containing the topics. If you have a single topic wrapped with health-topics it would be:
//health-topics
{
_total: "1909",
_date-generated: "01/03/2015 02:30:35",
health-topic: {
_title: Abdominal Pain"
...
health-topic is now an object. But maybe a single topic wouldn't even be wrapped with a health-topics element. That's something only you know.
All in all the data structure is different depending on the number of topics. You would need to check and adapt to that. The code working for multiple topics would be
$scope.topics = $scope.dom['health-topics']['health-topic']

Resources