AngularJS -- Resources and using them before the fulfilled - javascript

I am having a big issue with Resources.
I created a mod to hold the resource, I have to use the same data in many different locations almost at the same time. one bigest is to populate a select list.
I can populate the select list with no problems, but the default selection is always blank.
I tried to use
$scope.ServersList = Resource.List();
$scope.Server = $scope.ServersList[1];
I tried to add a watch and it never went off when the promise was fulfilled;
This will populate the list but will not set the default. Im looking for solutions to the problem of the promise not being fulfilled tell after Server is set.

Maybe like this
Resource.List().then(function(server){
$scope.ServersList = server;
$scope.Server = server[1]
});
regards

It hard to say whats going on with the information provided, but if this is happening inside a async call then make sure you are using apply.
$scope.$apply(function () {
$scope.ServersList = Resource.List();
$scope.Server = $scope.ServersList[1];
});

Related

How to use if else with protractor

I have very strange scenarios and I am not sure how to handle it.
Im a new into testning and I got a site to test where we check a cart function if its working property.
My problem is that we add x numbers of product and we do a stock check. If there is a stock conflict then we need to solve it before continue else we just continue.
I managed to create a function that looks like:
describe("Details page", function () {
detailsPage = new DetailsPage();
// The details page is accessible by the specified URL
it(`Is defined by the URL: ${userData["url"]}${browser.baseUrl}`,
async function () {
await detailsPage.navigateDesktop();
});
// Details page has a form and it can be filled out with user data
it("Has a form that can receive user data",
async function() {
await detailsPage.fillFormWithUserData();
await utils.click(detailsPage.getForm().buttons.nextStep);
});
if (detailsPage.hasStockConflict()) {
// Details page allows the user to fix conflicts in stocks
it('Enables resolution of stock conflicts', async function () {
// Wait for stock to fully load
await detailsPage.hasStockConflict();
await detailsPage.clickAllRemoveButtons();
await detailsPage.clickAllDecreaseButtons();
});
// Details page allows the user to proceed to the next stage when all conflicts (if any) has been resolved
it('Allows the user to proceed to the next stage of purchasing', async function () {
const nextStepButton = detailsPage.getForm().buttons.nextStep;
await utils.elementToBeClickable(nextStepButton);
await utils.click(nextStepButton);
});
}
});
however my function problem is that I need to wait until I get a response back from the server, either I do get a stock conflict which will be triggered by:
hasStockConflict() //checks if there is stockConflict message in DOM
or I will will get redirect to new page.
My question is, how can I either make a sort functionally that checks if there is a stock conflict then we solve the if statement else we just continue without needing to do anything (Which will take me to next page)?
I have set a timeout for 1 minute. After 1 minute it will pass the test as failed.
Basically I want to solve the if statement if there is a stock conflict else we just skip it basically. I might have done misunderstood the purpose of testning so all sort of knowledge would also be appreciated!
To add to what Code-Apprentice has mentioned, you can set up mock data to get the response as you see fit. You should have different responses mocked and depending on the response do one specific thing in one test. No if else stuff in the steps.
In your case, for now, use items which you know are in stock or add dummy items which are always instock and add dummy items to your database which are out of stock. Write separate tests for both and how you see fit.
Hope it helps!
Each test should test on specific thing. They should not contain if...else branching. Instead, you should have a test for each scenario. Each test should require initialized data that satisfies that specific scenario.
You have two different ways to approach this:
Set up data in resource that you query and request the specific data for the scenario being tested.
Mock the resource so that requests return mock data that is curated for the scenario being tested.
What everyone was saying is that there are best practices that one should follow in order to avoid pitfalls in future...
However, the best practice #1 is it always depends on your company, your product, your needs. So if you decide you need to go this route, go for it
Why your scenario doesn't work
Short answer, your it blocks are built before the browser started. At that time
your function can't run, and I assume fails or returns undefined
Answer
With that said ^, you can't skip it, just place your logic inside like this
it('Enables resolution of stock conflicts', async function () {
if (detailsPage.hasStockConflict()) {
// Wait for stock to fully load
await detailsPage.hasStockConflict();
await detailsPage.clickAllRemoveButtons();
await detailsPage.clickAllDecreaseButtons();
const nextStepButton = detailsPage.getForm().buttons.nextStep;
await utils.elementToBeClickable(nextStepButton);
await utils.click(nextStepButton);
}
});

How to correctly resolve a javascript promise for a bs-typeahead

