I have an ng-repeat of employees. One of the result fields returned is an employee number e.g. "12345".
How can I perform an ajax lookup and replace the employee number with the corresponding name?
Example: /_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '12345'
would return: "Doe, John".
I've tried using a filter but nothing ever gets displayed even though I can see results returned.
<div ng-repeat="emp in employees"">
<i class="fa fa-user"></i> {{emp.id}}
</div>
app.filter('getName', function($http) {
return function(id){
if (id) {
var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '"+id+"'";
$http({
method: 'GET',
url: url,
cache: true,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data, status, headers, config) {
userInfo = data.d.results[0].pn;
console.log(userInfo);
}).error(function (data, status, headers, config) {
userInfo = "0";
});
return userInfo;
}
};
});
The filter function is synchronous, while the $http call is asynchronous. The success callback isn't even going to be executed until after the filter function has already returned, so it looks like the return value will be undefined.
An angular filter isn't really appropriate for loading data from an API, and there's an easier approach. Add userInfo to the employees array in the appropriate service/factory/controller (that's up to how you're organizing your code, but the controller where you set $scope.employees is the quick and dirty option). Something like a forEach through the array making an API call for each one and setting employee.userInfo in the success callback:
app.controller('EmployeeController', function($scope, $http) {
// $scope.employees is initialized somehow
$scope.employees.forEach(function (employee) {
if (employee.id) {
var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '"+employee.id+"'";
$http({
method: 'GET',
url: url,
cache: true,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data) {
employee.userInfo = data.d.results[0].pn;
}).error(function () {
employee.userInfo = "0";
});
}
});
});
And in your template:
<div ng-repeat="emp in employees">
<i class="fa fa-user"></i> {{emp.userInfo}}
</div>
It's up to you to figure out what to do before the ajax request is finished, while emp.userInfo is undefined - hide the element, show a placeholder, etc.
Related
app.controller('contentixaeng', function ($scope, $http) {
$scope.subject = function(){
$scope.code=101;
};
$http({
method: "POST",
data:{
'subj_code':$scope.code, 'action':'singledata'
},
url: "pages/Entries/connectixa.php"
}).then(function mySuccess(response) {
$scope.users = response.data;
}, function myError(response) {
$scope.error = response.data;
});
});
I am trying to pass the value of $scope.code to data in HTTP service. It's not working properly and no data value is shown as output. Instead, I get the error "ng-repeat dupes".
The function subject get called through this line
<li class="nav-item" ng-contoller="contentixaeng"><a class="nav-link" href="#" ui-sref="ixaeng" ng-click="subject()" >English</a></li>
If I change the code like shown below then it works
app.controller('contentixaeng', function ($scope, $http) {
$scope.subject = function(){
$scope.code=101;
};
$http({
method: "POST",
data:{
'subj_code':101, 'action':'singledata'
},
url: "pages/Entries/connectixa.php"
}).then(function mySuccess(response) {
$scope.users = response.data;
}, function myError(response) {
$scope.error = response.data;
});
});
I want different data to be passed to the database search based on the on-click event.
The http request is sent immediatly when the controller is created. At this point $scope.code is not yet set.
Try something like this:
app.controller('contentixaeng', function ($scope, $http) {
$scope.subject = function(){
$scope.code=101;
callBackend();
};
function callBackend() {
$http({
method: "POST",
data:{
'subj_code':$scope.code, 'action':'singledata'
},
url: "pages/Entries/connectixa.php"
}).then(function mySuccess(response) {
$scope.users = response.data;
}, function myError(response) {
$scope.error = response.data;
});
});
}
Like this the http request is sent only when the callBackend method is explicitly called.
If you're receiving ng-repeat dupes error that means that you've got duplicated entries in $scope.users- try debugging that and see what's going on there. Also, you can use track by option like below:
ng-repeat="user in users track by $index"
It will assure that each user will be treated as unique entity, even if you have duplicated entries in $users variable.
Another thing is, where this piece of code is run? I do not see it anywhere in the code you provided
$scope.subject = function(){
$scope.code=101;
};
Here is the working code:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $http) {
$scope.name = 'World';
$http({
url: 'http://landregistry.data.gov.uk/landregistry/query',
headers: { 'Content-type' : 'application/x-www-form-urlencoded',
'Accept' : 'application/sparql-results+json' },
method: "GET",
params: {
query : "select * where {?s a ?o} limit 10",
format: "json"
}
})
.success(function(data, status, headers, config) {
$scope.results = data.results.bindings;
// this callback will be called asynchronously when the response is available
})
.error(function(data, status, headers, config) {
// called asynchronously if an error occurs or server returns response with an error status.
});
});
http://plnkr.co/edit/LfkIPZRsZ4QHTfq2A2vc?p=preview
My doubt is how to store for instance, all s.values in an array such that the array can be further used to populate a drop down menu without having to save the JSON output to an external file.
Thanks.
You are already storing the results as a list in $scope.results.
To use the values for s in a dropdown menu, for example, you can do this:
<select ng-options="item as item.s.value for item in results" ng-model="selected"></select>
Plunkr
This is an app that I took over from another developer. I have a controller that calls a function inside a service and it passes in the $scope of the controller as a parameter. THis exact same thing is done with another function in the service.
THe problem is that only one of these services seem to update the view.
Here is my code (very condensed to keep it short, or a short as possible)
var KrisisEventsApp = angular.module('KrisisEventsApp', []);
KrisisEventsApp.filter('unsafe', function ($sce) { return $sce.trustAsHtml; });
//Controller for registration
KrisisEventsApp.controller('RegistrationCtrl',
['$scope', 'RegistrationService', '$timeout',
function ($scope, RegistrationService, $timeout) {
$scope.SaveRegistrantData =
function () {
//save user input
RegistrationService.SaveRegistrationForm($scope);
};
$scope.Products_Continue =
function () {
RegistrationService.ListItemsForCart($scope);
};
}
]
);
KrisisEventsApp.service('RegistrationService',
['$http', function ($http) {
var thisService = this;
//injects custom properties into registration form
thisService.SaveRegistrationForm = function ($scope) {
this.count = 0;
this._scope = $scope;
// if no product are found, go straight to payment page
this.count = Object.keys($scope.products).length;
// console.log(this.count);
$http({
method: "POST",
url: v_ModulePath + "/API/Registrants_WebAPI/RegisterUser",
dataType: 'text/plain',
data: data,
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
})
.then(function (response) {
data = JSON.parse(JSON.parse(response.data))
this.config2 = {
method: "GET",
url: v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID,
dataType: 'text/plain',
data: '',
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
}
return $http.get(v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID, this.config2);
})
.then(function (response) {
data = JSON.parse(JSON.parse(response.data));
$scope.Cart = data;
});
}
//list cart items
thisService.ListItemsForCart = function ($scope) {
$http(
{
method: "GET",
url: v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID,
dataType: 'text/plain',
data: '',
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
}).success(function (data) {
data = JSON.parse(JSON.parse(data));
$scope.Cart = data;
}).error(function (data) {
});
}
}
]
);
Here is the view (portion):
...
<a ng-click="SaveRegistrantData()" class="small button success" id="but_InputForm_MoveNext">Continue</a>
...
<a ng-click="Products_Continue()" class="small button success">Continue</a>
...
<tbody>
<tr ng-repeat="CartItem in Cart.Cart_Items_List">
<td>{{ CartItem.Item_Description }}</td>
<td class="text-right">{{ CartItem.Item_Fee }}</td>
</tr>
<tr>
<td>Total to Pay</td>
<td class="text-right">{{ Cart.CartTotal }}</td>
</tr>
</tbody>
You can see that the SaveRegistrationForm and ListItemsForCart functions are called from the controller, which are in turn initiated by button clicks from the view.
NOTE:
the following lines are the important ones in each function:
data = JSON.parse(JSON.parse(response.data));
$scope.Cart = data;
PROBLEM:
Only the ListItemsForCart updates the view, when I run the SaveRegistrationForm function the view does not update.
I have used fiddler to examine the web api calls and both functions successfully return the same data.
I have also used the chrome console to confirm that the $scope.Cart object does in fact get assigned the right data.
QUESTION:
Can someone help me figure out why my view is not updating?
Okay, I inherited this app from another developer. They defined the controller with the same name twice in the html view. arrrrg. So the second set of data that was not updating was in the second instance of the controller.
Once I moved a single controller instance to wrap the whole view the problems all went away.
Thanks to all who helped. You would not have been able to tell this from the abbreviated view code I posted. (I also have many other things to update, like .success to then catch and like Satpal said: just returning promises from my services etc...)
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
I'm trying to load a function on a link click, which works perfectly. Then angularjs does its magic until it arrives on the point where it shows the user feedback. I need to refresh the page after deleting an item, but it simply won't refresh.
Here's my href:
<a ng-click="deleteGroup(group.id)" target="_self">
<img src="img/deleteGroupIcon.png" width="45px"/></a>
here's my Controller:
$scope.deleteGroup = function ($groupId) {
$http({
method: 'POST',
url: apiUrl + 'group/delete',
data: {'groupId': $groupId}, // pass in data as strings
//headers: {'Content-Type': 'application/x-www-form-urlencoded'}
// set the headers so angular passing info as
// form data (not request payload)
})
.success(function (data) {
if (!data.success) {
//$route.reload();
alert('groep is verwijderd');
} else {
$scope.group = data;
//$state.go('userDetail', {'user_id' : $userId});
}
});
};
and my html:
<div class="searchResults" ng-repeat="group in groups | searchForUser:searchString">
<div class="manageGroupWrapper">
<a class="normal" href="#">
<div class="newName h3">
{{group.title}}
</div>
<div class="newProfile">
<img ng-src="{{group.image}}" width="200px"/>
</div>
<div class="imageWrapper">
<a ui-sref="add_group" class="editGroupIcon"><img src="img/editGroupIcon.png" width="50px"/></a>
<a ng-click="deleteGroup(group.id)" target="_self"><img src="img/deleteGroupIcon.png" width="45px"/></a>
</div>
</a>
</div>
Your $scope.deleteGroup function should remove its target group from the $scope.groups so the content of the ng-repeat is automatically updated and does not display the group anymore.
$scope.deleteGroup = function (group) {
$http({
method: 'POST',
url: apiUrl + 'group/delete',
data: {'groupId': group.$groupId}, // pass in data as strings
//headers: {'Content-Type': 'application/x-www-form-urlencoded'}
// set the headers so angular passing info as
// form data (not request payload)
}).success(function(data) {
$scope.groups.splice($scope.groups.indexOf(group));
});
};
To force refreshing the page with uiRouter you can use $state.go(target, params, {reload: true}).
$scope.deleteGroup = function (group) {
$http({
method: 'POST',
url: apiUrl + 'group/delete',
data: {'groupId': group.groupId}, // pass in data as strings
//headers: {'Content-Type': 'application/x-www-form-urlencoded'}
// set the headers so angular passing info as
// form data (not request payload)
}).success(function(data) {
// This will reload the current state with the same stateParams
$state.go($state.current.name, $stateParams, {reload: true});
});
};