This is my code for initializing the app and creating a controller.
var app = angular.module('newstalk',[]);
app.controller("articleCtrl",['$scope','$http','dataService',function($scope,$http,dataService){
$scope.articles = dataService.getArticles();
$scope.commentForm = function(id,userid){
console.log(userid);
var uid = userid;
var c = this.contents;
var data = {
content: c,
user: uid
};
console.log(data);
$http.post('/api/article/'+id,data);
};
}]);
Now, I have also created a service to fetch the data from the server. Here is the code for that:
(function(){
angular.module('newstalk')
.factory('dataService',dataService);
function dataService(){
return {
getArticles : getArticles
};
function getAricles(){
console.log("yolo");
return $http({
method:get,
url:'/api/articles/0'
})
.then(sendResponse);
}
function sendResponse(response){
console.log(data);
return response.data;
}
}
})
This is in a seperate file. Now when I run this I get a Error: $injector:unpr Unknown Provider error.
I've read multiple other such questions, none of which came to help. Any ideas?
I think you have not used IIFE correctly.
you should put () at the end of file.
(function(){
angular.module('newstalk')
.factory('dataService',dataService);
function dataService(){
return {
getArticles : getArticles
};
function getAricles(){
console.log("yolo");
return $http({
method:get,
url:'/api/articles/0'
})
.then(sendResponse);
}
function sendResponse(response){
console.log(data);
return response.data;
}
}
})()
putting () execute/run the function. rightnow you are not executing IIFE.
Related
My logout function, linked to a logout button is:
$scope.logoutUser = function() {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
ref.unauth();
console.log(ref.getAuth);
$state.transitionTo('login');
}
When I click logout, it prints this to the console:
function (){x("Firebase.getAuth",0,0,arguments.length);return this.k.P.we()}
I am checking for authData in my other controller with:
CONTROLLER:
.controller('SearchCtrl',
function ($scope, $http, Movie, $state, UsersRef, AuthData, $timeout) {
$scope.$on('$ionicView.enter', function () {
if (!AuthData) {
console.log("Auth data null!");
swal("Unauthorized", "You are not logged in", "error");
$state.transitionTo('login');
} else {
console.log("Auth data found: " + AuthData);
//do stuff
}
});
})
FACTORY:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
});
return data;
}
])
If I logout, then go back to the page linked to SearchCtrl by changing the URL, it still says it found the authData.
However, if I try and go to the search page the FIRST time I open the app, before anybody has logged in, it gives me the right error message and exits out to the login page.
How can I ensure the user can't go back into the app after logging out?
Welcome to async programming 101.
Firebase's onAuth methods listens for changes on auth state. When the auth state changes, the callback method you provide is invoked. But while it's waiting for auth state changes, your other code continues to run.
It most easy to see this if you add some log statements to your code:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
console.log('before onAuth');
ref.onAuth(function (authData) {
console.log('in callback');
if (authData) {
data = authData;
}
});
console.log('after onAuth');
return data;
}
])
The output is going to be:
before onAuth
after onAuth
in callback
Which is likely not what you expected when you wrote this code.
The simplest way to fix this in your code is to use the synchronous ref.getAuth() method:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
return ref.getAuth();
}
])
But you're going to run into this asynchronicity problem quite often. I highly recommend using and studying AngularFire instead of reinventing the wheel.
You are never cleaning data inside AuthData so it will always have data after the first guy logs in. I'm not familiar with Firebase but you need something like this in your AuthData factory:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
else
data = null;
});
return data;
}
])
I'm using Express and trying to teach myself node/javascript callbacks and I've stumbled across something.
I have a route that looks like this:
var express = require('express');
var router = express.Router();
var api = require('../api');
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.methodname(res);
});
module.exports = router;
And then the module that is being called above looks like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function(res) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
res.setHeader("Content-Type","application/json");
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
res.send(returnInstances);
})
.on('error', function(resp){
console.log(resp);
})
}
};
module.exports = modulename;
As you can see I'm passing through the response parameter through to my module, but I'd rather pass back instances and then in the route return api.modulename.instances, like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function() {
var request = new library.asyncMethod();
request.on('success', function(resp) {
var returnData = resp.data;
instances = {
id: returnData[0].InstanceId,
state: {name: returnData[0].State.Name, code: returnData[0].State.Code}
};
})
.on('error', function(resp){
console.log(resp);
})
.send();
}
};
module.exports = modulename;
However, when I do, it's coming through as the default value {} but if I run it as above, I do get output so I know that there should be data in there.
Let me know if I have misunderstood your issue. If you are saying you want to pass back objects from getAllInstances then you pass in a callback and call it from the event handler like this-
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.getAllInstances(res, function(err, instances){
if(err){ ... }
else{
res.send(instances); //or however you want to use instances
}
});
});
and in getInstances
var modulename = {
getAllInstances: function(res, cb) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
cb(null, instances);
})
.on('error', function(err){
cb(err, null));
});
//.send(); not sure what this is it seems to be request.send() ??
}
};
The problem here lies with when the response from the API call is available. The event loop in Node means code won't block until the API replies with a response. Hence a callback is needed to handle that response when it becomes available. You probably want to use the API response in your Express router response so there's a chain of dependency.
One strategy here would be to use promises and not callbacks, it would alleviate some of the pain you're experiencing with async response from the API call.
In your routes:
router.get('/',function(req, res, next){
var instances = [];
// The function below could be refactored into a library to minimise controller code.
var resolver = function (response) {
var data = JSON.parse(response);
instances.push({
name: data[0].State.Name,
code: data[0].State.Code
});
res.render('instances'. {instances : instances});
};
modulename.methodname(resolver);
});
And in your module:
var rp = require('request-promise'); // Also see q-io/http as an alternate lib.
var modulename = {
methodname: function (resolver) {
rp('http://the-inter.net')
.then(resolver)
.catch(console.error);
}
};
This might not cut-n-paste work but have a look at the request-promise examples for further clarification.
I have LoginController and securityService.
This is LoginCtrl
// place the message if something goes wrong
$scope.authMsg = '';
$scope.login = function () {
$scope.authMsg = '';
var loginData = {email: $scope.account.email, password: $scope.account.password};
securityService.login(loginData);
};
This is securityService
login: function (logData) {
var _vm = this;
$http
.post('/api-token-auth/', logData)
.then(function (response) {
// assumes if ok, response is an object with some data, if not, a string with error
// customize according to your api
if (!response.data.token) {
_vm.authMsg = 'Incorrect credentials.';
} else {
$cookieStore.put('djangotoken', response.data.token);
$http.defaults.headers.common.Authorization = 'JWT ' + response.data.token;
$http.get('/api/account/restricted/').then(function (response) {
authService.loginConfirmed();
_vm.currentUser = response.data;
$rootScope.currentUser = response.data;
});
}
}, function (x) {
_vm.authMsg = 'Server Request Error';
});
},
This login is working fine but my problem is i don't know how can get the authMesg from service to controller because that is async. Everytime i get blank message in case of invalid login
you need to use promise service of angular to make you controller and service syn
login: function (logData) {
var _vm = this,d= $$q.defer();
$http
.post('/api-token-auth/', logData)
.then(function (response) {
// assumes if ok, response is an object with some data, if not, a string with error
// customize according to your api
if (!response.data.token) {
_vm.authMsg = 'Incorrect credentials.';
} else {
$cookieStore.put('djangotoken', response.data.token);
$http.defaults.headers.common.Authorization = 'JWT ' + response.data.token;
$http.get('/api/account/restricted/').then(function (response) {
authService.loginConfirmed();
_vm.currentUser = response.data;
$rootScope.currentUser = response.data;
});
}
d.resolve(vm.authMsg);
}, function (x) {
_vm.authMsg = 'Server Request Error';
d.reject(vm.authMsg);
});
},
In controller you need to resolve this promise
securityService.login(loginData).then(function(data){
consol.log(data); // get success data
},function(error){
consol.log(data); // get error message data
})
and inject $q in your service.
This will give you authMsg
securityService.login(loginData).authMsg
But follow #Vigneswaran Marimuthu comments, that is best practice.
I'm adding a fresh, angular client-tier to a legacy app. Upon login the legacy up redirects to a 'home' url. The url contains a session id which I need to grab and use (in the url) for any subsequent gets/posts. After login I call:
browser.getCurrentUrl()
and then use a regex to extract the session id. I store the session id away and use it for later gets/posts.
The problem is though that browser.getCurrentUrl() returns a promise and all my tests run before I can get the session id back. How can I make protractor wait for the browser.getCurrentUrl() to resolve.
Specifically below where I have the code:
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
I really need all code to block on loginPage.login() so I'll have a defined session id. My home page tests and any other page tests will need the session id to run properly.
How can I achieve this in protractor?
Thanks!
The relevant parts of my code looks like this...
home.spec.js:
describe('home page tests', function() {
var loginPage = new LoginPage();
var homePage;
// get sessionId from login and create a new HomePage object from it
beforeEach(function() {
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
homePage.get();
});
describe('main elements of home page test', function() {
it('page has correct username as part of user menu', function() {
expect(homePage.getUsername()).toEqual('testuser#example.com');
});
});
});
login.po.js:
function LoginPage {
// ...snip...
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
}
home.po.js:
function HomePage(sessionId) {
this.username = element(by.binding('username'));
this.getUsername = function() {
return this.username.getText();
}
this.get = function() {
return browser.get(browser.baseUrl + sessionId + '#/home');
};
};
module.exports = HomePage;
The simplest could be to use expect:
Jasmine expectations are also adapted to understand promises. That's why the line
`expect(name.getText()).toEqual('Jane Doe');
works - this code actually adds an expectation task to the control flow, which will run after the other tasks.
login.po.js:
function LoginPage {
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
expect(this.login).not.toBeUndefined();
}
Angular doc states:
Angular services are singletons
I want to use the angular service as singleton, so I can access the logged-in user data every where in my application. but the serivce does not seem to return the same data, here is my codes.
Service:
angular.module("myapp", [])
.service("identity", function (){
this.token = null;
this.user = null;
});
Facotry:
.factory("authentication", function (identity, config, $http, $cookieStore) {
var authentication = {};
authentication.login = function (email, password, remember) {
var p=$http.post(config.baseUrl+"api/","email="+email+"&password="+password);
return p.then(function (response) {
identity= response.data;
if (remember) {
$cookieStore.put("identity", identity);
}
});
};
authentication.isAuthenticated = function () {
if (!identity.token) {
//try the cookie
identity = $cookieStore.get("identity") || {};
}
console.log(identity) // {token: 23832943, user: {name: something}}
return !!identity.token;
};
return authentication;
});
controller:
.controller('LoginCtrl', function ($state, $scope, authentication, identity) {
var user = $scope.user = {};
$scope.login = function () {
authentication.login(user.email, user.password, user.remember)
.then(function () {
if (authentication.isAuthenticated()) {
console.log(identity); // {token:null, user: null}
$state.transitionTo("dashboard");
}
});
};
});
The identity is injected to both authentication and controller. But the first console logs the correct user data, while the second console just logs the same data as initially defined. If the service is singleton as stated, I would expect two identity returns the same data. What am I doing wrong here?. any pointers are appreciated.
In your authentication service change
identity= response.data;
to
identity.token=response.data.token;
identity.user=response.data.user;
and things should work.
Basically what you are doing is replacing the identity object reference.