I'm working on a project to develop a datasource plugin for Grafana. This means that I'm stuck with what appears to be reasonably old versions of some of the AngularJS libraries/modules. The Grafana project also seems to have pulled out the use of $q, and as a result I'm trying to work out how to use native Promise objects where possible (Promises are also something I'm new to).
I've got a bs-typeahead form input which is correctly calling the following promise which returns results:
getOptions(query) {
console.log('Getting options')
return this.datasource.metricFindQuery(query || '').then(a => {
console.log(a);
this.scope.$digest();
return a
});
}
However, the bs-typeahead drop down doesn't appear showing the results, despite an array showing the expected results being logged to the console.
With this.scope.$digest(); in the function, I get an error of $digest already in progress, and so now I'm stuck with where/how I should be calling $scope.$digest(), or if that's the best approach. If I remove that line I don't get an error, but no results appear.
I've taken a look at a few different suggestions to try and get this to work, but haven't had any success thus far.
If I swap out the getOptions return for a plain array (eg. ['a','b','c']) the lookahead works without any issue - so I'm confident the issue is with the Promise.
It seems like $scope.$apply, could be an option, but again I'm not sure where it should sit in the context of the codebase.
What should I be doing to get the promise to resolve appropriately in light of the bs-typeahead?
The whole Javascript file that the above function resides in is available here.
Beyond assistance with my immediate question, an explanation of how the Promise(s) resolve in my particular context would be a great help in making sure I'm understanding the concept correctly.
It seems that your typeahead is not able to handle a Promise.
You will have to manually provided an Array and update it once the Promise is resolved.
getOptions(query) {
let result = [];
console.log('Getting options')
this.datasource.metricFindQuery(query || '').then(a => {
console.log(a)
a.forEach(item => result.push(item));
});
return result;
}

how to handle JSON file loading with Angular

I have a few controllers that call the method getData() from a service.
In order to not do extra http calls for the same json file, i'm using something like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
return quizData ? quizData : quizData = $http.get('quiz.json');
};
return quizService;
}]);
...but things don't work properly if I do it like that (the data is used to populate a slider and a thumbnail gallery with angular-slick and some problems arise. For now it maybe doesn't matter, I just want to know if the code above makes sense).
On the other hand, if I write getData() like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
quizData = $http.get('quiz.json');
return quizData;
};
return quizService;
}]);
... which will do various http requests for the same json file (doesn't look like a good practice to me), everything works fine and the slick angular gallery works properly. But not 100% of the times though: kind of randomly things don't work well too (same symptoms. I might describe them but again, I don't think that's the point here)
So, in general, regardless of the context, which one of those versions of getData() looks good and which doesn't and why?
UPDATE
As Mark pointed out, Angular has a built in cache, but it's set to false by default. Here is a post and here is the documentation.
If I cache the result of the http request though I get the same problem (I'm not describing it here) I was getting with my second option, and it has apparently nothing to do with that.
Actually, it seems that if I repeat the http request two times (as in my second snippet of code) things work by chance (90% of the time?).
So, by caching the result, at least I get a consistent result, which means in this case that the slick-angular thing won't work properly (never) and I have to look for a solution somewhere else.
Angular $http has a built in cache, which you could make use of here. You can cache all $http requests, which is probably a bad idea, or specific ones.
On a very simple level, this should work for you
quizService.getData = function() {
return $http.get('quiz.json', {cache: true}).then(quizData => {
return quizData;
});
};
You can find out more in the Angular docs for $http

AngularJS : filter with AJAX

I have been working on AngularJS project recently and came over an interesting problem while trying to create a filter which is using data loaded via AJAX request.
First about the problem:
AngularJS filter is a synchronous piece of code (function) that returns a string which is inserted into your DOM. And in most of the cases it works perfectly fine e.g. following filter that capitilizes first letter:
angular.module('myApp.filters', []).filter('capitilize', function() {
return function (word) {
return word.charAt(0).toUpperCase() + word.substr(1);
}
});
And this works great. Now the question is what if I can't return the desired result right away? Say I need to load some data via AJAX request to get the desired result. If I make an AJAX request my return statement will return an empty result before I get my data. So really the question is how do I notify filter to update itself when my data is loaded?
Solution:
It turned out that the solution was right there in front of me, but it took me some time to figure out how the magic is happening. Say I need a filter that retrieves artist's biography based on their name (yeah, a little bit crazy example but it proves the point):
angular.module('myApp.filters', []).filter('biography', function($q, $http) {
var pending = {};
return function (artist) {
if ( !(artist in pending) ) {
pending[artist] = null;
$http.get('http://developer.echonest.com/api/v4/artist/biographies?api_key=FILDTEOIK2HBORODV&name='
+ artist + '&format=json&results=1&start=0&license=cc-by-sa')
.then(function(response){
pending[artist] = response.data.response.biographies[0].text;
});
}
return pending[artist] || '';
}
});
It works, but how? I made a get request, got my result, but how does it force filter to update itself. The key here is the angular's $q (A promise/deferred implementation inspired by Kris Kowal's Q.)
from angular documentation:
$q is integrated with the $rootScope.Scope Scope model observation mechanism in angular, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.
This means that whenever the promise is resolved it causes the update, in fact here is the code from angular:
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}
resolvePromise(response, status, headersString, statusText);
if (!$rootScope.$$phase) $rootScope.$apply();
}
Hope this was helpful for you. Here is the example, keep in mind that the example is making cross domain ajax calls, you will need to disable cross domain policy of your browser:
http://jsfiddle.net/pJuZ9/8/
IMPORTANT: Keep in mind not to overwhelm filter with same ajax request, otherwise you might end up with this:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
thank you for that post, at least I knew I'm not completely wrong. I have a very similar scenario:
1) a simple translate filter
module.filter('translate', ['Localization', function (localization) {
var translateFilter = function (key) {
return localization.get(key) || "[" + key + "]";
};
translateFilter.$stateful = true;
return translateFilter;
}]);
2) a localization service which stores a JS dictionary. That is updated via XHR. That means that there are multiple possible states of the dictionary:
no localization before init => empty
default localization after init => non-empty
user-defined localization after default is loaded => non-empty
My problem was that the filter result wasn't updated (re-rendered) after the XHR call ended, even though a digest was performed. I had to add
translateFilter.$stateful = true;
to make it work. Even changing filter dependency from service to value did not help. Someone might find this helpful, or event better, tell me, what I was doing wrong :-)

