I found it is very hard to deal with flash message returned from backend when working with single-page-application.
Say there are five actions:
login, logout,search,add,delete.
each action emits messages(successful or failed,could be more). in a Non-SPA, it is easy enough to do something like backend.getFlash() in the template to get the message stored in session.
However, in SPA, if the results are returned in JSON, and of course backend can pass the message to a js framework, say angularjs. But it is really tedious and not flexible at all.
Does anyone have any idea or experience?
Thanks
From your question I understood that what you really want is to have some messages for every actions, say for delete you need to display a message like "Deleted Successfully" and you don't want this message to be send along with the result set from your backend.
In AngularJS you can achieve this by having a service for showing the messages.
Here is an example;
Create a service like this in AngularJS
app.factory('FlashService', function ($rootScope) {
return {
show: function (message) {
$rootScope.flash = message;
return message;
},
clear: function () {
$rootScope.flash = '';
return null;
}
};
});
Here FlashService is the name of the service you are creating, don't bother about the name you can give any name of your choice.
To use this service inject it anywhere you want like;
var app = angular.module("your app name", []);
app.controller("MyCtrl", function(FlashService) {
FlashService.show("I am here");
});
And use it in your html like
<div id="flash">{{flash}}</div>
Related
I have a simple angular resource that I've defined as below:
CompanyService.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
);
I then have a controller that calls that resource passing in a success and fail function:
.controller('companyList', function($scope, CompanyService) {
$scope.companies = CompanyService.query(
function(data) {
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
The rest API is a .NET MVC Web API that is extremely basic. I've configured it to return JSON and it simply returns an array of two objects like below. I've also enabled CORS so my angular app, which is hosted in a different domain, can call the api.
[{ID:1, Name:"TEST1"}, {ID:2, Name:"TEST2"}]
I've tested the REST call using jquery and just straight call through browser. All was functional (including the cross site scripting when calling from my angular app just using a straight JavaScript HTTP call).
When I try to call the api from my controller however, it always ends up in the error function. The error object contains a data property that is always populated with the string "resource is required|resource is required|undefined"
When I check the network I see no call to the values end point. It's as if the call is failing before ever being made.
If I change out the url to point to some sample REST api like https://jsonplaceholder.typicode.com/users/ it works fine and I'm able to see the call to "users" in the network traffic, which makes me think there is something wrong with my C# REST endpoint, however all my tests to call the REST endpoint outside of angular work successfully.
Can anyone help? I can't find anyone reporting this issues before anywhere on the net.
should the code be the one below? i didn't test it, just guess.
myModule.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
)
.controller('companyList', function($scope, CompanyService) {
CompanyService.query(
function(data) {
$scope.companies = data;
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
I ended up rebuilding my angular app from scratch. My first app was from the angular-seed github and had a handful of libraries already added in for testing and other things. One of those things is was was leading to this error as once I started a new project completely from scratch and added in angular and my REST call things worked perfectly. I've already spent too much time working through this so not going to spend any more time identifying exactly what it is but in case anyone else runs into the problem I did want to answer this one and close the book on it.
I have a DB with a collection of games and a collection of comments about those games. each game has a unique ID and each comment has a field game_id.
in the client side, I wrote an async function in the scope that retrieves the number of comments for a game from the db and return a promise. what is the right (and most cleanest) way to show the value in the HTML ?
I know there are different ways to do this. I am looking for the simplest and cleanest way.
Will this code work?
//code in HTML
<div ng-repeat="game in games">
<span>{{game.date}}</span>
<span>{{game.home}}</span>
<span>{{game.away}}</span>
<span>{{getNumberOfComments(game.id)}} Comments</span>
</div>
app.factory('apiService', function($http) {
return {
getGames: function() {
return $http.get('/api/games',{cache: true});
},
getCommentsCount: function(game_id) {
return $http.get('/api/CommentsCount/' + id);
}
};
});
app.controller('MyCtrl', function ($scope, apiService) {
gameService.getGames().then(function(resp) {
$scope.games = resp.data;
});
$scope.getNumberOfComments = function(id){
return apiService.getCommentsCount(id).then(function(resp){
return resp.data;
});
});
});
Followup question:
if I have many games (lets say 30), is it a bad idea to send 30 HTTP requests to the server for each game? is it better to send one request called "getAllComments" and retrieve the comments count for each game from the repsonse ?
for example:
function getAllComments() {
$http.get('/allComments',function(data){
$scope.games.forEach(function(game){
game.commentsCount = data[game.id];
});
});
}
//code in HTML
<div ng-repeat="game in games">
<span>{{game.commentsCount}} Comments</span>
</div>
This seems like a good solution - but the problem starts when i have more than 30 games but i want to show 30 games to the user and give him the possibility to load more. Then im actually asking more comments than I need and i have to run this function every time the user loads more games. it seems like a complicated and efficient solution.
EDIT:
One solution is to aggregate the information on the server side and have the client send 1 HTTP request and the response will include all the information. To implement this I will need to iterate the list of games in the server side and for each game retrieve the comments count from the comment collection. The downside of this solution is that the it will take longer for the client to see the list of games. I rather the client to first see the list of games as soon as possible and then after that to load the number of comments. Another downside is the in NodeJS (the backend framework that I'm using), a code like that will block the main thread from serving other clients. Therefore I do not want to make any iterations on the server side.
One more thing that needs to take into account is caching. I want to save the HTTP request that retrieves the games in the $http cache. Because the list of games is static and not going to change, unlike the comments count, that is always increasing. This way when the client moves to a different route and comes back to the games list, the games will show up immediately. But I do want to load the comments count every time the user comes back to the games page.
A clean way to do this would be to first move the code that makes the http call to a service:
app.factory('gameService', function($http) {
return {
getGames: function() {
return $http.get('/api/games');
}
};
});
Then, inject the service in your controller, call the get method and set the value on scope to be used by the view:
app.controller('MyCtrl', function ($scope, gameService) {
$scope.model = {};
gameService.getGames().then(function(resp) {
$scope.model.games = resp.data;
});
});
And your html would be like this:
<div ng-repeat="game in model.games">
<span>{{game.comments.length}} Comments</span>
</div>
Note that this assumes that the endpoint returns all the games and each game has a field for the comments. For example, the schema for a Game might look like this:
{
"id": "1",
"name": "The best game",
"comments": [
{
"id": "c1",
"content": "The is the best game ever"
}
]
}
I am writing end-to-end tests for my AngularJS-based application using Protractor. Some cases require using mocks to test - for example, a network connection issue. If an AJAX request to server fails, the user must see a warning message.
My mocks are registered in the application as services. I want them to be accessible to the tests to write something like this:
var proxy;
beforeEach(function() { proxy = getProxyMock(); });
it("When network is OK, request succeeds", function(done) {
proxy.networkAvailable = true;
element(by.id('loginButton')).click().then(function() {
expect(element(by.id('error')).count()).toEqual(0);
done();
});
});
it("When network is faulty, message is displayed", function(done) {
proxy.networkAvailable = false;
element(by.id('loginButton')).click().then(function() {
expect(element(by.id('error')).count()).toEqual(1);
done();
});
});
How do I implement the getProxyMock function to pass an object from the application to the test? I can store proxies in the window object of the app, but still do not know how to access it.
After some reading and understanding the testing process a bit better, it turned to be impossible. The tests are executed in NodeJS, and the frontend code in a browser - Javascript object instances cannot be truly shared between two different processes.
However, there is a workaround: you can execute a script inside browser.
First, your frontend code must provide some sort of service locator, like this:
angular.module('myModule', [])
.service('proxy', NetworkProxy)
.run(function(proxy) {
window.MY_SERVICES = {
proxy: proxy,
};
});
Then, the test goes like this:
it("Testing the script", function(done) {
browser.executeScript(function() {
window.MY_SERVICES.proxy.networkAvailable = false;
});
element(by.id('loginButton')).click().then(function() {
expect(element.all(by.id('error')).count()).toEqual(1);
done();
});
});
Please note that when you use executeScript, the function is serialized to be sent to browser for execution. This puts some limitations worth keeping in mind: if your script function returns a value, it is a clone of the original object from browser. Updating the returned value will not modify the original! For the same reason, you cannot use closures in the function.
I am running an Angular app with a Parse backend. When I make a query, the app freezes, even when the DOM doesn't depend on the data being grabbing at the time. That is why this answer was not so helpful. Is there any way to have the rest of the app run along while some request is in progress?
Below is the code that freezes everything until the request resolves. The Parse Javascript SDK comes with promises, which is what 'QS.errorQuery.find()` returns.
function get() {
var queries = [
QS.errorQuery.find(),
QS.resLogQuery.find()
];
return $q.all(queries)
.then(function(res) {
return {
errors: res[0],
logs: res[1]
}; //end return
}); //end return
}; //end get
get().then(function(res) {
$scope.lineData = DP.bigLineParser(res.errors, res.logs);
});
The request is async so as the response is coming in, your UI wont freeze up. The long delay is probably happening when Parse gets its response from the server and parses it.
I am fairly new to AngularJS and am trying to learn some best practices. I have things working, but would like to start adding some unit tests to my modules and controllers. The first one I am looking to tackle is my AuthModule.
I have an AuthModule. This Module registers a Factory called "AuthModule" and exposes things like "setAuthenticatedUser" and also fields like "isLoggedIn" and "currentUser". I think this is a fairly common pattern in an AngularJS application, with some variations on the specific implementation details.
authModule.factory(
'AuthModule',
function(APIService, $rootScope) {
var _currentUser = null;
var _isLoggedIn = false;
return {
'setAuthenticatedUser' : function(currentUser) {
_currentUser = currentUser;
_isLoggedIn = currentUser == null ? false : true;
$rootScope.$broadcast('event:authenticatedUserChanged',
_currentUser);
if (_isLoggedIn == false) {
$rootScope.$broadcast('event:loginRequired')
}
$rootScope.authenticatedUser = _currentUser;
$rootScope.isLoggedIn = _isLoggedIn;
},
'isLoggedIn' : _isLoggedIn,
'currentUser' : _currentUser
}
});
The module does some other things like register a handler for the event "loginRequired" to send the person back to the home screen. These events are raised by the AuthModule factory.
authModule.run(function($rootScope, $log, $location) {
$rootScope.$on("event:loginRequired", function(event, data) {
$log.info("sending him home. Login is required");
$location.path("/");
});
});
Finally, the module has a run block which will use an API service I have to determine the current logged in user form the backend.
authModule.run(
function(APIService, $log, AuthModule) {
APIService.keepAlive().then(function(currentUser) {
AuthModule.setAuthenticatedUser(currentUser.user);
}, function(response) {
AuthModule.setAuthenticatedUser(null);
});
});
Here are some of my questions:
My question is how would you setup tests for this? I would think that I would need to Mock out the APIService? I'm having a hard time because I keep getting unexpected POST request to my /keepalive function (called within APIService.keepAlive())?
Is there any way to use $httpBackend in order to return the right response to the actual KeepAlive call? This would prevent me from having to mock-out the API service?
Should I pull the .run() block out which obtains the current logged in user out of the AuthModule and put it into the main application? It seems no matter where I put the run() block, I can't seem to initialize the $httpbackend before I load the module?
Should the AuthModule even be its own module at all? or should I just use the main application module and register the factory there?
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.angularjs docs
I suggest you take a look at this authentication service, using a service is the way to go.
Hopefully this would help ... Good luck