creating a service in angularJS, using restangular promises - javascript

Ok, this is driving me mad, basically what I am trying to do is create a service to get and evaluate user capabilities, I am using WP REST API. I use restangular to get my JSON data.
At this stage I am testing the function in the controller itself, but no matter where I test it, be it in my custom service using this.method or inside the controller, with or without using $scope the result is always undefined. I know I am missing something either in the way I am returning true or false inside the function, or there is something fundamentally different when it comes to promises in JS. Here is the code:
var current_user = parseInt(o2_i18n.user_id),
currentUserCapabilities,
capability;
$scope.currentUserCan = function(capability) {
if(current_user !== '0') {
wpAPIResource.one('users').get()
.then(function(allUsers){
for (var i = 0; i < allUsers.length; i++) {
if ( allUsers[i].id === current_user ) {
var currentUserCapabilities = allUsers[i].capabilities;
for(var prop in currentUserCapabilities){
if (capability === prop) {
//$log.log( prop );
return prop;
} else {
//$log.log( prop );
return false;
}
}
}
}
}, function(reason){
$log.error(reason);
});
} else {
//The user is not logged in, therefor no capabilities
return false;
}
};
$log.log($scope.currentUserCan('publish_posts'));
if ( $scope.currentUserCan('publish_posts') ) {
$log.log( 'Yes I Can!' );
} else {
$log.warn('No Can\'t Do!');
}

Your currentUserCan function doesn't return anything if current_user !== '0'. You should have it return a promise, for example (for the following you'll need to inject the $q service)
$scope.currentUserCan = function(capability) {
if(current_user !== '0') {
// note the "return" here
return wpAPIResource.one('users').get().then(function(allUsers){
for (var i = 0; i < allUsers.length; i++) {
if ( allUsers[i].id === current_user ) {
var currentUserCapabilities = allUsers[i].capabilities;
for(var prop in currentUserCapabilities){
if (capability === prop) {
return prop;
}
}
}
}
return false;
}, function(reason){
$log.error(reason);
return $q.reject(reason); // you still want the promise to fail
});
} else {
return $q.resolve(false);
// this turns the static value into a promise so the API for this
// function is consistent
}
};
then consume the function like this
$scope.currentUserCan('publish_posts').then(function(can) {
if (can) {
$log.log('Yes I Can!');
} else {
$log.warn("No Can't Do!");
}
});
I also cleaned up your loop a little. In your OP, there was no point in the inner loop and you didn't have a return value if the user was not found in the allUsers array.

Related

How to return multiple functions dynamically Javascript

I apologize if I didn't ask my question in the correct way. Let me explain my problem.
I'm working on a search function in a table, I'm adding the option to search in specific columns.
Right now I'm manually checking if the checkboxes are selected or not, then return the proper search function.
However I want to make it dynamically, I don't want to edit this code after every time I add new columns to the table. That's where I'm struggling.
This is my current code:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
if (vm.searchFilter[0] === true ){
return (contains(row[vm.table.columns[0].value], searchTerm))
}
if (vm.searchFilter[1] === true ){
return (contains(row[vm.table.columns[1].value], searchTerm))
}
if (vm.searchFilter[2] === true ){
return (contains(row[vm.table.columns[2].value], searchTerm))
}
if (vm.searchFilter[3] === true ){
return (contains(row[vm.table.columns[3].value], searchTerm))
}
if (vm.searchFilter[4] === true ){
return (contains(row[vm.table.columns[4].value], searchTerm))
}
if (vm.searchFilter[5] === true ){
return (contains(row[vm.table.columns[5].value], searchTerm))
}
if (vm.searchFilter[6] === true ){
return (contains(row[vm.table.columns[6].value], searchTerm))
}
if (vm.searchFilter[7] === true ){
return (contains(row[vm.table.columns[7].value], searchTerm))
}
});
}
});
This is the way I created it, I tried to use a loop, but I can't return the functions if there is a loop. Also adding return before the for loop wont help either.
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
for (let i=0; i<vm.searchFilter.length; i++){
if (vm.searchFilter[i] === true ){
return (contains(row[vm.table.columns[i].value], searchTerm))
}
}
});
}
});
This is probably a very easy case for the most of you, so I apologize if I'm just being stupid right now. I'm working since 2 hours on this till now...
EDIT:
The function I mean:
const contains = (value, searchTerm) => {
if (!value) return false;
return value.toLowerCase().includes(searchTerm);
}
2nd EDIT:
I just realized after a kind member told me that, that the first version isnt working either the way I want it.
There is the option to have a multiple selection, so if I select the first two checkboxes, then it should search in BOTH of them and not only one.
You can just iterate over the whole thing like this:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(row =>
vm.searchFilter.some((filter, index) => filter && contains(row[vm.table.columns[index].value])))
}
By using Array.prototype.some you essentially ask whether your row matches at least one of the filters.

Replace value with API value in ng-repeat

