AngularJS/Parse.com : $scope.currentUser variable not updated - javascript

I'm using parse with angularjs to authenticate users. Here is the login function.
$scope.doLogin = ->
Parse.User.logIn $scope.currentUser.username, $scope.currentUser.password,
success: (user) ->
console.log user
$scope.currentUser = user
error: (user, error) ->
console.log error
And here is the form (used twice in same page, navbar dropdown and in page content):
%form{"ng-submit" => "doLogin()"}
%input{"ng-model" => "currentUser.username", type: "text"}
%input{"ng-model" => "currentUser.password", type: "password"}
%button.btn.btn-block
%center Connexion
The problem is that whenever the form is submitted, I can see the user object in console, but $scope.currentUser doesn't always get updated. Sometimes I have to submit the form 3 or 4 times in a row for it to get updated.
What am I doing wrong ? Thank you.

perhaps you should include $scope.apply() in your success callback. From the Angularjs docs:
"$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life-cycle of exception handling, executing watches."
I had a similar problem with Parse and I'd solved it with $scope.apply().
Here is my controller
ps. note that I'm relatively new to angularjs and the above code may not be the most efficient :)

Related

JavaScript - Promise fulfilled too early?

I created a small sample application using VueJs and created a C# REST API to store and retrieve data in a SQL Server back end.
For testing, I created a simple web page with a form to create a "note". The note is stored by the following function, 'saveData()':
saveData()
{
let promiseStack = [];
var jsondata = JSON.stringify(this.note);
promiseStack.push(this.$http.post('REST_API/note', jsondata));
Promise.all(promiseStack).then(data =>
{
this.$http.get('REST_API/note');
this.$router.push({ name: 'viewnotes', params: { id: data[0].body.id }})
}, error =>
{
console.log(error);
});
}
I tried to use a promise to wait until the 'store' operation in the backend is complete, and issue a GET request to retrieve all notes once the promise is fulfilled.
However, the get request inside the promise doesn't return any data. If I issue the get request manually later one, I retrieve the data that was stored previously.
So I had look into the C# REST API. There are currently two functions: createNote(...), getAllNotes(...). I used a StreamWriter to log to the filesystem when these functions are called, using milisecond precision. What I see is that 'createNote' is called after 'getAllNotes'. So I suspect that the API is working correctly, but something with the way I'm using promises seems to be awfully wrong.
Maybe somebody has a hint?
UPDATE
I know that the GET request doesn't return any data by using the developer toolbar in Chromium. The response is empty
The developer toolbar in the network tab shows that the requests are submitted in the correct order, so the "POST" request is issued first
It seems I found the problem. I had a 'href' tag in my 'Save' link, which triggered an early routing. The intended 'POST' and 'GET' were fired correctly, but there was another 'GET' inbetween somewhere because of the 'href' tag in the link, even though it was empty.
I removed the tag, now it works as intended.

jsPlumb beforeDrop and ngToast message not showing instantly

I am working on a site that uses the great jsPlumb library to create a node interface.
jsPlumb has an event 'beforeDrop' that is triggered before a connection between two endpoints are connected, that I want to use to check a condition, and then decide to allow the connection or not.
It the connection is not allowed, I want to use ngToast to show a message to the user.
This is my 'beforeDrop' function
jsPlumb.bind('beforeDrop', function(info){
// Check that they property types match
var outNodeType = $('#'+info.sourceId).data( "ptype" );
var inNodeType = $('#'+info.targetId).data( "ptype" );
if(outNodeType !== inNodeType){
showMessage('warning', '<strong>Error:</strong> unable to connect '+outNodeType+' to '+inNodeType)
return false // false for not establishing new connection
}
return true; // true for establishing new connection
});
And this is the function that shows the ngToast message:
function showMessage(messageType, message){
ngToast.warning({
class: messageType,
content: message
});
}
The problem is that the ngToast message does not appear until I click anywhere on the page. Once I click, the message appears and everything works.
I don't know if this is an issue with jsPlumb and angularjs, or a problem with how I am calling the ngToast function.
I would really appreciate any suggestions as to how to resolve this. TIA!
jsPlumb event will be conaidered as event outside of angular context. Seems like you are calling angular code from outside of angular code. For make sync angular, you need to call $scope.apply() after calling the toaster message method. So that toast will get shown as soon as you clicked on it.

