How can I use ng-click to dynamically reload ng-repeat data? - angularjs

I have a page that contains an ng-repeat directive. The ng-repeat works when the page first loads, but I want to be able to use ng-click to refresh the contents of the ng-repeat. I have tried the following code but it doesn't work. Any suggestions?
<div ng-click="loadItems('1')">Load 1st set of items</div>
<div ng-click="loadItems('2')">Load 2nd set of items</div>
...
<table>
<tr ng-repeat="item in items">>
// stuff
</tr>
</table>
ItemsCtrl:
$scope.loadItems = function (setID) {
$http({
url: 'get-items/'+setID,
method: "POST"
})
.success(function (data, status, headers, config) {
$scope.items = data;
})
.error(function (data, status, headers, config) {
$scope.status = status;
});
};
I was hoping that my call to loadItems() would cause the ng-repeat directive to reload with the new data obtained from my server.

Add a broadcast in your callback and subscribe to it in your controller.
This should really be in a service btw
itemsService.loadItems = function (setID) {
$http({
url: 'get-items/'+setID,
method: "POST"
})
.success(function (data, status, headers, config) {
$scope.items = data;
$rootScope.$broadcast('updateItems', data);
})
.error(function (data, status, headers, config) {
$scope.status = status;
});
};
In your controller:
$scope.$on("updateItems",function(d){
$scope.items = d;
});
So whenever you ng-click="update(id)"
$scope.update = function(id){
itemsService.loadItems(id);
}
Your items will automatically update because it is subscribed.

Related

Update model and HTML after user action

I have the following angular controller:
application.controller('ImageController', function ImageController($scope, ImageService) {
$scope.model = {
images: []
}
var list = function () {
ImageService.GetList()
.success(function (data, status, headers, config) {
$scope.model.images = $scope.model.images.concat(data.Images)
})
.error(function (data, status, headers, config) { });
}
$scope.vote = function (image) {
ImageService.Vote(image)
.success(function (data, status, headers, config) { })
.error(function (data, status, headers, config) { });
};
list();
});
The service returns a list of images as follows:
{"Images": [
{"Key":"22","Url":"http://www.domain.com/i/img22.jpg", "Votes": 120},
{"Key":"88","Url":"http://www.domain.com/i/img88.jpg", "Votes": 428}
]}
I display a list of images each having a vote button and the number of votes.
<div data-ng-repeat='image in model.images'>
<img data-ng-src="{{image.Url}}" alt="" />
VOTE
<span>{{image.Votes}}</span>
</div>
When a user votes an image I would like to disable the vote button, even if only for this page request, and increase the number of votes by one.
PLAN
My idea would be to add a property "HasBeenVoted" as False to each image after being loaded from the service and when the image is voted change it to true and increase its votes by one. And of course sync with the HTML to disable the Vote button and update the {{image.Votes}}
PROBLEM
How to include the HasBeenVoted in all image here:
$scope.model.images = $scope.model.images.concat(data.Images)
How to update HasBeenVoted and Votes Number in the model and HTML here:
$scope.vote = function (image) {
ImageService.Vote(image)
.success(function (data, status, headers, config) { })
.error(function (data, status, headers, config) { });
};
Could someone, please, help me out with this?
If you don't need to check serverside you could just change your JS/HTML as follows:
HTML
<div data-ng-repeat='image in model.images'>
<img data-ng-src="{{image.Url}}" alt="" />
<a ng-disabled="image.HasBeenVoted" href="" data-ng-click="vote(image)">VOTE</a>
<span>{{image.Votes}}</span>
</div>
Javascript
$scope.vote = function (image) {
image.HasBeenVoted = true;
image.Votes += 1;
ImageService.Vote(image)
.success(function (data, status, headers, config) { })
.error(function (data, status, headers, config) { });
};
This works because image.HasBeenVoted will be false initially and true for each image you vote on.
But this does not persist if you navigate. I suggest you extend the service to handle this server side. Your server should be responsible for this. Your response should have the property HasBeenVoted for the logged in user.
Edit: you could do the check in your controller instead, change your HTML + JS as follows:
CSS
.disabled {
opacity: .2;
}
HTML
<div data-ng-repeat='image in model.images'>
<img data-ng-src="{{image.Url}}" alt="" />
<a ng-class="{disabled: image.HasBeenVoted}" href="" data-ng-click="vote(image)">VOTE</a>
<span>{{image.Votes}}</span>
</div>
JS
$scope.vote = function (image) {
if (image.HasBeenVoted) { return; }
image.HasBeenVoted = true;
image.Votes += 1;
ImageService.Vote(image)
.success(function (data, status, headers, config) { })
.error(function (data, status, headers, config) { });
};

