Angular typeahead response error - angularjs

I am implementing an input which allow to select multiple values as tags. I am working with angular-ui-bootstrap-typeahead
The following example with dummy data works fine:
view:
<script type="text/ng-template" id="form_field_ref">
<div class='container-fluid' ng-controller="typeHeadTestCtrl">
<input type="text" ng-model="selected" typeahead="x.formatted_address for x in dynamicSearch($viewValue, field.displayName)" class="form-control" typeahead-on-select='onSelect($item, field)'>
</div>
</script>
part of controller:
$scope.getLocation = function(val) {
return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
params: {
address: val,
sensor: false
}
}).then(function(response){
console.log(response);
return response.data.results.map(function(item){
console.log(item);
//items attribute=> address_components, formatted_address, place_id....
return item;
});
});
};
But when I try to connect to my actual data I get the following error:
TypeError: Cannot read property 'length' of undefined
at ui-bootstrap-tpls.js:3637
at processQueue (angular.js:13170)
at angular.js:13186
at Scope.$eval (angular.js:14383)
at Scope.$digest (angular.js:14199)
at Scope.$apply (angular.js:14488)
at $$debounceViewValueCommit (angular.js:20944)
at $setViewValue (angular.js:20916)
at HTMLInputElement.listener (angular.js:19632)
at HTMLInputElement.eventHandler (angular.js:3011)
Here is the code that fails:
view:
<input type="text" ng-model="selected" typeahead="x.theVal for x in dynamicSearch($viewValue, field.displayName)" class="form-control" typeahead-on-select='onSelect($item, field)'>
the parts of the controller:
dynamicSearch() prepares what data to request on call of getDbRefDocs():
$scope.dynamicSearch = function(searchTerm, name) {
var allowed = {};
var classString = "";
allowed = datamodel.classes[$routeParams.class].attribute[name].allowed;
for (key in allowed){
classString = classString + key + "|";
}
//remove last pipeline
classString = classString.slice(0, -1);
$scope.getDbRefDocs(searchTerm, name, classString);
};
$scope.getDbRefDocs = function(searchTerm, name, classString) {
var url = '/api/v2/docs/' + classString;
return $http.get(url, {
params: {
'>displayName': searchTerm,
count: 5
}
}).then(function(response){
var data = response.data.data;
console.log('data:'+data);
var requested = [];
angular.forEach(data.requested, function(searchTerm, k, o) {
requested.push(createDBOifNecessary(searchTerm));
});
$scope.item=[];
$scope.options=[];
$scope.options[name] = [];
for (key in requested) {
if (requested.hasOwnProperty(key)) {
//This is the storing value
//console.log(requested[key].cid);
//this is the display value
//console.log(requested[key].attributes.displayName[0]);
$scope.options[name][key] = requested[key].attributes.displayName[0];
$scope.item.push({
'theName':requested[key].attributes.displayName[0],
'theVal':requested[key].cid
});
}
}
console.log('item:'+$scope.item);
return $scope.item;
});
};
This last console.log returns the required data correctly!
For what I have been able to read the problem is related to the promise of the server request... but i am stuck!

I am not sure what was failing because i was receiving the expected data.
I think as someone mentioned it could be related to the manipulation of the response, delaying it...
In stead I added an event trigger that updates the array the typeahead attribute reads from and it now works fine. As well the typeahead-wait-ms is required cause my server response is between 20 and 30ms so just to be safe I set it to 200ms.
working code:
view: displays the values of the array "item"(item.theName == x.theName)
<input type="text" ng-model="selected" typeahead="x.theName for x in item" ng-change="dynamicSearch($viewValue, field.displayName)" typeahead-wait-ms="1000" class="form-control" typeahead-on-select='onSelect($item, field)'>
Controller functions:
On ng-change ->dynamicSearch() =>define what data request and call the request
$scope.dynamicSearch = function(searchTerm, name) {
var allowed = {};
var classString = "";
allowed = datamodel.classes[$routeParams.class].attribute[name].allowed;
for (key in allowed){
classString = classString + key + "|";
}
classString = classString.slice(0, -1);
$scope.getDbRefDocs(searchTerm, name, classString);
};
On call of getDbRefDocs() => i define values for the array "item"
$scope.getDbRefDocs = function(searchTerm, name, classString) {
var url = '/api/v2/docs/' + classString;
$http.get(url, {
params: {
'>displayName': searchTerm,
count: 5
}
}).then(function(response){
var data = response.data.data;
var requested = [];
angular.forEach(data.requested, function(searchTerm, k, o) {
requested.push(createDBOifNecessary(searchTerm));
});
$scope.item=[];
for (key in requested) {
if (requested.hasOwnProperty(key)) {
$scope.item.push({
'theName':requested[key].attributes.displayName[0],
'theVal':requested[key].cid
});
}
}
});
};
When item is selected from the available options of "item" => typeahead-on-select='onSelect($item, field)' => I store item.theVal:
$scope.onSelect = function (item, field) {
field.theValues[field.theValues.length] = item.theVal;
};

