I'm using Angular 1.5.5, Angular-ui-router 0.2.18, angular-meteor-promiser 1.1.0 and meteor 1.3.3
In my application I use the following route definition:
(There are many other states) :
function routerConfig($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider
.state('auditionExtExec', {
url: '/AuditionExtExecute/:id',
templateUrl: 'client/auditions/auditionExtExec.html',
resolve: {
auditionsubs: function($promiser) {
"use strict";
console.log('in resolve');
return $promiser.subscribe('auditions');
},
},
controller: 'AuditionExtExecCtrl',
controllerAs: 'vm'
})
The controller is :
angular
.module('brgo')
.controller('AuditionExtExecCtrl', function($state, $stateParams, $scope, $reactive, $promiser, auditionsubs) {
var vm = this;
$reactive(vm).attach($scope);
vm.auditionId = $stateParams.id;
vm.subscribe('auditions');
vm.start = false;
vm.auditionResults = {};
var currentDate = new Date();
vm.autorun(() => {
vm.audition = Auditions.findOne({
_id: vm.auditionId
});
});
vm.addNewExecution = function() {
vm.auditionResults.auditions = angular.copy(vm.audition.auditions);
AuditionExecs.insert(vm.auditionResults);
};
vm.startaudition = function() {
vm.start = true;
};
});
The purpose of this page is to display the contact of the record from audition MongoDb table and write results to AuditionExec MongoDb table
When the application run, everything looks OK.
However, when I'm in the auditionExtExec state and I copy the URL to a new window, the view in the new window is presented twice.
When I remove the $promiser from the resolve, it looks like it works fine
Remove promiser.subscribe and the problem disappear
Related
I have an Angular 1.5.x application that uses Angular UI Router's $stateChangeSuccess event to register a listener that verifies if a user is logged in on each state change to protect certain views of the application. After porting the application into a CommonJS environment via Browserify, the registered event handlers do not execute any more, but UI Router keeps changing states as previously. I need help figuring out if and why UI Router is not emitting the event or if the problem is being caused by Angular scopes behaving differently when working in a CommonJS environment. Any help is greatly appreciated, thanks!
EDIT
I had angular-ui-router 1.0.0-alpha.1 installed via npm. I checked out the angular ui router github releases docs based on Chris's comment and decided to see if it worked by reverting to 0.2.18, it didn't.
app.js
The only require statements of relevance are the routes, core, and services modules.
'use strict';
var angular = require('angular');
var routes = require('./app.routes.js');
// Sub-module Dependencies
var core = require('./app.core.js');
var services = require('./services');
var layout = require('./layout');
var auth = require('./authentication');
var landing = require('./landing');
var dashboard = require('./dashboard');
var userManual = require('./user-manual');
// Define Angular Module
var app = angular.module('app', [
core,
services,
layout,
auth,
landing,
dashboard,
userManual
])
.config(html5ModeTrue)
.config(routes)
.run(runBlock);
////////////////
html5ModeTrue.$inject = ['$locationProvider'];
runBlock.$inject = ['authService'];
////////////////
function html5ModeTrue ($locationProvider) {
$locationProvider.html5Mode(true);
}
function runBlock(authService) {
authService.initialize();
}
app.routes.js
'use strict';
routes.$inject = ['$stateProvider', '$urlRouterProvider'];
function routes($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$urlRouterProvider.when('/dashboard', '/dashboard/analysis');
$stateProvider
.state('home', {
url: "/",
templateUrl: "app/landing/home.html"
})
.state('about', {
url: "/about",
templateUrl: "app/landing/about.html"
})
.state('atlas', {
url: "/atlas",
templateUrl: "app/landing/atlas.html"
})
.state('login', {
url: "/login",
templateUrl: "app/authentication/login.html",
controller: "LoginController",
controllerAs: "login"
})
.state('dashboard', {
abstract: true,
// url: "/dashboard",
templateUrl: "app/dashboard/dashboard.html",
controller: "DashboardController",
controllerAs: "dash",
restricted: true,
})
.state('dashboard.analysis', {
url: "/dashboard/analysis",
templateUrl: "app/dashboard/dashboard.analysis.html"
})
.state('dashboard.vmod', {
url: "/dashboard/vmod",
templateUrl: "app/dashboard/dashboard.vmod.html"
})
.state('dashboard.datasets', {
url: "/dashboard/datasets",
templateUrl: "app/dashboard/dashboard.datasets.html"
})
.state('dashboard.defaults', {
url: "/dashboard/defaults",
templateUrl: "app/dashboard/dashboard.defaults.html"
})
.state('dashboard.user-management', {
url: "/dashboard/user-management",
templateUrl: "app/dashboard/dashboard.user-management.html"
})
.state('user-manual', {
url: "/user-manual",
templateUrl: "app/user-manual/user-manual.html",
restricted: true
})
.state('user-manual.analysis', {})
.state('user-manual.vmod', {})
.state('user-manual.datasets', {});
}
module.exports = routes;
app.core.js
As shown below, angular-ui-router, which was installed via npm, is being required into the core dependencies submodule of the application
'use strict';
// Module Dependencies
require('angular-animate'); // ngAnimate
require('angular-ui-bootstrap'); // ui.bootstrap
require('angular-ui-router'); // ui.router
require('ui-leaflet'); // ui-leaflet
require('leaflet'); // Dependency for ui-leaflet
require('angular-simple-logger'); // Dependency for ui-leaflet
// Define Angular Module
var name = 'app.core';
require('angular').module(name, [
'ui.bootstrap',
'ui.router',
'ngAnimate',
'ui-leaflet',
]);
// Expose Angular Module Name For Easy Injection
module.exports = name;
services/index.js
This file simply serves as the entry point for the requiring the application's Angular services.
'use strict';
// Module Dependencies
var authService = require('./auth.service.js');
var userManagementService = require('./user-management.service.js');
// Define Angular Module
var name = 'app.services';
require('angular')
.module(name, [])
.factory('authService', authService)
.factory('userManagementService', userManagementService);
module.exports = name;
services/auth.service.js
The initialize function in this service registers a listener to the $stateChangeSuccess event, which never gets called.
'use strict';
authService.$inject = ['$q', '$window', '$http', '$rootScope', '$location'];
function authService ($q, $window, $http, $rootScope, $location) {
var userSession = null;
var service = {
initialize: initialize,
isLoggedIn: isLoggedIn,
getUserSession: getUserSession,
login: login,
logout: logout
};
return service;
////////////////
function initialize() {
var accessToken = $window.sessionStorage["fa-session"];
userSession = accessToken ? JSON.parse(accessToken) : null;
$rootScope.$on('$stateChangeSuccess', onStateChangeSuccess);
function onStateChangeSuccess(event, toState, fromState, fromParams) {
if (toState.restricted && isLoggedIn() === false) {
$location.path('/login');
}
}
}
function isLoggedIn() {
return userSession || false;
}
function getUserSession() {
return userSession;
}
function login(username, password) {
var deferred = $q.defer();
$http.post('/api/login', {username: username, password: password})
.then(loginComplete)
.catch(loginFailed);
return deferred.promise;
function loginComplete(response) {
userSession = {
accessToken: response.data.access_token,
username: response.data.username
};
$window.sessionStorage["fa-session"] = JSON.stringify(userSession);
deferred.resolve(userSession);
}
function loginFailed(error) {
deferred.reject(error);
}
}
function logout() {
var deferred = $q.defer();
$http.post('/api/logout', {headers: {access_token: userSession.accessToken}})
.then(logoutCompleted)
.catch(logoutFailed);
return deferred.promise;
function logoutCompleted(response) {
userSession = null;
$window.sessionStorage["fa-session"] = null;
deferred.resolve(response);
}
function logoutFailed(error) {
deferred.reject(error);
}
}
}
module.exports = authService;
So I managed to get it working by using the $transition.on* hooks Chris T mentioned in his comment. I also investigated if this $rootScope problem was occurring outside of the state change events. From what I was able to verify, $scope and $rootScope event emitters and handlers worked perfectly fine with my controllers.
I changed this:
.state('user-manual', {
url: "/user-manual",
templateUrl: "app/user-manual/user-manual.html",
restricted: true
})
Into this, where the restricted property was used by the even listener inside of authService:
.state('user-manual', {
url: "/user-manual",
templateUrl: "app/user-manual/user-manual.html",
// restricted: true
onEnter: function (authService, $location) {
if (!authService.isLoggedIn()) {
$location.path('/login');
}
}
})
I got a small situation I am creating a front end portion of an application using Angular and ui-router for the routing.
Te Database is holding the routes for the links. The url would look like (AdminMsgReview?init=1) which initiates the building of the frame.
I am trying to put that frame into a single page application (hence angular), I tried hard coding the url and i know it works and injects the frame into the view.
When i try to build it dynamically, I get the issue where the templateUrl is loading first before the resolve is finished getting the url.
This is the route.js
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('login', {
url: '/login',
templateUrl: function(){
return 'AdminMsgReview?init=1'
}
})
.state('app', {
url: '/app/:id/:fnid',
resolve: {
fnUrl: function(Toolbar) {
return Toolbar.getToolbar();
}
},
controller: ['$rootScope', '$stateParams','fnUrl', function($rootScope, $stateParams, fnUrl){
var mid = Number($stateParams.id) -1 ;
var fnid = $stateParams.fnid;
$rootScope.newUrl = fnUrl;
$rootScope.url = JSON.stringify($rootScope.newUrl[mid].Function[fnid].URL);
alert("Url should be = " + $rootScope.url);
}],
templateUrl: function($rootScope){
console.log($rootScope.url);
alert($rootScope.url);
return $rootScope.url;
}
})
This is my factory for getting the data:
var json = "rwd/resources/data/toolbar.json";
return{
getToolbar : function(){
return $q.all([
$http.get(json)
]).then(function(results){
var toolbar = [];
angular.forEach(results, function(result){
toolbar = result.data;
});
return toolbar.toolbar;
});
}
}
Fairly new to AngularJS and WebAPI here, and figure the best way to learn is by doing. Apologies in advance if this question seems simple - I've spent a day flipping through StackOverflow and tried them all.
I currently have a separate Master & Detail view, both with their own controller. I am trying to pass the selected ID through to the Details controller so I can query my database using the ID, though am getting "undefined" on my $routeParams. I'm unsure if I am missing something simple, or whether I'm even approaching this correctly.
The controller doesn't seem to like it when I inject '$routeParams' either.
My app.js module:
var app = angular.module("ProjectDashboardModule", ["ngRoute"]);
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider
.when("/", { templateUrl: "/Home/Index" })
.when("/Project", { templateUrl: '/Project/Index', controller: 'ProjectCrudController' })
.when("/Project/project/:id", {templateUrl:'/Project/project', controller: 'ProjectTaskController' });
$routeProvider.otherwise({redirectTo: '/home' });
$locationProvider.html5Mode(true);
}]);
my Factory.js:
app.factory('projectFactory', ['$http', function ($http) {
var urlBase = '/api/Projects/';
var projectFactory = {};
projectFactory.getProjects = function () {
return $http.get(urlBase);
};
projectFactory.getSingleProject = function (id) {
return $http.get(urlBase + '/' + id);
};
return projectFactory;
}]);
my ProjectTaskController.js:
app.controller('ProjectTaskController', ['$scope', "$routeParams", 'projectFactory', function ($scope, $routeParams, projectFactory) {
alert($routeParams.id)
$scope.project;
$scope.message;
getProjectById($routeParams.id);
function getProjectById(id) {
projectFactory.getSingleProject(id)
.success(function (data) {
$scope.project = data;
})
.error(function (error) {
$scope.message = 'error retrieving project ' + error.message;
});
}
}]);
I found that my problem was that all my angular script references were scattered. I moved all my custom script references (controller, factory, module) to index.cshtml and fixed the issue.
I've been doing quite a lot of reading about angular dependency injection and factories vs services etc like in this post here - angular.service vs angular.factory
I'm struggling putting it into practise and wonder if you can give me suggestions on how you would do it.
My current code looks like this
var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])
function mainController($scope, $firebase) {
var db = new Firebase("https://**.firebaseio.com/");
$scope.messages = $firebase(db);
$scope.addItem = function(error) {
if (error.keyCode != 13) return;
$scope.messages.$add({ name: $scope.name, price: $scope.price });
$scope.name = "";
$scope.price = "";
};
}
I decided I wanted to use angular routes and split this basic function up into two different controllers that I would use for my test app. the MainController would just display everything in the firebase db and the AdminController would be able to add messages to it
var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])
.factory('fireBaseConnectionService', $firebase)
//code in here to connect to firebase and add messages
.controller('MainController', function(fireBaseConnectionService, $scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
//code here to retrieve everything from firebase db
})
.controller('AdminController', function(fireBaseConnectionService, $scope, $routeParams) {
$scope.name = "AdminController";
$scope.params = $routeParams;
//code here to add a row to the db
})
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/', {
redirectTo: '/menu'
})
.when('/menu', {
path: '/menu',
templateUrl: 'partials/menu.html',
controller: 'MainController'
})
.when('/admin', {
templateUrl: 'partials/admin.html',
controller: 'AdminController'
})
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(false);
});
My problem is I don't want to have to connect to the firebase db in each controller. I would like to have a factory that handles this for me and has maybe functions within that that I can call from my controllers to view everything in db and to add something to the db
factory()
As we’ve seen, the factory() method is a quick way to create and configure a service.
The factory() function takes two arguments:
• name (string)
This argument takes the name of the service we want to register.
• getFn (function)
This function runs when Angular creates the service.
angular.module('myApp')
.factory('myService', function() {
return {
'username': 'auser'
}
});
The getFn will be invoked once for the duration of the app lifecycle, as the service is a singleton
object. As with other Angular services, when we define our service, getFn can take an array or a
function that will take other injectable objects.
The getFn function can return anything from a primitive value to a function to an object (similar to
the value() function).
angular.module('myApp')
.factory('githubService', [
'$http', function($http) {
return {
getUserEvents: function(username) {
// ...
}
}
}]);
service()
If we want to register an instance of a service using a constructor function, we can use service(),
which enables us to register a constructor function for our service object.
The service() method takes two arguments:
• name (string)
This argument takes the name of the service instance we want to register.
• constructor (function)
Here is the constructor function that we’ll call to instantiate the instance.
The service() function will instantiate the instance using the new keyword when creating the
instance.
var Person = function($http) {
this.getName = function() {
return $http({
method: 'GET',
url: '/api/user'
});
};
};
angular.service('personService', Person);
provider
These factories are all created through the $provide service, which is responsible for instantiating
these providers at run time.
angular.module('myApp')
.factory('myService', function() {
return {
'username': 'auser'
}
})
// This is equivalent to the
// above use of factory
.provider('myService', {
$get: function() {
return {
'username': 'auser'
}
}
});
Why would we ever need to use the .provider() method when we can just use the .factory()
method?
The answer lies in whether we need the ability to externally configure a service returned by the
.provider() method using the Angular .config() function. Unlike the other methods of service
creation, we can inject a special attribute into the config() method.
from ng-book
All you have to do is just move the firebase connection into the service, and inject that service wherever you want . The connection line will execute the first time your app runs, given that you front load the service when your app runs, as you seem to be doing now:
.factory('fireBaseConnectionService', function($firebase){
var db = $firebase(new Firebase("https://**.firebaseio.com/"));//creating
//the firebase connection this line executes only once when the service is loaded
return{
getMessage:function(){
return db.whatever;
}
}
})
If you load the service script dynamically, on route where you need it, it will only connect to the database when it reaches that route. The code above will create one connection to the database, as the connection line is executed only once.
Just for anyone interested with the help of the answers above and this link - Firebase _ AngularJS this is what I ended up doing
var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])
.factory('fireBaseConnectionService', ["$firebase", function($firebase) {
var db = new Firebase("https://***.firebaseio.com/");
return {
getMessages: function() {
return $firebase(db);
},
addMessage: function(message) {
var messages = $firebase(db);
messages.$add(message);
}
}
}])
.controller('MainController', ["fireBaseConnectionService", "$scope", function (fireBaseConnectionService, $scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
$scope.messages = fireBaseConnectionService.getMessages();
}])
.controller('AdminController', ["fireBaseConnectionService", "$scope", function(fireBaseConnectionService, $scope, $routeParams) {
$scope.name = "AdminController";
$scope.params = $routeParams;
$scope.addItem = function(error) {
if (error.keyCode != 13) return;
fireBaseConnectionService.addMessage({ name: $scope.name, price: $scope.price });
$scope.name = "";
$scope.price = "";
}
}])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/', {
redirectTo: '/menu'
})
.when('/menu', {
path: '/menu',
templateUrl: 'partials/menu.html',
controller: 'MainController'
})
.when('/admin', {
templateUrl: 'partials/admin.html',
controller: 'AdminController'
})
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(false);
});
I have an interesting issue with the $http.delete() and $window.location.reload() functions. Basically I am calling a delete method that in turn calls the $http.delete() call which - using a REST API interface - removes data from a database (mongoDB). For reasons unknown this is what's happening:
delete is successful in the database
this is verified as the data is no longer in the database
also monitored using Chrome DevTools it shows status: 200
$window.location.reload() gets called and nothing happens
Chrome DevTools shows a GET call to the root of the domain but the status is 'pending'
The page does not time out, it basically keeps on loading and loading and loading. Once I hit refresh / CTRL-F5 all goes back to normal and I can see that my item has been deleted.
Some excerpts from my code:
app.js
angular.module('contacts', ['ngRoute', 'contacts.factory', 'contacts.filters', 'ui.bootstrap']).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: '/p/list',
controller: ListCtrl,
}).
otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}]);
controllers.js
function ListCtrl($scope, $modal, contactFactory) {
//code removed
$scope.delete = function(c) {
var id = c._id;
var modalInstance = $modal.open({
templateUrl: 'deleteContactModal',
controller: deleteContactModalCtrl,
resolve: {
contact: function() {
return contactFactory.getContact(id);
}
}
});
}
}
var deleteContactModalCtrl = function($scope, $route, $modalInstance, $window, contact, contactFactory) {
$scope.name = contact.data.contact.name;
$scope.deleteContact = function() {
contactFactory.deleteContact(contact.data.contact._id).success(function() {
//have also tried: $window.location.reload(true);
//as well as window.location.href('/') - didn't work either
$modalInstance.close($window.location.reload());
});
};
}
factory.js
angular.module("contacts.factory", []).
factory('contactFactory', function($http){
return {
//code removed
deleteContact: function(id) {
return $http.delete('/api/contact/' + id);
}
}
});
backend - app.js
//usual express setup
app.delete('/api/contact/:id', api.delete); //delete contact
backend - api.js
//code removed
exports.delete = function (req, res) {
var id = req.params.id;
if (id) {
ContactModel.findById(id, function (err, contact) {
contact.remove(function (err) {
if (!err) {
res.json(true);
} else {
res.json(false)
console.log(err);
}
});
});
}
};
At the moment I'm not even sure if this issue is related to the frontend or to the backend. As mentioned before - the backend portion is working fine - the data is deleted from the DB.
Okay the solution seems to be the following:
$scope.deleteContact = function() {
contactFactory.deleteContact(contact.data.contact._id).success(function() {
$modalInstance.close();
contactFactory.getContacts().success(function(contacts) {
return $scope.contacts = contacts;
});
$window.location.reload();
});
};
I need to get the getContacts() method from my factory again during the delete method.