Angularjs http call and saving received data to the controller instance

app.controller('sampleCtrl', function($scope, $http, nonService){
this.someVar = 'Hello World';
$http.post('funcsPHP/2.0/getConditionProductService.php',
{ 'product' : angular.toJson(this.productObj) }).
success(function(data, status, headers, config) {
$scope.saveData = data; // works fine
this.saveData = data // 'this' doesnt refer to the controller 'sampleCtrl' anymore, but to the $http instance, right ?
}).
error(function(data, status, headers, config) {
console.log("ERROR");
// log error
});
});
I am aware that I am able to save data to the $scope, but I would like to know how I would be able to save data to a controller variable, such as 'this.someVar'. Do I have to inject an instance of my controller into the $http ?
Cheers !
There are a few ways.
The easiest is to just assign a variable to point to the this value in your controller. The most common names are _this, self, and that.
So you get:
app.controller('sampleCtrl', function($scope, $http, nonService){
this.someVar = 'Hello World';
var self = this;
$http.post('funcsPHP/2.0/getConditionProductService.php',
{ 'product' : angular.toJson(this.productObj) }).
success(function(data, status, headers, config) {
$scope.saveData = data;
self.saveData = data;
})
.error(function(data, status, headers, config) {
console.log("ERROR");
// log error
});
});
The other way is to use Function#bind() for your success handler to set this correctly inside it.
That would give you:
app.controller('sampleCtrl', function($scope, $http, nonService){
this.someVar = 'Hello World';
$http.post('funcsPHP/2.0/getConditionProductService.php',
{ 'product' : angular.toJson(this.productObj) }).
success((function(data, status, headers, config) {
$scope.saveData = data;
this.saveData = data;
}).bind(this))
.error(function(data, status, headers, config) {
console.log("ERROR");
// log error
});
});
Use "Controller As" way of accessing your controllers instance variables:
<div ng-controller="sampleCtrl as ctrl">
{{ctrl.someVar}}
</div>

using promise in http request in angular

the problem is when I use $http or $resource to retrieve data, it is successfully retrieve and bind to view, but when I use promise the data come to client but it does not bind to the view.
here is my code:
//view
<div class="pull-left span6" >
<h3>{{Name}}</h3>
<ul>
<li ng-repeat="type in typeList">
<span>{{type.Title}}</span>
</li>
</ul>
//controller
var proxyControllers = angular.module('httpProxyControllers', []);
proxyControllers.controller('TypeListController',
function TypeListController($scope, typeListData) {
$scope.Name = 'Type List Addresses';
$scope.typeList = typeListData.getTypeList();
$scope.typeList.then(function(data){
console.log('data received');
console.log(data);
},function(status){
console.log(status);
});
});
//service
var proxyServices = angular.module('httpProxyServices', ['ngResource']);
proxyServices.factory('typeListData' , function($http, $q){
return{
getTypeList : function(){
var deferred = $q.defer();
$http({method : 'GET' , url: '/data/getTypeList'})
.success(function(data, status, headers, config){
deferred.resolve(data);
})
.error(function(data, status, headers, config){
deferred.reject(status);
});
return deferred.promise;
}
}
});
in then block of controller when i log data to console, it shows that data comes back successfully but nothing happen to my view and my view shows nothing actually.
thanks in advance,
You need to assign data to $scope.typeList
//initialize with empty array
$scope.typeList = [];
typeListData.getTypeList().then(function(data){
//initialize with received data
$scope.typeList = data;
console.log('data received');
console.log(data);
},function(status){
console.log(status);
});

Angular way to bind model from ng-repeat and send to new function

