How to keep displayed result before new search result display - angularjs

Due to low query result, in my AngularJS application, when I click a search button to request a new search, my page will display my pre-define error message "No related search result as you want", after one/two seconds, my query finish, my page refresh to display my new search result.
I want to keep my displayed result before new search result display?
Any suggestion or hint to do that? Thanks.

This comes down to where you put the code to clear or replace the list of results.
It seems like you currently do something like this in your controller:
runSearch(searchText) {
this.results = []; // this shows your "no results" text
this.$http.get('/search?q=' + searchText).then(res => this.results = res.data);
}
Instead, just remove the first line so you only ever replace the results once the HTTP call returns:
runSearch(searchText) {
this.$http.get('/search?q=' + searchText).then(res => this.results = res.data);
}
Explanation
There are two points inside runSearch where Angular will update the page for you:
After the function is run, but before the HTTP call returns
After the HTTP call returns
Here they are in the code:
runSearch(searchText) {
// #1 - any code here will update the page immediately
this.$http.get('/search?q=' + searchText)
.then(function (res) {
// #2 - any code here will update the page when the HTTP call returns
});
// (#1 - if you have any code here it will also update the page immediately)
}
The technical reason for this is that Angular calls your runSearch() function inside a $scope.$apply() call, which calls your function and then runs a full digest of all the scopes in the application. That's what allows Angular to see the change and update the page. However, the function you pass to .then() on the $http promise executes some time later, after the initial digest completes. But Angular will also call this other function (the one passed to .then()) inside $scope.$apply(), which triggers another digest and allows Angular to see and apply any changes to the page at that time.
If you think about what you wanted to achieve, you only wanted to update the page after the search results are returned (#2 in the code above). So by only putting your code there, you got the page to update at the correct time, not before.

Related

AngularJS: Nested http call doesn't update the view

In a particular scenario, I need to call the the github api to retrieve a specific user's info. Then issue a second call to retrieve the user's repositories:
search(login: string): void {
this.user = undefined;
// first call
this.githubApi.getUser(login)
.then(user => {
this.user = user;
// second call
this.githubApi.getRepos(user.repos_url)
.then(reposResponse => {
this.repos = reposResponse.repos;
// I don't like to call this.$scope.$apply() !!;
});
});
}
The first call gets executed and the bound elements to this.user gets updated with no problem in the view.
The second call gets executed and the result is returned successfully and this.repos is set correctly. But, the bound elements on the view are not updated.
If I call this.$scope.$apply() in the very last line of the second callback, it makes the view update work but I guess this is not correct approach.
Any solution?
Well, if you are not willing to use $scope.apply();, try updating your getRepos service response code with:
setTimeout(
() => {
this.repos = reposResponse.repos;
}, 0
)
First you need to know , why Angular-Js is not updating the view.
You have used $scope.$apply(), so I'm assuming you already know , how it works and why we use it. Now , to the problem!
Sometimes when you make a callback - nested callback in particular - Angular does not update the view. Sometimes angular thinks that it does not need to update the view because of callbacks. And the watchers do not take action when the value changes of the variable that they are watching.
Then you use $scope.$apply() to run the digest cycle again (assuming you already know the digest cycle if you don't then let me know). And it makes the watchers to update the view.In your case, digest cycle is not running, that is why angular is not updating the view. If your digest cycle was running , angular would have given you error. So, it will tell angular to run digest cycle again because two-way binding is not working properly.
I don't think there is another way. But if there is a way, I would love to know that way. Also its not a bad approach. It was made for these kind of problems.

angularjs fire promise and redirect to external URL before it resolves [duplicate]

I'm writing a small script to capture link clicks and save the link's URL into a database table in order to track how many times each link on a particular page is clicked. The links are to external sites.
So, I capture the click event in my JavaScript function, use jQuery to post to a PHP page that saves the data in MySQL, and then the JavaScript function redirects the user to the URL of the link they clicked on.
The problem I'm running into is that it seems like the post never completes because the of redirect. I've worked around this by calling the redirect inside the post callback, but that adds a few second delay because it doesn't get called until after the post completes. I'm looking for a way to just post the data and redirect the user immediately, regardless of whether or not the post succeeds or fails.
This is the current code with the workaround. It works fine, but adds the delay:
function trackClicks(event)
{
var clicked = $(this).attr('href');
$.post
(
$('#track-click-post-url').attr('value'),
{
action: 'track_landing_page_clicks',
clicked_url: clicked,
nonce: $('#track-click-nonce').attr('value')
},
function( response )
{
window.location = clicked;
}
);
event.preventDefault();
}
And this is what I'd like to do, but when I try it never completes:
function trackClicks(event)
{
var clicked = $(this).attr('href');
$.post
(
$('#track-click-post-url').attr('value'),
{
action: 'track_landing_page_clicks',
clicked_url: clicked,
nonce: $('#track-click-nonce').attr('value')
}
);
window.location = clicked;
event.preventDefault();
}
jQuery doesn't provide a callback for what you're looking for. Here are the available ready states:
Value State Description
0 UNSENT open()has not been called yet.
1 OPENED send()has not been called yet.
2 HEADERS_RECEIVED send() has been called, and headers and status are available.
3 LOADING Downloading; responseText holds partial data.
4 DONE The operation is complete.
You're looking for readystate 2, as that's the earliest you're aware of the fact that the server received the message.
This should get you off the ground:
var xhr = new XMLHttpRequest();
xhr.open("POST", clicked);
xhr.onreadystatechange = function() {
if (xhr.readyState >= 2) window.location = clicked;
};
xhr.send($('#track-click-post-url').attr('value'));
https://developer.mozilla.org/en/XMLHttpRequest for further reading.
Why do you post using Javascript when you are going to load a page any way?
Just update the db with the link clicked on the new page.
Perhaps using the referrer URL to track on what page the click was.
Or some other solution to get on what page the click was (e.g. url param) or some other way.
When you leave a page, all pending requests are killed, and the new page loads. The 1st way is the correct way. Yes, there will be a delay when a link is clicked, that's because the POST request is running.
You can't run a request in the background of a page, if the user is not on that page.
Maybe you can save the link URL in a cookie, and put it into the DB when the next page loads.

AngularJs $http get not working 1st time but does on subsequent invocations

All:
I have an Iframe tag on a simple HTML page and inside this Iframe tag is a HTML form - lets call it a widget. Its a form where the user can enter search criteria and click on a button to obtain the search results. I have decided to use Angularjs to make the necessary DB calls to a REST service that returns the search results back to the widget for display.
In testing this I enter a value in a textbox on the widget, that value being a value that I know exists in a particular table I'm testing against.
Here is my $http get call:
$http
.get('http://localhost/XXXXService/folder/getfolders/' +
$scope.formData.srchterm **==>** mapped to search textbox
).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
//$scope.formData = response;
//$scope.nrdFolderDataArray = response;
console.log('Success retrieving data.');
console.log(response.length);
console.log('response:');
console.log(response);
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log('Error occurred retrieving data.');
console.log(response.status);
console.log('returned data count:');
console.log(response.size);
console.log('response:');
console.log(response);
});
This call is inside a function called "submitForm" that is mapped to the widget with a "ng-submit="submitForm()" attribute on the submit button. And yes there are several log statements so I can try and figure out what is going on here. And what is happening is this: after entering a value in the textbox, THE FIRST TIME the search button is clicked the .get command does not work. I does not make the call to the REST service. I know this because I wrote the REST service and made sure I put in plenty of log statements while doing so and there are no log statements in the log file from the REST service. Instead the errorCallback block runs and I see all those log statements in the console. The "response" (object?) after THE FIRST call looks like this:
Object {data: null, status: 0, config: Object, statusText: ""} Method = GET Status = (canceled) type xhr
Further, in FF there is no response data to view in the Net tab of Firebug upon THE FIRST call, but there is response data to view in all subsequent calls.
So, interestingly enough each subsequent invocation of that $http.get call works! I see log statements in the log file from the REST service method and the successCallback block runs and I can see my data via those console.log messages.
Also, if I were to change the search value in the text box, which would then be THE FIRST TIME we would be searching for data for that new key, once again, the call does not work, but does work on subsequent clicks on the "search" button for that new key value.
I really am not sure if I have a CORS issue here since, other than THE FIRST calls each subsequent $http.get call works like a champ. Its just that very first call or first call after the search key has changed that the $http.get does not want to work.
I have tried setting headers in the .get call and I have tried using
#CrossOrigin(origins = "http://localhost/7001") as per
https://spring.io/guides/gs/rest-service-cors/ but I continue to have this issue on the first time invoking this .get call.
I've been dealing with this issue for way too many hours now and would sure appreciate some input as to why this is happening.
I'm sorry for being verbose here. I wanted to explain my issue thoroughly.
Please help.
Thank you
-g

