I have a table name Post, the function below Posts.query give me all the post and stock them in a variable postsATraiter.
Posts.query({}, function() {
$scope.postsATraiter = $scope.posts;
});
This works fine I can do a :
console.log($scope.postsATraiter.length);
This give me the number of post who are in my table, but now I would like to display the value inside my postsATraiter ( date for exemple ).
I try this :
console.log(postsATraiter.valueOf(1).date);
This is not working, I think valueOf is not the correct function for get one element, but I don't know which one I need to use. Thanks for answer
You can iterate through the array of objects and print the needed property value as
$scope.postsATraiter.forEach(function(element) {
console.log(element.date);
});
The function is called for each object in the array.
To access the first element's date property you can simply do
$scope.postsATraiter[0].date
Related
I am new to React JS & currently trying to iterate a certain data to present the same in react js but not able to do the same. The data which looks something like this
Now, the final output should be look something like this in tabular format
The things which I tried are:-
The error which I am getting below one is for 1 image and for second, the code is not getting parsed.
[![error][5]][5]
So, how to achieve the desired output in react js
It looks like you're trying to use map onto an object, while you should use it on a collection. Maybe try something like this :
Object.values(listData.data).map((meetingRoom) => { // your code here });
This will allow you to use the content inside your data object as an array of objects.
Edit : Sorry, I didn't understand you need to access the key as well as the value. To achieve that you can simply use Object.entries which will return the key (Meeting Room 1, Meeting Room 2 in this instance) in the first variable and the array of items in the second variable.
Here's a quick example:
Object.entries(listData.data).forEach(([key, value]) => {
console.log(key, value);
// You could use value.map() to iterate over each object in your meeting room field array.
});
Note : you can also use a for (... of ...) loop like this instead of a forEach :
for (const [key, value] of Object.entries(listData.data)) {
console.log(key, value);
};
For more information about the Object.entries method, feel free to check the MDN Webdocs page about it here.
vm.Parameters is a list of Parameter objects (vm is an alias for the controller).
Each Parameter has at least these 3 properties (to keep it simple):
param.Name
param.Dependensies
param.Values
Parameter may have dependency on another Parameter, for example, we have 3 parameters (Country, Region and City).
Region depends on Country, and City depends on Region and Country, like this:
vm.Parameters['Region'].Dependencies = ['Country'];
vm.Parameters['City'].Dependencies = ['Country', 'Region'];
When I render UI, I generate dropdowns for each parameter.
When country is selected, I need to populate Region dropdown with regions of selected country.
When region is selected, I need to populate City dropdown with cities of selected region and country.
Question: I want to know if it is possible to use $scope.$watch so that each child parameter watches for changes in parent parameters (param.Values property), listed in param.Dependencies.
I am not sure how exactly this should be implemented.
I added this function to the controller, that loops thru all the parameters in the list, and for each parameter it loops thru all the dependencies (names of parent parameters this parameter depends on, like Country and Region for City)
cascadeReportParameters() {
for (let param of this.reportParameters) {
for (let parentParam of param.Dependencies) {
this.$scope.$watch(parentParam, function (newValue, oldValue) {
this.getDependentParameterValues(param);
});
};
}
}
This function doesnt work.
According the documentation, first param is a string name of controller's property being watched.
So, if I had a property Property1, I could write
this.$scope.$watch('Property1', function (newValue, oldValue){}
However in my case I need to watch for Parameters['SomeName'].Values and I dont know how to set this watch. I am not sure what should be the first parameter to $watch function.
Any help is appreciated.
When used that way, $watch expects a scope variable. Notice the string notation in this example:
$scope.somevariable = 1;
$scope.$watch('somevariable', function(vNew, vOld) {
alert('somevariable has changed');
});
But you can watch a function instead. When watching a function, the watch is set on the function's return value, which can be anything and does not need to be a scope variable:
$scope.$watch(function(){
// return whatever value you'd like to watch
return Parameters['SomeName'].Values;
}, function(vNew, vOld) {
alert('The watch value has changed');
});
Hope that helps. Note that the function watch will be called multiple times per digest, which could potentially create performance issues.
EDIT: This answer: add watch on a non scope variable in angularjs also shows a bind syntax that might help further readability for controllerAs syntax, but it shouldn't be necessary.
I've a function which deletes individual items from the array, based on the value i pass. Here's the flow:- I call a function on click of a button deleteImage('{{image}}') and in the controller i've function defined. Here's the function
$scope.deleteImage = function(image) {
console.log(image); // Output : {{image}} instead of image url
// code to delete items from array
}
The problem here is when i checked the console, i can see the image value in the element attribute ng-click="deleteImage('url_of_the_image')", but in controller, the image variable is having the value {{image}}. What could be the issue here ?
you do not need to interpolate the value.
ng-click="deleteImage(image)"
should be enough to fix your issue.
I receive data from my back end server structured like this:
{
name : "Mc Feast",
owner : "Mc Donalds"
},
{
name : "Royale with cheese",
owner : "Mc Donalds"
},
{
name : "Whopper",
owner : "Burger King"
}
For my view I would like to "invert" the list. I.e. I want to list each owner, and for that owner list all hamburgers. I can achieve this by using the underscorejs function groupBy in a filter which I then use in with the ng-repeat directive:
JS:
app.filter("ownerGrouping", function() {
return function(collection) {
return _.groupBy(collection, function(item) {
return item.owner;
});
}
});
HTML:
<li ng-repeat="(owner, hamburgerList) in hamburgers | ownerGrouping">
{{owner}}:
<ul>
<li ng-repeat="burger in hamburgerList | orderBy : 'name'">{{burger.name}}</li>
</ul>
</li>
This works as expected but I get an enormous error stack trace when the list is rendered with the error message "10 $digest iterations reached". I have a hard time seeing how my code creates an infinite loop which is implied by this message. Does any one know why?
Here is a link to a plunk with the code: http://plnkr.co/edit/8kbVuWhOMlMojp0E5Qbs?p=preview
This happens because _.groupBy returns a collection of new objects every time it runs. Angular's ngRepeat doesn't realize that those objects are equal because ngRepeat tracks them by identity. New object leads to new identity. This makes Angular think that something has changed since the last check, which means that Angular should run another check (aka digest). The next digest ends up getting yet another new set of objects, and so another digest is triggered. The repeats until Angular gives up.
One easy way to get rid of the error is to make sure your filter returns the same collection of objects every time (unless of course it has changed). You can do this very easily with underscore by using _.memoize. Just wrap the filter function in memoize:
app.filter("ownerGrouping", function() {
return _.memoize(function(collection, field) {
return _.groupBy(collection, function(item) {
return item.owner;
});
}, function resolver(collection, field) {
return collection.length + field;
})
});
A resolver function is required if you plan to use different field values for your filters. In the example above, the length of the array is used. A better be to reduce the collection to a unique md5 hash string.
See plunker fork here. Memoize will remember the result of a specific input and return the same object if the input is the same as before. If the values change frequently though then you should check if _.memoize discards old results to avoid a memory leak over time.
Investigating a bit further I see that ngRepeat supports an extended syntax ... track by EXPRESSION, which might be helpful somehow by allowing you to tell Angular to look at the owner of the restaurants instead of the identity of the objects. This would be an alternative to the memoization trick above, though I couldn't manage to test it in the plunker (possibly old version of Angular from before track by was implemented?).
Okay, I think I figured it out. Start by taking a look at the source code for ngRepeat. Notice line 199: This is where we set up watches on the array/object we are repeating over, so that if it or its elements change a digest cycle will be triggered:
$scope.$watchCollection(rhs, function ngRepeatAction(collection){
Now we need to find the definition of $watchCollection, which begins on line 360 of rootScope.js. This function is passed in our array or object expression, which in our case is hamburgers | ownerGrouping. On line 365 that string expression is turned into a function using the $parse service, a function which will be invoked later, and every time this watcher runs:
var objGetter = $parse(obj);
That new function, which will evaluate our filter and get the resulting array, is invoked just a few lines down:
newValue = objGetter(self);
So newValue holds the result of our filtered data, after groupBy has been applied.
Next scroll down to line 408 and take a look at this code:
// copy the items to oldValue and look for changes.
for (var i = 0; i < newLength; i++) {
if (oldValue[i] !== newValue[i]) {
changeDetected++;
oldValue[i] = newValue[i];
}
}
The first time running, oldValue is just an empty array (set up above as "internalArray"), so a change will be detected. However, each of its elements will be set to the corresponding element of newValue, so that we expect the next time it runs everything should match and no change will be detected. So when everything is working normally this code will be run twice. Once for the setup, which detects a change from the initial null state, and then once again, because the detected change forces a new digest cycle to run. In the normal case no changes will be detected during this 2nd run, because at that point (oldValue[i] !== newValue[i]) will be false for all i. This is why you were seeing 2 console.log outputs in your working example.
But in your failing case, your filter code is generating a new array with new elments every time it's run. While this new array's elments have the same value as the old array's elements (it's a perfect copy), they are not the same actual elements. That is, they refer to different objects in memory that simply happen to have the same properties and values. Hence in your case oldValue[i] !== newValue[i] will always be true, for the same reason that, eg, {x: 1} !== {x: 1} is always true. And a change will always be detected.
So the essential problem is that your filter is creating a new copy of the array every time it's run, consisting of new elements that are copies of the original array's elments. So the watcher setup by ngRepeat just gets stuck in what is essentially an infinite recursive loop, always detecting a change and triggering a new digest cycle.
Here's a simpler version of your code that recreates the same problem: http://plnkr.co/edit/KiU4v4V0iXmdOKesgy7t?p=preview
The problem vanishes if the filter stops creating a new array every time it's run.
New to AngularJS 1.2 is a "track-by" option for the ng-repeat directive. You can use it to help Angular recognize that different object instances should really be considered the same object.
ng-repeat="student in students track by student.id"
This will help unconfuse Angular in cases like yours where you're using Underscore to do heavyweight slicing and dicing, producing new objects instead of merely filtering them.
Thanks for the memoize solution, it works fine.
However, _.memoize uses the first passed parameter as the default key for its cache. This could not be handy, especially if the first parameter will always be the same reference. Hopefully, this behavior is configurable via the resolver parameter.
In the example below, the first parameter will always be the same array, and the second one a string representing on which field it should be grouped by:
return _.memoize(function(collection, field) {
return _.groupBy(collection, field);
}, function resolver(collection, field) {
return collection.length + field;
});
Pardon the brevity, but try ng-init="thing = (array | fn:arg)" and use thing in your ng-repeat. Works for me but this is a broad issue.
I am not sure why this error is coming but, logically the filter function gets called for each element for the array.
In your case the filter function that you have created returns a function which should only be called when the array is updated, not for each element of the array. The result returned by the function can then be bounded to html.
I have forked the plunker and have created my own implementation of it here http://plnkr.co/edit/KTlTfFyVUhWVCtX6igsn
It does not use any filter. The basic idea is to call the groupBy at the start and whenever an element is added
$scope.ownerHamburgers=_.groupBy(hamburgers, function(item) {
return item.owner;
});
$scope.addBurger = function() {
hamburgers.push({
name : "Mc Fish",
owner :"Mc Donalds"
});
$scope.ownerHamburgers=_.groupBy(hamburgers, function(item) {
return item.owner;
});
}
For what it's worth, to add one more example and solution, I had a simple filter like this:
.filter('paragraphs', function () {
return function (text) {
return text.split(/\n\n/g);
}
})
with:
<p ng-repeat="p in (description | paragraphs)">{{ p }}</p>
which caused the described infinite recursion in $digest. Was easily fixed with:
<p ng-repeat="(i, p) in (description | paragraphs) track by i">{{ p }}</p>
This is also necessary since ngRepeat paradoxically doesn't like repeaters, i.e. "foo\n\nfoo" would cause an error because of two identical paragraphs. This solution may not be appropriate if the contents of the paragraphs are actually changing and it's important that they keep getting digested, but in my case this isn't an issue.
I am learning knockout and was trying to build a page that will build a list of selectable users.
JSFiddle: http://jsfiddle.net/Just/XtzJk/3/ (I am unable to get the data assignment right).
The data assignment is working in my page as I make a call to Controller, like below and it binds to the controls as expected
$.getJSON("/Wizard/GetUsers",function(allData){
var mappedUsers = $.map(allData.AllUsers, function(item){return new User(item)});
self.AllUsers(mappedUsers);
if(allData.SelectedUsers != null){
var mappedSelectedUsers = $.map(allData.SelectedUsers, function(item){return new User(item)});
self.SelectedUsers(mappedSelectedUsers);}
});
Problems:
a.) What's wrong with the JSFiddle I wrote? Got it working.
b.) In my code I am able to get the function for selected checkbox invoked but I am unable to get the value stored in the "User" parameter that I receive in the function. In Chrome JS console I can see the user object has the right value stored, I just am unable to retrieve it. Got this by doing ko.toJS().
Thanks.
EDIT:
Ok, I got my JSFiddle working, I had to select Knockout.js in the framework. The updated fiddle: http://jsfiddle.net/Just/XtzJk/5/
Also, for getting the selected checkboxe's value I did
ko.toJS(user).userName
But I think I'll take the approach of selecting values from a list and then on click move them to another "Selected" list and remove the values from the previous ones. Got this idea from this post: KnockoutJS: How to add one observableArray to another?
OK, I think I've got the solution you need...
I started by setting up an observable array of selectedUserNames, and I applied this to the <li> elements like this:
<input type="checkbox"
name="checkedUser"
data-bind="value: userName, checked:$root.selectedUserNames" />
[Note: it's important to declare the value before declaring the checked binding, which threw me for a bit… ya learn something new every day!]
Why bind an array of userName values to the checked binding? Well, when an array is passed to the checked binding, KO will compare the value of each checkbox to the values in the checked array and check any checkbox where its value is in that array. (Probably explained better in the KO documentation)
Then, while I left the observableArray for SelectedUsers, I set up a manual subscription to populate it, like so:
self.selectedUserNames.subscribe(function(newValue) {
var newSelectedUserNames = newValue;
var newSelectedUsers = [];
ko.utils.arrayForEach(newSelectedUserNames, function(userName) {
var selectedUser = ko.utils.arrayFirst(self.AllUsers(), function(user) {
return (user.userName() === userName);
});
newSelectedUsers.push(selectedUser);
});
self.SelectedUsers(newSelectedUsers);
});
[I had originally tried to set up a dependent observable (ko.computed) for selectedUserNames with functions for both read and write, but the checkbox wasn't having it.]
This subscription function examines the new selectedUserNames array, looks up the user from AllUsers whose userName matches a value in that selectedUserNames array, and pushes matching User objects to the SelectedUsers array… well, actually it pushes each matching User to a temp array and then that temp array is assigned to SelectedUsers, but the goal is met. The SelectedUsers array will now always contain what we want it to contain.
Oh, I almost forgot… here's the fiddle I created, so you've got the full solution: http://jsfiddle.net/jimmym715/G2hxP/
Hope this helps, but let me know if you have any questions