Backbone/Marionette: why shouldn't I trigger route handler on navigate?

I'm reading David Sulc's A gentle introduction to Maionette, and came across the following:
It’s important to note that the route-handling code should get
fired only when a user enters the application by a URL, not each time the
URL changes. Put another way, once a user is within our Marionette app,
the route-handling shouldn’t be executed again, even when the user
navigates around;
What's the problem with triggering a handler on navigate?
There is no difference IF you aren't already in your Marionette app. So say we are first getting into our Marionette app and we want it to initially route to the posts index page. Initially we can either
call navigate({trigger: true) or
call navigate (to update the URL) and then call App.vent to trigger the call.
Both of them will resolve in our controller's API.list function and behave exactly the same way (fetch our list of posts and then display it). So calling trigger: true when initially entering your app/routing to the first page is totally fine. I think David just tries to make it a practice to not do so to re-enforce the power of Marionette's pub/sub infastructure since with it you don't need to pass trigger: true.
However, let's say we're now in the list view displaying a list of posts. We've already spent the time of fetching our list of posts from the server when initially entering our app. Now we click on a post and want to view the show view of that post. The post already exists in memory so we can just do a App.vent.trigger "post:clicked", post to use the post already in memory to display it. If we were to instead utilize the navigate({trigger: true}) route instead we'd end up on the same page but we would have to re-fetch the individual post instead using the one already in memory.
So the main reason is because you don't need to - triggering the page would cause a reload, re-fetch, etc. It would make your app feel slow and kind of defeat the purpose of a responsive web app/single page application.
Here's what your router should look like - you always want it setup so that you can just navigate to the page via a App.vent call when inside your app AND able to handle the manual browser refresh/navigating to the route directly (which is what the trigger would do, but this is the slow load that you'd kind of expect when initially fetching resources/entering the application. When in your app you want it to be the fast responsive piece that the pub/sub infrastructure affords).
#SampleApp.module "PostsApp", (PostsApp, App, Backbone, Marionette, $, _) ->
class PostsApp.Router extends Marionette.AppRouter
appRoutes:
"" : "list"
":id" : "show"
API =
list: ->
new PostsApp.List.Controller
show: (id, post) ->
new PostsApp.Show.Controller
id: id
post: post
App.vent.on "posts:list:clicked", ->
App.navigate "/"
API.list()
App.vent.on "post:clicked", (post) ->
App.navigate "/" + post.id
API.show post.id, post
App.addInitializer ->
new PostsApp.Router
controller: API
Then to navigate there you'd just call App.vent.trigger "posts:list:clicked" from wherever you want (like after clicking a "View all posts" button and bubbling the event up to the controller and active on that event).
#listenTo bannerView, "posts:list:button:clicked", (args) ->
model = args.model
App.vent.trigger "posts:list:clicked"
EDIT:
In the controller handling the show call to avoid the re-fetch:
#SampleApp.module "PostsApp.Show", (Show, App, Backbone, Marionette, $, _) ->
class Show.Controller extends App.Controllers.Application
initialize: (options) ->
{ post, id } = options
post or= App.request "post:entity", id
App.execute "when:fetched", post, =>
#layout = #getLayoutView()
#listenTo #layout, "show", =>
#panelRegion post
#postRegion post
#bannerRegion post
#show #layout

Angular $http service, how to cancel / unsubscribe pending requests?

