why is this resource not updating the view after using $delete method - angularjs

In my angular app I have a controller as follows:
function TemplateListControl($scope, TemplateService){
$scope.templates = TemplateService.get(); // Get objects from resource
// Delete Method
$scope.deleteTemplate = function(id){
$scope.templates.$delete({id: id});
}
}
Within the view I have a table thats bound to templates model. as follows:
<table ng-model="templates">
<thead>
<tr>
<th style="width:40%">Title</th>
<th style="width:40%">controls</th>
</tr>
<thead>
<tbody>
<tr ng-repeat="template in templates">
<td>
<!-- Link to template details page -->
<a href="#/template/[[template.id]]">
[[template.title]]
</a>
</td>
<td>
<!-- Link to template details page -->
<a class="btn btn-primary btn-small"
href="#/template/[[template.id]]">Edit
</a>
<!-- Link to delete this template -->
<a class="btn btn-primary btn-small"
ng-click="deleteTemplate(template.id)">Delete
</a>
</td>
</tr>
</tbody>
</table>
Now when I click on the delete link in the above template, It calls the deleteTemplate method and a successful DELETE call is made to the REST api. But the view does not get updated until it is refreshed and $scope.templates = TemplateService.get(); is initiated again. What am I doing wrong?

I prefer using promises instead of callback. Promises are the new way to handle asynchronous processes. You can inspect the result using a a promise right after it came back from the server.
//Controller
myApp.controller('MyController',
function MyController($scope, $log, myDataService) {
$scope.entities = myDataService.getAll();
$scope.removeEntity = function (entity) {
var promise = myDataService.deleteEntity(entity.id);
promise.then(
// success
function (response) {
$log.info(response);
if (response.status == true) {
$scope.entities.pop(entity);
}
},
// fail
function (response) {
$log.info(response);
// other logic goes here
}
);
};
});
//DataService
myApp.factory('myDataService', function ($log, $q, $resource) {
return {
getAll: function () {
var deferred = $q.defer();
$resource('/api/EntityController').query(
function (meetings) {
deferred.resolve(meetings);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
},
deleteEntity: function (entityId) {
var deferred = $q.defer();
$resource('/api/EntityController').delete({ id: entityId},
function (response) {
deferred.resolve(response);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
}
};
});
//Web API Controller
public class MeetingController : BaseApiController
{
// .... code omited
public OperationStatus Delete(int entityId)
{
return _repository.Delete(_repository.Single<MyEntity>(e => e.EntityID == entityId));
}
}
Note: $log, $q, $resource are built in services. Hope it helps :)

You also have to update client side so modify your source code as below
ng-click="deleteTemplate($index)">
$scope.delete = function ( idx ) {
var templateid = $scope.templates[idx];
API.Deletetemplate({ id: templateid.id }, function (success) {
$scope.templates.splice(idx, 1);
});
};

You could pass a callback function to $resource.$delete
function TemplateListControl($scope, TemplateService){
$scope.templates = TemplateService.get(); // Get objects from resource
// Delete Method
$scope.deleteTemplate = function(id){
TemplateService.$delete({id: id}, function(data) {
$scope.templates = data;
});
}
}
Attention
If your REST APIs delete function returns an array you have to set isArray: true in your Angular $resource to avoid a AngularJS $resource error - TypeError: Object # has no method 'push'
$resource(URL, {}, {
delete: {method:'DELETE', isArray: true}
});

Related

Get Value of column by API in angular

I want to get status value from API in my grid made in angular js. Below is the code, I applied in my view file:
<tr ng-repeat="item in list">
<td>{{item.id}}</td>
<td class="text-center" ng-init="statusUpdate(item.id ,$index)">
</td>
<td>{{myVar[$index]}}</td>
</tr>
Then in my controller I added:
app.controller('MerchantController', function ($scope, MerchantService, Alert, toaster) {
$scope.statusUpdate = function (id, index) {
var api = MerchantService.statusUpdate(id, index).then(function (response) {
console.log($scope.myVar[index]);
$scope.myVar[index] = response.data;
console.log($scope.myVar[index]);
});
};
});
In my service file, I added:
app.service('MerchantService', function(API, $stateParams, $q,$http) {
this.statusUpdate = function(item,index) {
return $http({
url: 'http://10.10.10.7/petca_magento/integration/vendor/vendor/id/' + item,
method: "GET",
});
};
});
I want to get of status field dynamically based on the user id in {{myVar[$index]}}

Angular 1.6: Data not showing until scope function execute

Title might sound a bit strange but that's how it is.
Code:
angular.module('app')
.controller('TenantsCtrl', ['$scope', 'tenantsService','$window','loginService', function ($scope, tenantsService, $window, loginService) {
$scope.mode = "list";
$scope.g = [];
$scope.editTenant = {
firstName: "",
lastName: "",
room: "",
to: "",
from: ""
}
tenantsService.get().then((data) => {
data.tenants.map((tenant) => {
tenant.to = new Date(tenant.to).toLocaleDateString('ro-RO')
tenant.from = new Date(tenant.from).toLocaleDateString('ro-RO')
});
console.log(typeof data.tenants);
$scope.g = data.tenants;
console.log($scope.g)
}).catch((reason) => {
loginService.doLogout();
if(confirm(`${reason}. Please login`)) {
$window.location.href="#!/login";
}
})
$scope.showTenantForm = function(tenant) {
console.log($scope.g)
}
}]);
View:
<div class="page-header">
<h1>Cazati <button class="btn btn-xs btn-primary" ng-click="showTenantForm()">Cazeaza</button></h1>
</div>
<div class="row" ng-cloak>
{{g}}
<table class="table table-hover">
<thead>
<tr>
<th>Nume</th>
<th>Camera</th>
<th>Cazat din</th>
<th>Cazat pana la</th>
<th>Cazat de</th>
<!--<th>Status</th>-->
<th>Modifica</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in g">
<td>{{x.lastName}} {{x.firstName}}</td>
<td>{{x.room}}</td>
<td>{{x.to}}</td>
<td>{{x.from}}</td>
<td></td>
<td>
<button type="button" ng-click="editTenant(tenant.id)" class="btn btn-danger btn-xs">Modifica</button>
</td>
</tr>
</tbody>
</table>
</div>
Problem: data from table isn't shown. Strangely it gets shown only when press the button from header and the function gets called.
Data from service comes correctly.
Update: tenantsService
angular.module('app')
.service('graphQLService', ['baseUrl', 'loginService', function (baseUrl, loginService) {
var graphQLService = {};
graphQLService.sendQuery = function (query, vars) {
var self = this;
vars = vars || {};
return new Promise(function(resolve, reject) {
// use fetch to get the result
fetch(baseUrl + '/gql', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
query,
vars,
authToken: loginService.getToken()
})
})
.then(function(response) {
var status = response.status;
if(status != 401) {
return response.json().then((res) => {
resolve(res.data);
})
} else {
reject(response.statusText);
}
if(response.status == 401) {
}
})
.catch(reject);
});
};
return graphQLService;
}]);
Add a $scope.$apply() after you get your response and you manipulate your data, and after that you assign your data to your g scope variable. So:
$scope.g = data.tenants;
$scope.$apply();
AngularJS already wraps a lot of its code inside the $scope.$apply method. This in order to trigger the $scope.$digest method, which is the one responsible for refreshing all the bindings inside the related scope and update the values and so the views.
For example, if you were using the custom $http service, it's executed inside the $scope.$apply, so you don't need to call it explicitly.
In your case, you are not using the standard $http, so you need to trigger it manually.
The code was working after calling the other function because the ng-click is wrapped inside the $apply as well, so it refreshes all the bindings in the scope.
If you want to read more about it, this is a pretty good article:
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
I hope it helps
I think the problem is that data.tenants.map returns you a new array. You don't save the reference to it.
Have you tried to do this
$scope.g = data.tenants.map((tenant) => {
tenant.to = new Date(tenant.to).toLocaleDateString('ro-RO');
tenant.from = new Date(tenant.from).toLocaleDateString('ro-RO');
return tenant;
});
console.log(typeof data.tenants);
console.log($scope.g)