<li ng-repeat="result in apiResult">
<span class="">{{result.id}}</span>
</li>
<form ng-submit="findone()">
<input type="text" ng-model="searchTerm" value=""></span>
<input class="btn-primary" type="submit" value="go">
</form>
The above is my template.
What is the "angular way" to bind the ng-model searchTerm, to the value of an individual {{result.id}} when a user clicks on this <span>{{result.id}}</span>?
Controller:
var app = angular.module('campaign', []);
function campaignCtrl($scope, $http) {
$scope.search = function() {
$http({method: 'GET', url: "http://localhost:3002/voters.json?token=17975700jDLD5HQtiLbKjwaTkKmZK7zTQO8l5CEmktBzVEAtY&street_name="+$scope.searchTerm}).
success(function(data, status, headers, config) {
$scope.apiResult = data.voters;
}).
error(function(data, status, headers, config) {
});
};
$scope.init = function(id) {
$http({method: 'GET', url: "http://localhost:3002/voters.json?token=17975700jDLD5HQtiLbKjwaTkKmZK7zTQO8l5CEmktBzVEAtY&street_name="+id}).
success(function(data, status, headers, config) {
$scope.apiResult = data.voters;
console.log(data.voters)
}).
error(function(data, status, headers, config) {
});
};
$scope.findone = function(searchTerm) {
$http({method: 'GET', url: "http://localhost:3002/voters.json?token=17975700jDLD5HQtiLbKjwaTkKmZK7zTQO8l5CEmktBzVEAtY&id="+searchTerm}).
success(function(data, status, headers, config) {
console.log(data);
$scope.apiResult = data.voters;
}).
error(function(data, status, headers, config) {
});
};
}
function indyCtrl($scope, $http) {
}
Not exactly sure about the question. I think you are looking for something like this
<li ng-repeat="result in apiResult">
<span class="" ng-click="searchTerm=result.id">{{result.id}}</span>
</li>

How can I track the start and end of a number of async processes with AngularJS?

I have a loading icon set up on my page that looks like this:
<div class="loading-mask"
data-ng-show="action != null">
<span>{{action}} ...</span>
</div>
When I set $scope.action to a message appears in the loading box.
When loading my page I have a number of different async processes that get data. For example I have:
getUserProfiles: function ($scope) {
var url = '/api/UserProfile/GetSelect';
$http({ method: 'GET', url: url })
.success(function (data, status, headers, config) {
$scope.option.userProfiles = data;
})
.error(function (data, status, headers, config) {
alert("Error: No data returned from " + url);
});
},
and:
getSubjects: function ($scope) {
var url = '/api/Subject/GetSelect';
$http({ method: 'GET', url: url })
.success(function (data, status, headers, config) {
$scope.option.subjects = data;
})
.error(function (data, status, headers, config) {
alert("Error: No data returned from " + url);
});
},
How can I make it so that the first of these async processes causes a "Loading" message to appear and the last of the async process causes the loading box to not show any more. Note at this time I am not concerned about error messages. I just want the loading to not show when everything is completed.
To expand on what devmiles has said, but to handle the multiple asynchronous functions, you will want to set a loading flag on your first function to be called. I.e.:
getUserProfiles: function ($scope) {
$scope.loading = true;
var url = '/api/UserProfile/GetSelect';
$http({ method: 'GET', url: url })
.success(function (data, status, headers, config) {
$scope.option.userProfiles = data;
})
.error(function (data, status, headers, config) {
alert("Error: No data returned from " + url);
});
},
And then you will want to wrap each of your asynchronous functions in a promise, like so:
getUserProfiles: function ($scope) {
var deferred = $q.defer();
$scope.loading = true;
var url = '/api/UserProfile/GetSelect';
$http({ method: 'GET', url: url })
.success(function (data, status, headers, config) {
$scope.option.userProfiles = data;
deferred.resolve();
})
.error(function (data, status, headers, config) {
alert("Error: No data returned from " + url);
deferred.reject();
});
return deferred;
},
You can then call $q.all on all of your asynchronous functions, and the success callback of this will occur once all asynchronous functions have resolved:
$q.all([getUserProfiles, getSubjects]).then(function() {
$scope.loading = false;
}
This means once all of your functions have resolved, loading will be set to false.
NB: If you want to access the data of your callbacks, you can pass it in as a parameter of "deferred.resolve(x)", and then in your $q.all callback, it will be available as function(x) { do something with x }.
Hope this helps!
EDIT: Don't forget to pass in angular's promise service, $q, to the controller where your functions are.
Just set some boolean flag on when your controller is being instantiated and reset this flag in your success/error functions.
.controller('MyCtrl', function ( $scope ) {
$scope.isLoading = true;
$http({ method: 'GET', url: url })
.success(function (data, status, headers, config) {
$scope.option.subjects = data;
$scope.isLoading = false;
})
.error(function (data, status, headers, config) {
alert("Error: No data returned from " + url);
$scope.isLoading = false;
});
});
Use ng-show with this flag to show your loading thingy.

Resources