I have an AngularJS application which perform
- 1 request to fetch the main user profile, that contains references to the user friends,
- and then 1 request per friend to retrieve the friend profile.
When we click on a friend's profile, we load this profile as the main profile.
I am in the RDF / semantic web world, so I can't model these interactions differently, this is not the point of the question.
This is an early version of the application I'm trying to build, that can help you understand what's my problem: http://sebastien.lorber.free.fr/addressbook/app/
The code looks like:
$scope.currentProfileUri = 'http://bblfish.net/people/henry/card#me';
$scope.$watch('currentProfileUri', function() {
console.debug("Change current profile uri called with " + $scope.currentProfileUri);
loadFullProfile($scope.currentProfileUri);
})
// called when we click on a user's friend
$scope.changeCurrentProfileUri = function changeCurrentProfileUri(uri) {
$scope.currentProfileUri = uri;
}
function loadFullProfile(subject) {
$scope.personPg = undefined;
// we don't want to capture the scope variable in the closure because there may be concurrency issues is multiple calls to loadFullProfile are done
var friendsPgArray = [];
$scope.friendPgs = friendsPgArray;
fetchPerson(subject).then(function(personPg) {
$scope.personPg = personPg
fetchFriends(personPg,function onFriendFound(relationshipPg) {
friendsPgArray.push(relationshipPg);
});
},function(error) {
console.error("Can't retrieve full profile at "+uri+" because of: "+error);
});
}
So the friends are appended to the UI as they come, when the http response is available in the promise.
The problem is that the function changeCurrentProfileUri can be called multiple times, and it is possible that it is called by while there are still pending requests to load the current users's friends.
What I'd like to know is if it's possible, on changeCurrentProfileUri call, to cancel the previous http requests that are still pending? Because I don't need them anymore since I'm trying to load another user profile.
These pending requests will fill an instance of friendsPgArray that is not in the scope anymore and won't be put in the scope, so it is just useless.
Using Java Futures, or frameworks like RxScala or RxJava, I've seen there's generally some kind of "cancel" or "unsubscribe" method which permits to de-register interest for a future result. Is it possible to do such a thing with Javascript? and with AngularJS?
Yes, it is! Please, see this section of angular $http service docs. Note timeout field in config object. It does, I think, exactly what you need. You may resolve this pormise, then request will be marked as cancelled and on error handler will be called. In error handler you may filter out this cases by there http status code - it will be equal to 0.
Here is fiddle demonstrating this

Passing object between views (flash message)

What is the best way to pass message in the below scenario.
In the success scenario of $scope.p.$save, the result contains a message (res.message), which I like to display in the next view ($location.path("/test/"+res.reply.Id)). Without AngularJS, I may pass it in the url or save it in session cookies. But, I guess there might be a better way in AngularJS as there is no browser redirect and the state should be available. What is the best way to achieve this?
Setting it in rootScope shows it while I use browser back button, and the scope of the message should only for the first navigation to the new view.
function NewCtrl(Phone, $location, $rootScope, $scope) {
$scope.p = new Phone();
$scope.save = function () {
$scope.p.$save(
{},
function (res) {
$rootScope.message = res.message **//<-- this will cause message still set when using browser back button, etc**
$location.path("/test/"+res.reply.Id); **//<-- Req: needs to pass the message to next view**
}, function (res) {
//TODO
}
);
};
}
....
PhoneApp.factory('Phone', function ($resource) {
return $resource('/api/test/:_id')
});
You could use a service which displays the flash on $routeChangeSuccess.
Each time you set a flash message, add it to a queue, and when the route changes take the first item off the queue and set it to the current message.
Here's a demo:
http://plnkr.co/edit/3n8m1X?p=preview
I was looking to implement similar functionality, but actually wanted more of a growl style message.
I've updated the excellent plinkr code that Andy provided above to include a 'pop' method that leverages the toastr growl-style notification library.
My update also lets you to specify the notification type (info, warning, success, error) and title.
The 'pop' method skips adding the message to the queue, and instead pops it up on the screen immediately. The set/get functionality from Andy's previous plinkr remains mostly unchanged.
You can find my update here: http://plnkr.co/edit/MY2SXG?p=preview
I don't believe there's a way to do this default to AngularJS. Your best bet would just be passing the message (encoded) through a query string.

Categories

Resources