Why calling different subscription on the same collection cause error in meteor?

I working on this angular-meteor tutorial step 12
an I have a question in
Stopping a subscription Topic
you can use ctrl+f using "meteorSubscribe"
then the key sentence on that topic is
The reason is that we are calling a different subscription on the same collection inside the partyDetails controller.
the code before correction is
$scope.party = $meteor.object(Parties, $stateParams.partyId).subscribe('parties');
$scope.users = $meteor.collection(Meteor.users, false).subscribe('users');
then after correction
$scope.party = $meteor.object(Parties, $stateParams.partyId);
$scope.users = $meteor.collection(Meteor.users, false).subscribe('users');
$scope.$meteorSubscribe('parties');
I try to run before correction code and nothing(error) show in cmd but it just cause the wrong result as tutorial say
if you navigate to the party details page and then go back, pagination and search will stop working.
Then i got two question
Why no error show on cmd?
Why error from partyDetails controller affect to partiesList controller search and pagination? What is their relation?
EDIT: If you don't cancel a subscription, then if you navigate away and back again you will end up trying to subscribe twice to the same publication, resulting in the error, because subscriptions in meteor last until you end them.
There are two ways to get rid of a subscription with angular-meteor. One you can assign a handle variable to the subscription and then on navigating away from the page you can stop it. Two (the recommended way) is to use $scope.$meteorSubscribe instead of $meteor.subscribe() because it is set up to automatically remove the subscription when the scope is destroyed.
I can't see all of your code to know for sure why you are or are not getting the errors you think you should, hopefully this sheds some light on what is going on in the tutorial.
The very end result would be something like:
$meteor.autorun($scope, function() {
$meteor.subscribe('parties', {
limit: parseInt($scope.perPage),
skip: parseInt(($scope.page - 1) * $scope.perPage),
sort: $scope.sort
}).then(function() {
$scope.partiesCount = $meteor.object(Counts, 'numberOfParties', false);
$scope.parties = $meteor.collcetion(function() {
return Parties.find({}, {
sort: $scope.getReactively('sort');
});
});
});
});
Notice that he's also changing the publish function on the server. It helps to understand if you click the links to show the git diffs.

How to use $resource in AngularJS properly for building a client app?

I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!

Resources