How to do $http.get wait response

I have a trouble with $http.get. When my service.js access my MVC Controller, the $http.get don't wait for response. How can I do $http.get wait MVC Controller response? My code.
AngularJS Service:
angular.module("cadastroCliente").factory("clientesAPI", function($http) {
var getClientes = function () {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/");
};
return {
getClientes: getClientes
};
});
ControllerJS - Edited
angular.module("cadastroCliente").controller("cadastroClienteCtrl", function ($scope, $http, $q, $timeout, clientesAPI) {
$scope.app = "Pesquisar Clientes";
$scope.clientes = [];
var carregarClientes = function () {
clientesAPI.getClientes().success(function (data) {
$scope.clientes = data;
}).error(function (data, status) {
$scope.message = "Aconteceu um problema: " + data;
});
};
carregarClientes();
$scope.totalItems = $scope.clientes.length;
$scope.itemsPerPage = 2;
$scope.currentPage = 1;
$scope.maxSize = 5;
$scope.bigTotalItems = 175;
$scope.bigCurrentPage = 1;
$scope.pageCount = function () {
return Math.ceil($scope.clientes.length / $scope.itemsPerPage);
};
$scope.$watch('currentPage + itemsPerPage', function () {
var begin = (($scope.currentPage - 1) * $scope.itemsPerPage),
end = begin + $scope.itemsPerPage;
$scope.filteredClientes = $scope.clientes.slice(begin, end);
});
});
View:
<div class="table-responsive">
<table class="table table-bordered table-hover table-striped" id="clienteId">
<thead>
<tr>
<th class="col-lg-1">Código</th>
<th class="col-lg-7">Nome</th>
<th class="col-lg-3">Documento</th>
<th class="col-lg-1">Extrato</th>
</tr>
</thead>
<tbody ng-repeat="cliente in filteredClientes">
<tr>
<td>{{cliente.ClienteId}}</td>
<td>{{cliente.Nome}}</td>
<td>{{cliente.CpfCnpj}}</td>
<td style="cursor: pointer"><i class="glyphicon glyphicon-search"></i></td>
</tr>
</tbody>
</table>
</div>
<uib-pagination total-items="totalItems" items-per-page="itemsPerPage" ng-model="currentPage" ng-change="pageChanged()"></uib-pagination>
You can't set $scope.totalItems until the data has arrived which means you need to set it in the success callback where you assign $scope.clientes from the response data.
$http is an ajax request and the first A in ajax is for "asynchronous"
Note that success and error are deprecated and you should be using the recommendation in documentation to use the promise callbacks then() and catch()
I suggest that you put as much logic pertaining to the request in your factory as possible, including handling the response:
var getClientes = function () {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/")
.then(function(res) {
return res.data
});
};
Also don't forget that the get request returns the response object, and that you might need to return the data property on the response (if that's what you need).
You should also favor the .then() method on promises.
In es6 we can make this very precise and also catch any errors like so:
var getClientes = () => {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/")
.then(({data}) => data)
.catch(({data}) => console.log(data.message));
};
In your controller we can then do this:
var carregarClientes = function () {
clientesAPI.getClientes()
.then(function (data) {
$scope.clientes = data;
})
};
In es6:
var carregarClientes = () => {
clientesAPI.getClientes()
.then(data => $scope.clientes = data);
};
EDIT: Response to update code in OP
Unfortunately, anything pertaining to clientes should be done in the callback of the request:
var carregarClientes = function () {
clientesAPI.getClientes()
.then(function (data) {
$scope.clientes = data;
//Assign any other variables
})
};
The reason being, is that all your code in the controller referencing clientes is referencing the original empty array and not the data from the request. This is because it's outside of the call. Therefore, that code is run while the request is being made. If you place it inside the callback it has access to the returned data once it arrives.