I am listing a bunch of JSON objects to a view, but only it's category ID is included. Not the name, which I need to display. I have to make a separate $http call to process which items match. Values will not render to view. Code:
$scope.cardCategories = function(id) {
angular.forEach($scope.categoryFilter, function(category, index){
if (category.id == id) {
//console.log(category.name);
return category.name;
} else {
return;
}
})
}
value inside a simplified ng-repeat
<div ng-repeat="topic in latest">
{{cardCategories(topic.category_id)}}
</div>
I have also attempted to write this as a filter, but the value will not display. Console shows the matches. My suspicion is that I have to process the original latest array. Thanks.
.filter('cardCategories', function($rootScope, $state){
return function(catId) {
if (catId !== undefined && $rootScope.cardCategories) {
angular.forEach($rootScope.cardCategories.categories, function(category, index){
if (category.id == catId.category_id) {
return category.name;
} else {
return;
}
})
}
}
})
View:
{{topic.category_id | cardCategories}}
That return statement in the forEach callback function will not return a value from the $scope.cardCategories, it returns something from the callback you provided to forEach (and angular happily ignores it). Try something like this:
$scope.cardCategories = function(id) {
var i = $scope.categoryFilter.length; // assuming it's an array
while(i--) {
if($scope.categoryFilter[i].id === id) {
return $scope.categoryFilter[i].name;
}
}
};
Also -- there's no way to break (stop early) an angular.forEach loop, so for performance reasons it's better to avoid it if you're locating something inside an array/object.
You need to make sure the forEach loop doesn't do a return if the current iteration doesn't match the ID you passed in.
Take out the else condition from your if statement
$scope.cardCategories = function(id) {
angular.forEach($scope.categoryFilter, function(category, index){
if (category.id == id) {
return category.name;
}
});
};

Javascript error when create object and comparing fields

I created a javascript object that i fill with data, When i return this object to anoother class and run the isError function on the object, i always get true, and i cant figure out why.
This is the new object i've created:
function WSResponse()
{
this.error = new Object();
this.isError = function()
{
if (this.error.status === '0')
{
return false;
}
else
{
return true;
}
}
}
This is my service:
...Some code..
var wsresponse = new WSResponse();
wsresponse.data = responseFromServer.ws_result;
wsresponse.error.status='0';
wsresponse.error.msg = responseFromServer.msg;
return wsresponse;
And this is my controller:
if (response.isError)
{
$scope.isLoginError = true;
}
else
{
$scope.isLoginError = false;
}
From some reason i always get true, when i debug i can see the value is 0 or -1 (string)
What is the problem ?
Thanks
isError is a function, so you need to call it like a function, i.e. response.isError()
if(response.isError) will always return true because you are effectively checking if there is a isError object - in this case function - attached to a response object.
if (response.isError())
{
$scope.isLoginError = true;
}
else
{
$scope.isLoginError = false;
}

Javascript promise conversion

I'm having a hard time understanding how promises works. I've seen some examples and some of it makes sense. However, when I try to actually use it I get stuck. I have the following example:
(I'm using q and nodejs. getItem() returns a promise, but my function doesn't wait for it.)
function calculate(id, item) {
var calcId = id ? id : getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Based on the examples, I don't see how to do it without code duplication.
Promises are resolved asynchronously, so you can't treat them as synchronous as in your example. What you want to do is coerce the value is a Q promise if needed, then proceed from there, that way you have deterministic behavior and no code duplication.
I'm making some assumptions here about update and insert returning promises and returning them when needed so that calculate itself returns a promise.
function calculate( id, item ) {
return Q( id || getItem(item) ).then(function( calcId ) {
// Code seems weird to be, but it's based on the original example.
// Might want to review this.
if ( !id && calcId ) {
calcId = calcId.Id;
}
return calcId ?
update( calcId ) :
insert( item );
});
}
Don’t duplicate your id checks:
function calculate(id, item) {
var calcId;
if (id) {
calcId = id;
} else {
calcId = getItem(item).Id;
}
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Now make calcId consistently a promise holding an Id:
function calculate(id, item) {
var p;
if (id) {
p = Promise.resolve({ Id: id });
} else {
p = getItem(item);
}
return p.then(function (calcId) {
if (calcId.Id) {
return update(calcId.Id);
} else {
return insert(item);
}
});
}
Where, in the case of Q, Promise.resolve is Q.resolve, or just Q.
Bonus: as a generator!
function calculate(id, item) {
var calcId = id ? id : yield getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Several points :
Promise-wrapped results need to be handled with a function specified as a parameter of a promise method (eg .then(fn))
Q(x) can be used to ensure that x is Promise-wrapped. The operation is transparent if x is already a promise - it won't be double wrapped
you need safety in case both id and item are empty or missing
you need further safety in case calcId is falsy and item was not provided
id versus calcId.Id can be more elegantly catered for.
function calculate(id, item) {
if(!id && !item) {
throw new Error("'calculate(): parameters empty or missing'");
}
return Q(id ? {Id:id} : getItem(item)).then(function(resultObj) {
var calcId = resultObj.Id;
if(calcId) {
update(calcId);
} else {
if(item) {
insert(item);
} else {
throw new Error("'calculate(): `calcId` is falsy and `item` is empty of missing'");
}
}
return calcId; //in case you need to chain calculate(...).then(...)
//(Alternatively, if update() and insert() return promises, then return them instead as per dherman's answer)
});
}
Call as follows :
calculate(myId, myItem).then(function(calcId) {
//(optional) anything else you wish to do with `calcId`
}).catch(function(e) {
console.error(e);
});

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

Categories

Resources