Update model and HTML after user action - angularjs

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) { });
};

Related

AngularJs - unable to set ng-class to a specific element according to a server response

I'm using ng-class in my app to set some styles on the fly to a clicked element, depending on the response from the server. So far, I'm only able to set the style to all elements at once (I guess due to same scope?), generated by ng-repeat, instead of only the clicked one.
I read here, but was unable to combine that with a dynamic response from the server.
The scenario is a follows, unknown amount items with two buttons in each, publish and unpublish. The style is set only to the publish button, according to user selection (unpublish removes something from the db and sets btn-publish class to this button, while click on publish adds item to the db and sets the publishedBtn class to the publish button).
EDIT: I added a "simulation" of the behavior, jsfiddle here.
HTML:
<div ng-repeat="item in items">
<button class="btn btn-publish" ng-click="publish(item.id)" ng-class="(pub==true) ? 'publishedBtn' : 'btn-publish'">publish</button>
<button class="btn btn-publish" ng-click="unPublish(item.id)">unpublish</button>
</div>
JS code:
$scope.publish = function(postId) {
$http({
url: "someUrl",
method: "POST",
headers:{'someHeader'},
data: $.param({publish: postId})
}).success(function(data, status, headers, config) {
if ($.trim(data) == "published") {
$scope.pub = true;
console.log("published");
}
}).error(function(data, status, headers, config) {});
}
$scope.unPublish = function(postId){
$http({
url: "someUrl",
method: "POST",
headers:{'someHeader'},
data: $.param({unPublish:postId})
}).success(function(data, status, headers, config) {
if($.trim(data)!="published"){
$scope.pub = false;
console.log("un-published");
}
}).error(function(data, status, headers, config) {});
}
Any ideas?
This is not how ng-class works. If you want to use it correctly, your HTML should look like:
<button class="btn" ng-click="publish(item.id)" ng-class="{'publishedBtn': pub,'btn-publish': !pub}">publish</button>
EDIT
Now that I understand the problem better, here is a complete solution.
The reason all your publish buttons got changes is the one you guessed: you save the pub value on the scope. The solution is easy: save it on the item.
Consider this solution:
HTML:
<div ng-repeat="item in items">
<button class="btn btn-publish" ng-click="publish(item)" ng-class="{'publishedBtn': item.pub,'btn-publish': !item.pub}">publish</button>
<button class="btn btn-publish" ng-click="unPublish(item)">unpublish</button>
</div>
JS code:
$scope.publish = function(item) {
$http({
url: "someUrl",
method: "POST",
headers:{'someHeader'},
data: $.param({publish: item.id})
}).success(function(data, status, headers, config) {
if ($.trim(data) == "published") {
item.pub = true;
console.log("published");
}
}).error(function(data, status, headers, config) {});
}
$scope.unPublish = function(item){
$http({
url: "someUrl",
method: "POST",
headers:{'someHeader'},
data: $.param({unPublish:item.id})
}).success(function(data, status, headers, config) {
if($.trim(data)!="published"){
item.pub = false;
console.log("un-published");
}
}).error(function(data, status, headers, config) {});
}
See improved fiddle

Cascading Dropdown Binding On Edit Click Angular

I want to bind dropdowns in edit mode but with value selected according to each record
My Edit View
<select ng-model="user.StateId" ng-init="user.StateId=#Model.StateId" data-ng-options="s.Id as s.State for s in States " data-ngchange="GetCities()"></select>
<select ng-model="user.CityId" data-ng-options="c.Id as c.City for c in Cities " ></select>
My Angular Js
function GetStates() {
$http({
method: 'Get',
url: '/Home/GetStates'
}).success(function (data, status, headers, config) {
$scope.States = data;
}).error(function (data, status, headers, config) {
$scope.message = 'Unexpected Error';
});
}
$scope.GetCities = function (obj) {
var stateId = $scope.user.StateId;
alert(stateId);
if (stateId) {
$http({
method: 'POST',
url: '/Home/GetCities/',
data: JSON.stringify({ stateId: stateId })
}).success(function (data, status, headers, config) {
$scope.Cities = data;
}).error(function (data, status, headers, config) {
$scope.message = 'Unexpected Error';
});
}
else {
$scope.states = null;
}
}
$scope.edit = function (user) {
var ref = user;
$http
({
method: 'GET',
dataType: 'html',
url: location.href = '../Home/Edit?Id=' + ref.Id,
})
}
Now when user click on edit i want to open user details in edit mode and i m doing so using $scope.edit user is my object in which i m getting data to edit now i want dropdown of edit view to show state and city selected as per the state and city i got as a response in function(user)

Disallow multiple votes on image on an angular list

I have the following Angular and HTML code to display a list of images and allow voting:
<script type="text/javascript">
var application = angular.module('Application', []);
application.service('ImageService', function ($http) {
return {
GetList: function (page) {
return $http.get('api/images', { params: { page: page } });
},
Vote: function (image) {
return $http.post('api/images/{key}/vote', { key: image.Key });
}
}
});
application.controller('ImageController', function ImageController($scope, ImageService) {
var page = 0;
$scope.images = [];
ImageService.GetList(page)
.success(function (data, status, headers, config) {
$scope.images = $scope.images.concat(data);
})
.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) { });
};
});
</script>
<div data-ng-app="Application" data-ng-controller="ImageController" class="gallery">
<div data-ng-repeat='image in images' class="image">
<img src="{{image.Url}}" alt="" />
<i class="icon-heart"></i>
<span>{{image.Votes}}</span>
</div>
</div>
Each image has an unique id, image.Id.
How can I disallow a user to vote the same image twice?
Use ng-hide to hide link if hasVoted is true.
<div class="gallery">
<div data-ng-repeat='image in images' class="image">
<img src="{{image.Url}}" alt="" />
<i class="icon-heart"></i>
<span>{{image.Votes}}</span>
</div>
</div>
Then modify controller to set voted to true. If it fails we will set it back to false. The reason to do it is to prevent multiple clicks on the button until we receive success back from the server:
$scope.vote = function (image) {
image.hasVoted = true;
ImageService.Vote(image)
.success(function (data, status, headers, config) { })
.error(function (data, status, headers, config) { image.hasVoted = false; });
};
The simplest tweak on the client side would be something like this:
$scope.vote = function (image) {
if (!image.voted) {
ImageService.Vote(image)
.success(function (data, status, headers, config) { image.voted = true })
.error(function (data, status, headers, config) { });
}
};
Simply flag the image as voted.

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 use ng-click to dynamically reload ng-repeat data?

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.

Resources