Related

An empty array is returned when calling $http.get it within a service [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I want to get the name from an array that is being generated from $http.get, however this is returning an empty array. When i do a console it see the array populated however when i loop inside the array to get the value of name property based on whether an id is equal to a certain, the array is empty.
In my controller i have a service call that shall return the name value.
var params = { Id: $scope.Id, SettingId: $scope.SettingId };
$scope.selectedUserName = helloService.getSelectedUserName($scope.UserId, params);
In my service
I have used the getUserList function to populate the list of user in a dropdown and it works by generating the array with the values.
However When i got another page , i want to be able to display the name of the selected user, so I wanted to use the same getUserList function to retrieve the name
this.getUserList = function (val) {
var usersObj = [];
var url = "/api/v1/hello/getusers";
var params = { Id: val.Id, SettingId: val.SettingId };
var config = { params: params };
var promise = $http.get(url, config)
.then(function (response) {
angular.forEach(response.data, function (key, value) {
angular.forEach(key, function (k, index) {
usersObj[index] = ({ userId: k.userId, name: k.name});
});
});
},
function errorCallback(response) {
console.log("Unable to perform get request");
throw response;
});
var usersList = usersObj;
return usersObj;
};
this.getSelectedUserName = function (id, param) {
var name = "";
var userList =this.getUserList(param);
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
Array length is 0 but if i do a console.log(userList) before the loop , the array display the list of user data
this.getSelectedUserName = function (id, param) {
var name = "";
var userList =this.getUserList(param);
console.log(userList) ;
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
Thank you for kind responses.
Please see screenshot
This is simple Javascript, not specific to Angular. You can do
userList.forEach(user => {
if(user.userId === id) {
name = user.name;
}
});
return name;
you can try like this.
here we are using a async await.
Service
this.getUserList = function (val) {
var usersObj = [];
var url = "/api/v1/hello/getusers";
var params = { Id: val.Id, SettingId: val.SettingId };
var config = { params: params };
return new Promise((resolve, reject) => {
$http.get(url, config)
.then(function (response) {
angular.forEach(response.data, function (key, value) {
angular.forEach(key, function (k, index) {
usersObj[index] = ({ userId: k.userId, name: k.name});
});
});
},
function errorCallback(response) {
console.log("Unable to perform get request");
throw response;
});
var usersList = usersObj;
resolve(usersObj);
});
};
this.getSelectedUserName = async function (id, param) {
var name = "";
var userList = await this.getUserList(param);
console.log(userList);
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
let me know if it is working or not.
EDIT:
If you're only trying to match one id in the array of users you don't even need to loop:
anArray = source.filter(source => source.toLowerCase().indexOf(id) === 0);
or
anObject = source.find(obj => obj.id === id);
Which Angular version is this? Your tag denotes 2.+ but you have $scope there which is ng1.x
Why can't you use ngFor in your view since you already have your arrays. You don't need to sort them in the control.
component
this.getSelectedUserName = function (id, param) {
let name = ""; // should be array if you want to add unames to it
let userList = this.getUserList(param);
// what is `angular` here? And why loop here? Use ngFor in view.
angular.forEach(userList, function (value, key) {
if (value.userId == id){
name = value.name; // will be overwritten each time
// should be name.push(value.name); // but loop in view instead
}
});
// this.users = name; // for your original sorted version
this.users = userList;
}
In your view
<li *ngFor="let user of users; index as i;>
{{user.name}}
</li>

$rootScope:infdig error in angularjs 1.4.0

I have the following markup in a form mixed with some asp.net razor:
<div class="account-form__field-container" ng-show="postcodeRequired()" ng-cloak>
#Html.LabelFor(x => x.Postcode)
#Html.TextBoxFor(x => x.Postcode, new { #class = "account-form__field", placeholder = "Postcode here...", ng_required = "postcodeRequired()",ng_validpostcode="", ng_model = "postcode", ng_init = "postcode = '" + Model.Postcode + "'" })
#Html.ValidationMessageFor(x => x.Postcode, null, new { #class = "account-form__error-message" })
<span class="account-form__error-message" ng-show="registrationForm.$submitted && registrationForm.Postcode.$error.required" ng-cloak>
Please enter your postcode
</span>
<span class="account-form__error-message" ng-show="registrationForm.$submitted && !validPostCode()" ng-cloak>
Please enter valid postcode
</span>
</div>
I have a dropdown which will show hide the postcode field, so if uk selected the postcode field will show. The field is required but additionally I am doing a check in whether is a valid postcode via a webservice. The angular controller that deals with form submission looks like:
$scope.submitForm = function () {
$scope.registrationForm.$submitted = true;
if ($scope.enableSubmit()) {
registrationForm.submit();
}
};
$scope.postcodeRequired = function () {
return $scope.country === 'United Kingdom';
};
$scope.validPostCode = function () {
if ($scope.postcodeRequired()) {
if ($scope.postcode !== undefined && $scope.postcode.length > 5) {
postcodeService.ValidatePostCode($scope.postcode).success(function (response) {
return response;
});
} else {
return false;
}
}
return true;
};
$scope.enableSubmit = function () {
return $scope.registrationForm.$valid
&& $scope.passwordsMatch()
&& $scope.acceptTerms
&& $scope.validPostCode();
};
The postCodeService is just doing an http get to validate the post code that returns true or false. The issue i have is on submitting it validates the postcode but then goes into a loop and gives the following error:
angular.min.js:34 Uncaught Error: [$rootScope:infdig] http://errors.angularjs.org/1.4.0/$rootScope/infdig?p0=10&p1=%5B%5D
at angular.min.js:34
at m.$digest (angular.min.js:563)
at m.$apply (angular.min.js:571)
at l (angular.min.js:373)
at O (angular.min.js:388)
at XMLHttpRequest.N.onload (angular.min.js:392)
I have seen other people with this issue when doing an ng-repeat but as you can see I am not doing that.
Any ideas?
Without a plunkr to test against and verify its hard to tell exactly what is causing the infinite digest cycle loop. However I believe it might be cause by the amount of calls made towards your $scope.validPostCode function (which wasn't correctly returning its validity). Basically the change proposed is to only call the validate function when the postcode has been changed (trigged by ng-change on the field). The result of that function sets $scope.validPostCode variable to true or false, which is then what is checked for validity;
HTML (add ng-change to the input)
#Html.TextBoxFor(x => x.Postcode, new { <!-- other attributes -->, ng_change = "validatePostCode()" })
JavaScript
$scope.postcodeRequired = function () {
return $scope.country === 'United Kingdom';
};
// by default its not valid
$scope.validPostCode = false;
// our Validition check
$scope.validatePostCode = function () {
if ($scope.postcodeRequired()) {
if ($scope.postcode !== undefined && $scope.postcode.length > 5) {
postcodeService.ValidatePostCode($scope.postcode).success(function (response) {
$scope.validPostCode = response;
});
} else {
$scope.validPostCode = false;
}
} else {
$scope.validPostCode = true;
}
};
// call our function to properly set the initial validity state.
$scope.validatePostCode();
$scope.enableSubmit = function () {
return $scope.registrationForm.$valid
&& $scope.passwordsMatch()
&& $scope.acceptTerms
&& $scope.validPostCode;
};

Django upload error: Upload a valid image

I am looking for the easiest way to post image via angular and django rest.
I've took some code samples and combined to this, but I am still getting error:
Upload a valid image. The file you uploaded was either not an image or a corrupted image.
Maybe someone has a good eye and could easily see what I am missing here?
P.S.
libjpeg-dev is already the newest version
javascript.js
/* global angular */
var products = angular.module('products',['ngCookies']);
products.config(function($interpolateProvider) {
//allow django templates and angular to co-exist
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
products.run(function($rootScope, $log, $http, $cookies) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies['csrftoken'];
});
products.factory('ModelUtils', function($http, $log) {
var handleErrors = function(serverResponse, status, errorDestination) {
if (angular.isDefined(errorDestination)) {
if (status >= 500) {
errorDestination.form = 'Server Error: ' + status;
} else if (status >= 401) {
errorDestination.form = 'Unauthorized Error: ' + status;
} else {
angular.forEach(serverResponse, function(value, key) {
if (key != '__all__') {
errorDestination[key] = angular.isArray(value) ? value.join("<br/>") : value;
} else {
errorDestination.form = errorDestination.form || '' + key + ':' + angular.isArray(value) ? value.join("<br/>") : value;
}
});
}
}
};
var ModelUtils = {
get: function(url,id) {
$http.get(url + id + '/').then(function(response){response.data});
},
create: function(url, obj, errors) {
//TODO
//obj.author = username;
return $http.post(url, obj).
success(function(response, status, headers, config) {
angular.extend(obj, response);
}).
error(function(response, status, headers, config) {
handleErrors(response, status, errors);
});
},
save: function(url, obj, errors) {
if (angular.isDefined(obj.id)) {
return $http.put(url + obj.id + '/', obj).
success(function(response, status, headers, config) {
angular.extend(obj, response);
});
error(function(response, status, headers, config) {
handleErrors(response, status, errors);
});
} else {
return this.create(url, obj, errors);
}
},
del: function(url, obj) {
console.log(url, obj, obj.id);
return $http.delete(url + obj.id + '/');
}
};
return ModelUtils;
});
products.controller('ListCtrl', function ListCtrl($scope, $log, $http, ModelUtils) {
// dcl variables
$scope.tempUrlJs = document.getElementById("tempVar1").value;
// just a dummy init function
$scope.initialize = function(data) {
$log.log('initialize',data);
$scope.initData = data;
};
$scope.loaditems = function() {
console.log('hello');
$scope.items = $http.get('/api/images/').then(function(response){
return response.data;
});
};
//mainFlow
$scope.loaditems($scope.tempUrlJs);
$scope.currentitem = {};
$scope.errors = {};
console.log('hello');
$scope.saveitem = function() {
console.log('hello');
ModelUtils.save('/api/images/',$scope.currentitem, $scope.errors).then(function(){
$scope.loaditems();
$scope.currentitem = {};
});
};
$scope.delitem = function(item) {
console.log('0');
ModelUtils.del('/api/images/',item).then(function(){
console.log('10');
$scope.loaditems();
});
};
});
products.controller('subListCtrl', function ListCtrl($scope, $log, $http, ModelUtils) {
// dcl variables
$scope.tempUrlJs = '/api/ingredients/'
//document.getElementById("tempVar2").value;
// just a dummy init function
$scope.initialize = function(data) {
$log.log('initialize',data);
$scope.initData = data;
};
$scope.loaditems = function(tempUrlJs) {
$scope.items = $http.get(tempUrlJs).then(function(response){
return response.data;
});
};
$scope.saveitem = function() {
ModelUtils.save('/api/ingredients/',$scope.currentitem, $scope.errors).then(function(){
$scope.loaditems();
$scope.currentitem = {};
});
};
//mainFlow
$scope.loaditems($scope.tempUrlJs);
$scope.currentitem = {};
$scope.errors = {};
});
index.html
<body ng-app="products">
<div ng-controller="ListCtrl">
<h3>Insert image</h3>
<form>
<ul>
<li>Title: <input type="text" name="title" ng-model="currentitem.title"/><span class="error">{[{ errors.title }]}</span></li>
<li>Description: <input type="text" name="description" ng-model="currentitem.description"/><span class="error">{[{ errors.description }]}</span></li>
<li>Image: <input type="file" name="image" ngf-select ng-model ="currentitem.image" accept="image/*"/><span class="error">{[{ errors.image }]}</span></li>
</ul>
<button ng-click="saveitem()">Save</button>
<pre>currentitem:{[{ currentitem | json }]}</pre>
</form>
serializers.py
class Base64ImageField(serializers.ImageField):
def to_internal_value(self, data):
from django.core.files.base import ContentFile
import base64
import six
import uuid
def decode_base64(data):
missing_padding = len(data) % 4
if missing_padding != 0:
data += b'='* (4 - missing_padding)
return base64.decodestring(data)
if isinstance(data, six.string_types):
if 'data:' in data and ';base64,' in data:
header, data = data.split(';base64,')
try:
data = decode_base64(data)
decoded_file = base64.b64decode(data)
#decoded_file = decode_base64(decoded_file)
except TypeError:
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension, )
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
class UploadedImageSerializer(serializers.ModelSerializer):
image = Base64ImageField(
max_length=None, use_url=True,
)
class Meta:
model = image
fields = ('pk', 'image', 'thumbnail', 'title', 'description', )
read_only_fields = ('thumbnail',)
views.py
class UploadedImages(TemplateView):
template_name = 'image.html'
queryset = image.objects.all()
serializer_class = UploadedImageSerializer
class UploadedImagesApi(generics.ListCreateAPIView):
queryset = image.objects.all()
model = image
serializer_class = UploadedImageSerializer
models.py
def scramble_uploaded_filename(instance, filename):
extension = filename.split(".")[-1]
return "{}.{}".format(uuid.uuid4(), extension)
def create_thumbnail(input_image, thumbnail_size=(256, 256)):
if not input_image or input_image == "":
return
image = Image.open(input_image)
image.thumbnail(thumbnail_size, Image.ANTIALIAS)
filename = scramble_uploaded_filename(None, os.path.basename(input_image.name))
arrdata = filename.split(".")
extension = arrdata.pop()
basename = "".join(arrdata)
new_filename = basename + "_thumb." + extension
image.save(os.path.join(settings.MEDIA_ROOT, new_filename))
return new_filename
class image(models.Model):
image = models.ImageField("Uploaded image", upload_to=scramble_uploaded_filename)
thumbnail = models.ImageField("Thumbnail of uploaded image", blank=True)
title = models.CharField("Title of the uploaded image", max_length=255, default="Unknown Picture")
description = models.TextField("Description of the uploaded image", default="")
def __str__(self):
return self.title
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.thumbnail = create_thumbnail(self.image)
super(image, self).save(force_update=force_update)
I don't know angular so this might be completely wrong, but don't you always need enctype="multipart/form-data" in your <form> tag to upload images?

How to check string in json array

console.log($scope.filteredOffers);hi to all want to check string in json like that string coming form params now want to check in whole json and display only data which is related to that . Kindly check my code .. in other words want to display data according to the params which is coming form url (which is coming ).
.controller('mixCtrl', function($scope,$http,$stateParams) {
$http.get("http://tools.vcommission.com/api/coupons.php?apikey=e159f64e3dd49fddc3bb21dcda70f10c6670ea91aac30c7cb1d4ed37b20c45b8").then(function (response) {
$scope.myData = response.data;
$scope.offerName = ''; //set initially
$scope.selectedIndex = -1;
$scope.filteredOffers = [];
// $scope.link1 = [];
$scope.da = $stateParams.offer_name;
var a = $scope.da;
console.log(a);
$scope.filteredOffers = $scope.myData.filter(function(a) {
for (var i=0;i<$scope.myData.length;i++)
{
$link =$scope.myData[i].offer_name;
if (a==$link)
{
return a ;
console.log(a );
}
//console.log(a );
}
// return offer.offer_name == $scope.da;
console.log($scope.da);
});
});
/*
$scope.showData = function(offer_name, index) {
$scope.offerName = offer_name;
$scope.filteredOffers = $scope.myData.filter(function(offer) {
return offer.offer_name == $scope.offerName;
});
$scope.selectedIndex = index;
}*/
$scope.dealopen = function(a){
for (var i=0;i<$scope.myData.length;i++)
{
//console.log($scope.data[i].name);
$link=$scope.data[i].name;
console.log($link);
if ($link==$a)
{
$window.open($link,"_self","location=yes");
//console.log($a);
}
}
}
})
Html
<div ng-repeat="offer in filteredOffers">
<div class="couponCode">{{offer.coupon_code}}</div>
<div class="couponTitle">{{offer.coupon_title}}</div>
<div class="couponDescription">{{offer.coupon_Description}}</div>
</div>
You are use the Array.filter() function wrongly!
Before :
$scope.filteredOffers = $scope.myData.filter(function(a) {
for (var i=0;i<$scope.myData.length;i++)
{
$link =$scope.myData[i].offer_name;
if (a==$link)
{
return a ;
console.log(a );
}
//console.log(a );
}
// return offer.offer_name == $scope.da;
console.log($scope.da);
});
After (According to correct syntax) :
$scope.filteredOffers = $scope.myData.filter(function(a) {
if (check condition)
{
return true ;// when true is returned the json held in a gets pushed into the filteredOffers array
}
else{
return false;//when false is returned, the json held in 'a' is ignored and not pushed into the filteredOffers array
}
});
Array.filter's Doc Link
But there is something wrong with the logic that you have used. If you can tell me exactly what you are trying to do, I might be able to help you out.
If you are processing json and arrays frequently in your application, then it's better to integrate loadash or underscore in you application.
Add below code to your HTML before loading the controller file.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
After looking the response of http://tools.vcommission.com/api/coupons.php?apikey=e159f64e3dd49fddc3bb21dcda70f10c6670ea91aac30c7cb1d4ed37b20c45b8, below code will give you the array that matches the offer_name.
$http.get("http://tools.vcommission.com/api/coupons.php?apikey=e159f64e3dd49fddc3bb21dcda70f10c6670ea91aac30c7cb1d4ed37b20c45b8").then(function (response) {
$scope.filteredOffers = [];
var offerName = $stateParams.offer_name;
$scope.filteredOffers = _.filter(response.data, ["offer_name",offerName]);
})

Switch between 2 ng-shows

I have two elements with a ng-show in them,
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
%a.unfollow{"ng-click" => "unfollowUser(user)", "ng-show" => "isFollowed(user.id)"} unfollow
It depends on the user.id which ng-show is being rendered in the template. So only one of the two ng-shows is displayed.
So for example a user wants to start following another user. Then the follow link is displayed.
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
When a user clicks on it, I would like to hide the clicked ng-show, and show the unfollow ng-show so that the user can unfollow the just followed user.
The follow and unfollow user function,
$scope.followUser = function (user) {
followUser.create({
followed_id: user.id
}).then(init);
Notification.success(user.name + ' is toegevoegd als vriend.');
}
$scope.unfollowUser = function(user){
unfollowUser.unfollowUser(user).then(function(){
},function(){
}).then(init);
Notification.success(user.name + ' is verwijderd als vriend.');
}
And the isFollowed function,
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
console.log ($scope.users)
angular.forEach(response, function(user){
$scope.user = user
$scope.isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i=0; i<following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
})
})
I've tried building this,
<a ng-click="follow=false ;unfollow=true", ng-show="follow">Follow!</a>
<a ng-click="follow=true; unfollow=false", ng-show="unfollow">Unfollow!</a>
This does switch between the two ng-shows, but when I try to get the isFollowed(user.id), !isFollowed(user.id) in them the code crashes.
You should create single function to follow/unfollow, Here in the code snippet I have introduced a new property i.e. isFollowed to object user whose value is set using the isFollowed function.
Additionally, Don't overuse isFollowed(user.id) method, it will be huge performance hit.
HTML
<a ng-click="followUnfollowUser(user)"> {{ user.isFollowed : "Unfollow!" : "Follow!"}} </a>
Script
$scope.followUnfollowUser = function(user) {
//If followed - unfollow
if (user.isFollowed) {
unfollowUser.unfollowUser(user).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
}).then(init);
Notification.success(user.name + ' is verwijderd als vriend.');
} else {
followUser.create({
followed_id: user.id
}).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
}).then(init);
Notification.success(user.name + ' is toegevoegd als vriend.');
}
}
//Define method to check wheather current user is beign followed
var isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i = 0; i < following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
//Fetch Users
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
//Iterate and create isFollowed property
angular.forEach($scope.users, function(user) {
user.isFollowed = isFollowed(user.id);
})
})
Note: I'm not familiar with following syntax thus used standard HTML.
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
Alrhgout Satpal did point me to the right direction and helped me with some code. His answer isn't complete. So I've decided that add the code I'm using for this function (made with the help of Satpal!).
I've created a followUnfollowUser function. But instead of having two .then(init) I have one init() at the end of the function. Having the two inits gave me some looping trouble.
$scope.followUnfollowUser = function(user) {
//If followed - unfollow
if (user.isFollowed) {
unfollowUser.unfollowUser(user).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
})
Notification.success(user.name + ' is verwijderd als vriend.');
} else {
followUser.create({
followed_id: user.id
}).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
})
Notification.success(user.name + ' is toegevoegd als vriend.');
}
init();
}
Then the init function,
var init = function () {
loadCurrent_user.loadCurrent_user().then(function(response) {
$scope.current_user = response.data;
});
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
//Iterate and create isFollowed property
angular.forEach($scope.users, function(user) {
user.isFollowed = isFollowed(user.id);
})
})
var isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i = 0; i < following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
}
First I load the current user so that the $scope.current_user gets updated when a user is being followed/unfollowed. And then I iterate through each user and create the isFollowed value using the isFollowed function.
And in my template I have,
%a{"ng-click" => "followUnfollowUser(user)"}
-# {{ user.isFollowed }}
{{ user.isFollowed ? "Unfollow user" : "Follow user"}}

Resources