dojo.data.objectStore.deleteItem

I have a dojo.store.Memory wrapped in a dojo.data.ObjectStore which I am then plugging into a dataGrid. I want to delete an item from the store and have the grid update. I have tried every combonation I can think of with no success. For example:
var combinedStore = new dojo.data.ObjectStore({objectStore: new dojo.store.Memory({data: combinedItems})});
combinedStore.fetch({query:{id: 'itemId'}, onComplete: function (items) {
var item = items[0];
combinedStore.deleteItem(item);
combinedGrid.setStore(combinedStore);
}});
combinedGrid.setStructure(gridLayout);
This throws no errors but combinedStore.objectStore.data still has the item that was meant to be deleted and the grid still displays the item. (The also seems to be a complete mismatch between combinedStore.objectStore.data and combinedStore.objectStore.index);
There's a simple solution, luckily! The delete is successfully happening, however, you need to save the ObjectStore after the deletion for it to be committed.
Change your code to look like this:
onComplete: function (items) {
var item = items[0];
combinedStore.deleteItem(item);
combinedStore.save();
combinedGrid.setStore(combinedStore);
}
That little save should do the trick. (Please note: the save must occur after the deleteItem - if you put it outside the fetch block, do to being asynchronous, it will actually happen before the onComplete!)
Working example: http://pastehtml.com/view/b34z5j2bc.html (Check your console for results.)
This does seem rather poorly documented at present in the new dojo.store documentation.
The old dojo.data.api.Write documentation make it fairly clear. An excerpt from http://dojotoolkit.org/reference-guide/dojo/data/api/Write.html:
Datastores that implement the Write interface act as a two-phase
intermediary between the client and the ultimate provider or service
that handles the data. This allows for the batching of operations,
such as creating a set of new items and then saving them all back to
the persistent store with one function call.
The save API is defined as asynchronous. This is because most
datastores will be talking to a server and not all I/O methods for
server communication can perform synchronous operations.
Datastores track all newItem, deleteItem, and setAttribute calls on
items so that the store can both save the items to the persistent
store in one chunk and have the ability to revert out all the current
changes and return to a pristine (unmodified) data set.
Revert should only revert the store items on the client side back to
the point the last save was called.
dojo.store has evolved from dojo.data and seems to follow many of its behavioral aspects.
The new dojo.store documentation http://www.sitepen.com/blog/2011/02/15/dojo-object-stores/ and http://www.sitepen.com/blog/2011/02/15/dojo-object-stores/ manages to talk specifically about the delete operation without mentioning having to call save() (in fact I can't find the word 'save' on that page at all).
I'm staying away from dojo.store as long as possible, hopefully it will be easier to follow in 1.7 or later, whenever I'm forced to use it for real :)

Categories

Resources