I've got a controller:
.controller('myCtrl', function (myService) {
var that = this;
that.myService = myService;
});
a service:
.service('myService', function (DataFactory) {
var listData = [];
var mockList = ['aaa','bbb','ccc'];
var handleData = function (data) {
listData = data;
};
and html:
<button ng-click="myCtrl.myService.handleData()">Go</button>
<div ng-repeat="item in myCtrl.myService.listData>
{{ item }}
</div>
The thing is: I've got a list which contains data from myService.listData, and (with all code above) it works as expected - by updating list on button click.
I've add:
that.list = myService.listData
and changed
item in myCtrl.myService.listData to item in myCtrl.list
and the whole thing stopped working.
Could you explain that to me? I feel like I'm missing some really basic stuff and I need the direction to start searching more info.
plunks:
working one
broken
Here's a working plunker.
Your misconception was that you expect that.myService to be updated when underlying list is populated on click by handleData().
However, look at your handleData() implementation:
listData = data;
You don't mutate the initial list, you're pointing to another one (therefore referencing a distinct list) since it acts as a complete re-assignement!
So one way to solve the problem would be to mutate the initial list:
var handleData = function (data) {
// populating elements into the initial list
Array.prototype.push.apply(listData, mockedList);
};
Related
I've got a function that takes a container and loops through its objects to perform a search and retrieve an array of the found items.
The ViewModel has a container passed like such e.g.:
function ShowRoomBuildingsViewModel() {
this.LoadModel = function (container) {
for (var u in container.Payload.ShowRoom) {
if (container.Payload.ShowRoom[u].Inventory!== undefined)
this.Content.push(container.Payload.ShowRoom[u].HVehicle, container.Payload.ShowRoom[u].Cars);
}
// Definitions
this.Inventory= ko.observableDictionary();
this.Content= ko.observableArray();
I'm trying to create an ObservableArray object to loop through in my front end using <!-- ko foreach -->. The below code should retrieve either one result or multiple.
this.Content = function (container) {
var concatedList;
Content.forEach(element, elementIndex => {
var Content1 = container.Payload.Inventory.HVehicle.find(el => el.ID == element.ID);
if (Content1 != undefined) {
concatedList[elementIndex] = element;
concatedList[elementIndex].push(Content1);
}
});
return concatedList;
};
I've tried putting breakpoints to see what is happening inside, but it doesn't go in. I'm not sure if this is the correct approach or if it even works, but it is not showing any errors. In the first loop, where it populates Content, I can see that there are five results.
Any suggestions would be much appreciated.
I have a user filter. You can see the mock example below (it returns the same data which it gets, i.e. users_data_contents, in reality there is the function call):
var appModule = angular.module('appModule', [])
.filter('filter_ghosts', function($rootScope){
return function(users_data_contents){ // gets data from view
return users_data_contents;
};
});
I need to use it in async mode, but not quite understand how. I tried something like this:
var appModule = angular.module('appModule', [])
.filter('filter_ghosts', function($rootScope){
function getPromised(){
var cnt = 0, intrv = setInterval(function() {
++cnt;
if (cnt>10) {
return function(users_data_contents){
clearInterval(intrv);
return users_data_contents;
}
}
if (cnt > 50) {
clearInterval(intrv);
}
}, 100);
}
getPromised.$stateful = true;
return getPromised;
});
But this doesn't work.
The html where the filter is applied looks like this:
<tr ng-repeat="user_data in usersList | filter_ghosts">
....
Can anybody help?
UPD
The most close solution to my issue is here: AngularJS : Asynchronously initialize filter
However, it is turned out that the more appropriate approach is not to use filter at all but bind the repeater to array which is changed on corresponding procedures.
Create an empty array in the scope. Then bind your ng-repeat with filter to that array. Then just fill that array with the values when your async operation completes.
This is my controller:
app.controller("PlaceController", ['$http', function($http){
this.places = shops;
var hotels = this;
hotels.objects = [];
this.spots = new Array;
this.newPlace = {};
this.city = new String();
this.addPlace = function() {
this.places.push(this.newPlace);
this.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
});
for (each in hotels.objects.elements) {
this.spots.push(each.tags.name);
};
console.log(this.spots);
}}] );
I get an empty array when I log this.spots to the console. The http request etc work perfectly because the console.log(hotels.objects.elements) statement works perfectly.
Because of this problem, I can't output it into my HTML either. What should I do?
You are issuing an asynchronous request to get the spots, but you're logging them before they complete.
Change this.addPlace to log / act on the spots array inside the promise callback:
this.addPlace = function() {
this.places.push(this.newPlace);
this.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
for (each in hotels.objects.elements) {
this.spots.push(each.tags.name);
};
console.log(this.spots);
});
You're adding to the spots array before the ajax request is done, move your calls to push inside the callback:
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
angular.forEach(hotels.objects.elements, function(value) {
hotels.spots.push(value.tags.name);
});
});
Also, you should really be using $scope instead of references to this. This would simplify your code a bit, without needing to rename this to hotels
full controller code using $scope
app.controller("PlaceController", ['$scope', '$http', function($scope, $http){
$scope.places = shops;
$scope.objects = [];
$scope.spots = new Array;
$scope.newPlace = {};
$scope.city = new String();
$scope.addPlace = function() {
$scope.places.push($scope.newPlace);
$scope.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
$scope.objects = data;
console.log($scope.objects.elements);
angular.forEach($scope.objects.elements, function(value, key) {
$scope.spots.push(value.tags.name);
});
// spots is ready, log it, do whatever
console.log($scope.spots);
});
}}] );
NOTE: Using $scope means you won't need to call this from your html to reference the objects and functions defined in your controller.
An example:
<div ng-controller="PlaceController">
<!-- no need to call places.city, if you use $scope just write city -->
{{city}}
</div>
EDIT: You probably shouldn't use JavaScript's for-in, the problem with it is that it iterates on the names or indexes of your objects/arrays.
An example:
var someArray = ['a', 'b', 'c'];
for (i in someArray) {
console.log(i); // prints 0, 1, 2
console.log(someArray[i]); // prints 'a', 'b', 'c'
}
This is different from any for-in/for-each implementation in other popular languages.
Anyway, in this case I've edited the code above to use Angular's forEach, which is a more appropriate solution (many libraries implement custom for-each functions to fix JS's weird for-in)
You can read more in Angular's docs
Another option, in plain javascript is, if $scope.objects.elements is an array, using the map() function, like this:
$scope.spots = $scope.objects.elements.map(function(value) {
return value.tags.name; // value is an item in object.elements
});
try this ..
due to your async call you need to perform task inside success
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
for (each in hotels.objects.elements) {
hotels.spots.push(each.tags.name);
};
console.log(this.spots);
});
I am using a service(.factory) to hold an array of objects/items which gets updated by multiple controllers and then the data is pulled back down by each controller. Each time I add a new array of data, I push it onto the array. However, when I attempt to load an array within my controller so I can display it my view, it is returning a character array which makes it impossible to use ng-repeat to iterate over the values.
// controllers.js
angular.module('myapp.controllers', [])
.controller('MainCtrl', function($scope, MainService) {
$scope.mainItems = MainService.all(); // Why does this return a char array?
// get the count
$scope.getCount = function(item){
return TempOrder.getCount(item);
}
// add an item
$scope.addItem = function(item){
TempOrder.addItem(item);
return TempOrder.getCount(item);
}
})
.controller('SecondaryCtrl', function($scope, MainService) {
$scope.items = MainService.all(); // Why does this return a char array?
// get the count
$scope.getCount = function(item){
return TempOrder.getCount(item);
}
// add an item
$scope.addItem = function(item){
TempOrder.addItem(item);
return TempOrder.getCount(item);
}
});
This is my service
// services.js
angular.module('myapp.services', [])
.factory('MainService', function() {
orderItems = [];
return {
all: function() {
return orderItems; // Why does return a char array???
},
getCount: function(item){
var count = 0;
for (i = 0;i<orderItems.length;i++) {
if (orderItems[i] === item ){
count++;
}
}
return count;
},
addItem: function(item) {
orderItems.push(item);
}
}
});
I've updated your above code, but for some reasons getting module not found while adding in as a code snippet here.
Refer this fiddle for your solution, I'm not sure what went wrong, but maybe it can be the html where your only passing in item.name or any key or maybe you haven't initialized your $scope.item = {} in the controller and that's why it remains undefined and the angular can't bind to it. Remember Angular won't bind to values that are undefined on controller bootstrap
Thank you to I_Debug_Everything for your help. I realized that in my view I wasn't properly passing the item object. When passed the wrong way, it isn't passed as an object making it impossible to reference attributes of the item. The right way passes the item as an object and therefore I can access stuff like item.name in my functions.
WRONG WAY
<div ng-controller="MainCtrl">
<div ng-repeat="item in items track by $index">
<button ng-click="addItem('{{item}}')">{{Item.name}}</button>
</div>
</div>
RIGHT WAY
<div ng-controller="MainCtrl">
<div ng-repeat="item in items track by $index">
<button ng-click="addItem(item)">{{Item.name}}</button>
</div>
</div>
I have some code which is working to add and remove entries to and from arrays in my scope. Right now the code isn't reused but rather cut/pasted and tweaked. Also, it rather naughtily uses scope inheritance to access the array. I'm trying to create a directive that will fix these two problems. The directive works fine as long as I add entries to the array. As soon as I remove an entry I appear to break the bi-directional binding. Any clues as to how I should go about this?
Fiddle is here.
It shows the SkillsCtrl which is the old code, and ListEditCtrl which is the new (reproduced below from the fiddle). Adding an entry to either list will update both but removing an entry from either list breaks the binding.
function SkillsCtrl($scope) {
$scope.addSkill = function () {
$scope.profile.skills = $scope.profile.skills || [];
$scope.profile.skills.push($scope.newskill);
$scope.newskill = "";
};
$scope.removeSkill = function () {
$scope.profile.skills = _.without($scope.profile.skills, this.skill);
};
}
function ListEditorCtrl($scope) {
$scope.addItem = function () {
$scope.list = $scope.list || [];
$scope.list.push($scope.newitem);
$scope.newitem = "";
};
$scope.removeItem = function () {
$scope.list = _.without($scope.list, this.item);
};
}
It's because you use http://underscorejs.org/#without, which creates a copy of the array instead of just removing the item. When you remove an item a new array will be linked to the scope, and the new array is not linked with array in the isolate scope.
To solve this problem you can use splice instead, which removes the item from the original array:
$scope.removeSkill = function() {
$scope.profile.skills.splice(_.indexOf($scope.profile.skills, this.skill),1);
};
...
$scope.removeItem = function() {
$scope.list.splice(_.indexOf($scope.list, this.item),1);
};
Updated plunker: http://jsfiddle.net/jtjf2/