angularjs not updaing propeties when promises used

I am using service to get JSON object using promises. It is then converted into array and assign to a property which is in $scope object. The problem is that array is not getting updated or any properties inside promise then() method.
Controller
var searchController = function ($scope, $SPJSOMService) {
$scope.myName = "old name";
$scope.getUsers = function ($event) { //called on button click
$event.preventDefault();
var queryText = "test user";
$SPJSOMService.getUsers(queryText)
.then(function myfunction(users) {
$scope.userCollection = JSON.parse(JSON.stringify(users)).d.results;
// $scope.$apply(); this line throwing error. $rootScope in progress
$scope.myName = "new name"; //not getting updated
}, function (reason) {
alert(reason);
});
};
};
Service
var SPJSOMService = function ($q, $http, $rootScope) {
this.getUsers = function (userToSerach) {
var deferred = $q.defer();
$http({
url:'some url',
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
"content-Type": "application/json;odata=verbose"
}
})
.success(function (data, status, headers, config) {
deferred.resolve(data); //successfully return data
})
.error(function (data, status, headers, config) {
deferred.reject(data);
});
return deferred.promise;
};
};
Updated
index.html
<div ng-include="'Search.html'">
</div>
<div ng-include="'searchResults.html'"></div>
search.html
<div id="containerDiv" ng-controller="searchController as search">
<input class="button" type="button" ng-click="getUsers($event);" value="Search" id="btnSearch" />
</div>
searchResults.html
<div ng-controller="searchResultController as result">
<table >
<thead>
<tr>
<th>Name</th>
<th>EMail</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in userCollection">
<td>{{$index}} {{item.userName}}</td>
<td>{{$index}} {{item.userEmail}}</td>
</tr>
</tbody>
</table>
</div>
So search.html returns the data by calling the service and I am trying to pass it to the page namely searchResults.html which have its own controller.
$scope.users = $scope.userCollection;
Service class correctly returning data, but the problem is inside then(), which is not updating $scope.userCollection, hence UI is not getting updated.
Please help me out, what am I missing here.
In angular controllers can bind to the view in two ways:
"controller as"
through scope
It looks you are using the "controller as" flavor here...
<div ng-controller="searchResultController as result">
Refer to https://docs.angularjs.org/api/ng/directive/ngController for more information.
Change your controller code to:
var searchController = function ($scope, $SPJSOMService) {
//note use of "this" instead of "$Scope"
var _this = this;
_this.myName = "old name";
_this.getUsers = function ($event) { //called on button click
$event.preventDefault();
var queryText = "test user";
$SPJSOMService.getUsers(queryText)
.then(function myfunction(users) {
_this.userCollection = users.d.results; //You don't need JSON.parse not JSON.stringify
_this.myName = "new name";
}, function (reason) {
alert(reason);
});
};
};
and in your view use the controller "alias":
<div ng-controller="searchResultController as result">
...
<tr ng-repeat="item in result.userCollection">
...
You have to push it to the new array I think. Have you tried this in your then statement?
$scope.myName.push("new name");
It should put it in the existing array. Let me know if it works.

ng table with data from http request

Does anyone have a decent example with ng-table loading data upon a success callback from http service?
this.getData = function(){
tmp = this;
tmp.loading = true;
$http.post('/fetch',
$.param({
service_request: JSON.stringify(this.session)
}
)).success(function(data) {
tmp.loading = false;
tmp.tableData = data;
});
};
I would like to build the table from the tableData variable.
Thanks
I think this is one way of doing it.
Create a service to get the table data (I just picked the code from your :
app.factory('getTableData', function($http) {
return {
getData: function(session){
return $http.post('/fetch',
{
service_request: session
}))
}
}
})
Then you inject the service in your controller:
app.controller('DemoCtrl', function(getTableData, $scope, $window) {
$scope.loadTable = function(){
getTableData.getData(JSON.stringify($window.session)) //not sure where you have the session variable stored.
.sucess(function(data){
$scope.tableData = data;
})
.error(function(){
//do something
})
}
})
The HTML should be pretty straightforward:
<button ng-click="loadTable()">Load Table</button>
<table ng-table class="table">
<tr ng-repeat="row in tableData">
<td data-title="'Property1'">{{row.Property1}}</td>
<td data-title="'Property2'">{{row.Property2}}</td>
</tr>
</table>

Resources