How to make Provider while I have Factory? - angularjs

Currently I'm working with Blur Admin template and I have a case which sidebar menu should appear depends on user role.
After gave it a try I discover that if I comment $stateProvider.state(....); for example in app/pages/components/components.module.js so Components menu will disappear from sidebar menu.
So from here I think I can implement defining state depends on user role maybe something like this
if(user_role == "partner"){
$stateProvider.state(....);
}
So on in other pages module.
So far I have factory which check current user role which is get from localStorage
My problem is state definition located at .config() which only accept Provider.
So my question is How I can make provider to check current user role when I have factory to do it.
Here is app/pages/components/components.module.js
angular.module('BlurAdmin.pages.components', [
'BlurAdmin.pages.components.mail',
'BlurAdmin.pages.components.timeline',
'BlurAdmin.pages.components.tree',
])
.config(routeConfig);
// should .provider("CheckCurrentUserRole", CheckCurrentUserRole);
function CheckCurrentUserRole(){
///provider code here
}
/** #ngInject */
function routeConfig($stateProvider, /*CheckCurrentUserRoleProvider*/) {
//if(CheckcurrentUserRole.method() == "partner"){
$stateProvider
.state('components', {
url: '/components',
template : '<ui-view></ui-view>',
abstract: true,
title: 'Components',
sidebarMeta: {
icon: 'ion-gear-a',
order: 200,
},
});
//} so if current user is not partner then components sidebar menu doesn't appear.
}
Here is my factory to check user role
angular.module('BlurAdmin.pages')
.factory('authFactory', function (sessionFactory, $http) {
var authFactory = {};
...
authFactory.isPartner = function () {
if(sessionFactory.get('role') == "partner"){
return true;
};
};
authFactory.isCustomer = function () {
if(sessionFactory.get('role') == "customer"){
return true;
};
};
authFactory.isAdmin = function () {
if(sessionFactory.get('role') == "admin"){
return true;
};
};
authFactory.currentRole = function () {
return sessionFactory.get("role");
}
return authFactory;
})
.factory('sessionFactory', function () {
var sessionFactory = {};
...
sessionFactory.get = function (key){
return localStorage.getItem(key);
};
...
return sessionFactory;
});
The above is my logic as new in angular, if you have better approach or correction please let me know.

Related

Angular string binding not working with ControllerAs vm

I've been trying to do a two-way bind to a string variable on the Controller. When the controller changes the string, it isn't updated right away. I have already run the debugger on it and I know that the variable vm.overlay.file is changed. But it isn't updated on the View... it only updates the next time the user clicks the button that fires the selectOverlayFile() and then it presents the previous value of vm.overlay.file
Here goes the code:
(function () {
angular
.module("myapp.settings")
.controller("SettingsController", SettingsController);
SettingsController.$inject = [];
function SettingsController() {
var vm = this;
vm.overlay = {
file: undefined,
options: {
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
destinationType: Camera.DestinationType.DATA_URL
}
};
vm.errorMessages = [];
vm.selectOverlayFile = selectOverlayFile;
vm.appMode = "photo";
vm.appModes = ["gif-HD", "gif-video", "photo"];
activate();
function activate() {
}
function selectOverlayFile() {
navigator.camera.getPicture(successOverlay, errorOverlay, vm.overlay.options);
}
function successOverlay(imageUrl) {
//If user has successfully selected a file
vm.overlay.file = "data:image/jpeg;base64," + imageUrl;
}
function errorOverlay(message) {
//If user couldn't select a file
vm.errorMessages.push(message);
}
}
})();
Thanks!
After a couple of hours searching for the issue and testing various solutions. I finally found it. The issue was that when the navigator.camera.getPicture(successOverlay, errorOverlay, vm.overlay.options) calls the callback function, it is out of AngularJS scope. So we need to notify Angular to update binding from within these callbacks using $scope.$apply():
(function () {
angular
.module("myapp.settings")
.controller("SettingsController", SettingsController);
SettingsController.$inject = ["$scope"];
function SettingsController($scope) {
var vm = this;
vm.overlay = {
file: undefined,
options: {
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
destinationType: Camera.DestinationType.DATA_URL
}
};
vm.errorMessages = [];
vm.selectOverlayFile = selectOverlayFile;
vm.appMode = "photo";
vm.appModes = ["gif-HD", "gif-video", "photo"];
activate();
///////////////////
function activate() {
}
function selectOverlayFile() {
navigator.camera.getPicture(successOverlay, errorOverlay, vm.overlay.options);
}
function successOverlay(imageUrl) {
//If user has successfully selected a file
vm.overlay.file = "data:image/jpeg;base64," + imageUrl;
$scope.$apply();
}
function errorOverlay(message) {
//If user couldn't select a file
vm.errorMessages.push(message);
$scope.$apply();
}
}
})();

Clear rootscope ionic framework

Hi I'm doing a login for ionic app.....and I'm using rootscope like a global variable to use in all controller (LoginCtrl,SalirCtrl) When the user is log-in I save his info in a rootscope variable and show that info in SalirCtrl.
BUt when user log-out and other user log-in his info is not present in SalirCtrl.
Someone knows about that.
LoginCtrl
if($scope.datos=='true') {//if token is true. User is log-in
$rootScope.pNombre=data.persona.primerNombre;
$rootScope.sNombre=data.persona.segundoNombre;
$rootScope.pApellido=data.persona.primerApellido;
$rootScope.sApellido=data.persona.segundoApellido;
$state.go('tabs.perfil');
}
SalirCtrl
.controller('SalirCtrl', function($scope, $state, $ionicPopup, ServUsuario,$rootScope,$ionicHistory) {
//para bloquear el boton atras
$ionicHistory.nextViewOptions({
disableAnimate: true,
disableBack: true
});
//FIN para bloquear el boton atras
$scope.pNombre = $rootScope.pNombre;//save in a scope variable rootscope
$scope.sNombre = $rootScope.sNombre;
$scope.pApellido = $rootScope.pApellido;
$scope.sApellido = $rootScope.sApellido;
//METODO SALIR
$scope.salir = function() {
var confirmPopup = $ionicPopup.confirm({
title: 'Log-out',
template: '¿Log-out?'
});
confirmPopup.then(function(res) {
if(res) {
console.log('You are sure');
$state.go('login');
$scope.pNombre=" "; //When log-out is true. Variables equals empty
$scope.sNombre=" ";
$scope.pApellido=" ";
$scope.sApellido=" ";
} else {
console.log('You are not sure');
}
});
};
//FIN METODO SALIR
})
Finally I print that variables in perfil.html
{{pNombre}} {{sNombre}} {{pApellido}} {{sApellido}}
Thanks....!
My guess is that you're suffering from caching in the AngularJS UI-Router. In your route config, what happens when you do something like this?:
$stateProvider.state('myState', {
cache: false,
url : '/myUrl',
templateUrl : 'my-template.html'
})
By default Ionic caches the views as described here:
http://ionicframework.com/docs/api/directive/ionNavView/
When the views are cached your controllers don't reload during navigation. Let me know if that did the trick?
Best,
Your goal is to implement a User Service that can store user credentials and that can be accessed from all of your controllers. See an example implementation below:
angular.module('your-app').service('myUserService', myUserService);
function myUserService() {
var _identity = null;
return {
getUser: getUser,
setUser: setUser,
login: login,
logout: logout
...
};
function getUser() {
return _identity;
}
function setUser(user) {
_identity = user;
}
function login() {
//your login logic here
}
function logout() {
_identity = null;
//other logout logic
}
...
}

using resolve in a ui router parent state

i was trying to use Angular JS to consume web api of a simple patient DB , where each patient is having a list of medications.
each of the medication and the patient has its own add , edit and list actions.
when i tried to make the edit of a patient, i have added the option of adding a new medicine(this should also be sort of editing the patient entry).
so i have used the nest state concept of nested states, where the patient edit is the parent state, while the patientedit.Medications is the child state as follows:
.state("patientEdit", {
abstract: true ,
url: "/patient/edit/:PatID",
templateUrl: "app/patients/patientEditView.html",
controller: "PatientEditCtrl as vm",
resolve: {
patientResource: "patientResource",
patient: function (patientResource, $stateParams) {
console.log("the value of PatID is " + PatID);
console.log("Patient Edit is called ???")
return patientResource.get(
{ PatID: PatID }).$promise;
}
}
})
.state("patientEdit.Medications", {
url: "/medications/:MedID/:PatID",
templateUrl: "app/patients/patientEditMedicationsView.html",
controller: "medicationEditCtrl as vm",
resolve: {
medicationResource: "medicationResource",
medication: function (medicationResource, $stateParams) {
var MedID = $stateParams.MedID;
var PatID = $stateParams.PatID;
console.log("the value of PatID is " + PatID);
console.log("the value of MedID is " + MedID);
console.log("Medication Edit is called ???");
return medicationResource.getMedication(
{ MedID: MedID, PatID: PatID }).$promise;
}
}
})
i transition to the create medicine (which is the child state in the code above as follows ) when clicking a button in the patient detail view
<a class="btn btn-primary"
data-ui-sref="patientEdit.Medications({MedID: 0, PatID: vm.patient.ID})"
style="width:160px">
Creat New Medicine
</a>
i have known from several posts here in stack over flow and the documentation of nested states that child state would wait till resolution of parent state , and also that i can use the resolved parent object from the parent state in the child state and controller.
what happens is that after i click the button for new medicine , and according to the network tab in the development tools both patient in parent state and medication in child state has been resolved correctly (according to the Response tab of the network of dev tools), but there is no transition
happening anywhere.
Edit
here is the Controllers for the aforementioned states that I'm using:
patientEditCtrl.JS:
(function () {
angular.module("patientManagement")
.controller("PatientEditCtrl",
[ "patient", "$state", PatientEditCtrl]);
function PatientEditCtrl(patient, $state) {
var vm = this;
// vm.title = "Editing";
vm.patient = patient;
if (patient == null)
{
vm.title = "New medication";
}
else {
console.log("here is the Parent controller of editing");
if ( (vm.patient.ID ) ) {
console.log("here is the controller of editing");
vm.title = "Edit: " + vm.patient.Name;
}
else {
console.log(vm.patient.Id);
vm.title = "New Patient";
}
}
vm.submit = function () {
vm.patient.$save();
}
vm.cancel = function () {
$state.go('patientList');
}
}
}());
medicationEditCtrl.Js
(function () {
angular.module("patientManagement")
.controller("medicationEditCtrl",
["medication", "$state", medicationEditCtrl]);
function medicationEditCtrl(medication, $state) {
var vm = this;
vm.medication = medication;
console.log("we are in medication edit CTRL");
if ((vm.medication.MedID)) {
console.log("here is the controller of editing a medication ");
vm.title = "Edit: " + vm.medication.Name;
}
else {
console.log(vm.medication.MedID);
console.log("having a patientId of " + vm.medication.PatID);
vm.title = "New Medication";
}
vm.submit = function () {
vm.medication.$save();
}
vm.cancel = function () {
$state.go('patientList');
}
}
}());
here is the service that i was using :
(function () {
"use strict";
angular.module("common.services")
.factory("patientResource", ["$resource", patientResource]);
function patientResource($resource) {
console.log("service is called");
return $resource("http://localhost:49190/api/patients/:Id");
};
}());
while i was calling it using (PatId), so the required solution was to change the service call in the resolve of the state named "patientEdit" to be as follows:
return patientResource.get({ Id: PatID }).$promise;
thanks again for your time and replies

Back Arrow and Angular Routing - Press Back Twice

Angularv1.1.5
Site: http://tilsa.azurewebsites.net
I have a very simple route setup however when the user goes from the default/home route to the detail (pregunta) route and then clicks the back button nothing happens. The 2nd/3rd time the back button is clicked the user returns (chrome) to the default/home route. I'm not sure as to how or why this is happening.
$routeProvider.
when('/', {
templateUrl: '/js/app/partial/index.html',
controller: 'IndexCtrl'
})
.when('/pregunta/:id', {
templateUrl: '/js/app/partial/detalle.html',
controller: 'PreguntaDetalleCtrl'
}).
otherwise({
redirectTo: '/'
});
Here are the two relevant controllers. I've removed some of the code that doesn't seem relevant (polling for new info/etc):
// load the index list of questions, the actual questions are loaded in parent scope
.controller('IndexCtrl', ['$scope', 'services', 'data', '$modal', 'navigation', 'timeFunctions', function ($scope, services, data, $modal, navigation, timeFunctions)
{
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/';
$scope.url = encodeURIComponent($scope.noEncodeUrl);
// controls the back arrow visibility to go back
navigation.setReturn(false);
}])
.controller('PreguntaDetalleCtrl', ['$scope', '$routeParams', 'services', 'navigation', 'graphService', 'stringFx', '$timeout', 'timeFunctions', function ($scope, $routeParams, services, navigation, graphService, stringFx, $timeout, timeFunctions) {
$scope.notas = [];
$scope.comentario = '';
navigation.setReturn(true);
$scope.loadPregunta = function (id, loadComments)
{
services.preguntas.getDetalle(id).then(function (data)
{
$scope.safeApply(function ()
{
$scope.pregunta = data;
graphService.setProp('title', $scope.pregunta.pregunta);
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/pregunta/' + id;
$scope.url = encodeURIComponent($scope.noEncodeUrl);
$scope.preguntaText = stringFx.removeAccent('¿'+$scope.pregunta.pregunta+'?');
});
if (loadComments)
{
$scope.commentTracker = {
defaults: { },
skip: 0,
take: 20
};
$scope.$on('$destroy', function (e)
{
$scope.stopPolling();
});
$scope.startPolling = function ()
{
// scrollTimeout will store the unique ID for the $setInterval instance
return $scope.scrollTimeout = timeFunctions.$setInterval(poll, 10000, $scope);
// Function called on interval with scope available
function poll($scope)
{
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
}
}
$scope.stopPolling = function ()
{
return timeFunctions.$clearInterval($scope.scrollTimeout);
}
$scope.startPolling();
$scope.cargarAnteriores = function ()
{
//$scope.commentTracker.skip++;
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas, true).then(function (data)
{
$scope.safeApply(function ()
{
$scope.notas = $scope.notas.concat(data.notas);
$scope.masNotas = $scope.notas.length > 0;
});
});
}
$scope.cargarAnteriores();
}
});
}
$scope.notaNueva = function () {
//$scope.commentario;
if ($scope.comentario.length < 3)
{
alert('Escribe algo mas, no seas tacano con tus palabras');
return;
}
$scope.processing = true;
services.preguntas.insertNota($scope.pregunta.id, $scope.comentario, $scope.notas, false).then(function (data)
{
$scope.comentario = '';
$scope.processing = false;
$scope.loadPregunta($scope.pregunta.id, false);
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
});
}
$scope.loadPregunta($routeParams.id, true)
$scope.$on('updatedpregunta', function (event, obj)
{
$scope.loadPregunta(obj, false)
});
}]);
I had this issue as well! Turned ut that artur grzesiak was right! I had a iframe on my page that had a binding for its src-attribute.
<iframe src="{{selected.url}}"></iframe>
Since the default value of $scope.selected.url was null the first thing that happened was that it was loading a url called null.
After some research I found that there was a special directive for the iframe:
<iframe ng-src="{{selected.url}}"></iframe>
This change solved my is
It seems that the Angular side of your app is fine.
99% the problem is caused by some external library. For sure there is some problem with this script kVEquaeit4R (it seens to be a facebook plugin), as it fails to load some resource (404 error): The resource you are looking for has been removed, had its name changed, or is temporarily unavailable. and as a consequence a couple of further errors are generated (look at the console). And in turn it prevents the app from calling window.location.hostname.replace what actually is present in the kVEquaeit4R script.
So my suggestion is as follow: remove this fb plugin from your site and check if the routing works properly...

using ng-include for a template that has directives that use data retrieved by XHR

I am using ng-include in order to include a persistent menu, that exists in all of the views of my SPA.
The problem is that I want to display different options and content in this menu per each user type(admin, guest, user etc.), and this requires the service function authService.loadCurrentUser to be resolved first.
For the purpose of managing this content easily and comfortably, I have created a simple directive, that takes an attribute with the required access level, and at the compile phase
of the element, if the permissions of the given user are not sufficient, removes the element and it's children.
So after failing miserably at trying to make the ng-include go through the routeProvider function, I've tried to use ng-init, but nothing seems to work, the user role remain undefined at the time that I am logging it out.
I am thinking about trying a new approach, and making the entire menu a directive that includes the template that is suitable for each user type, but first I would like to try and solve this matter.
Directive:
'use strict';
/* Directives */
angular.module('myApp.directives', []).
directive('restrict', function(authService){
return{
restrict: 'A',
prioriry: 100000,
scope: {
// : '#'
},
link: function(){
// alert('ergo sum!');
},
compile: function(element, attr, linker){
var user = authService.getUser();
if(user.role != attr.access){
console.log(attr.access);
console.log(user.role);//Always returns undefined!
element.children().remove();
element.remove();
}
}
}
});
Service:
'use strict';
/* Services */
angular.module('myApp.services', []).
factory('authService', function ($http, $q) {
var authServ = {};
var that = this;
that.currentUser = {};
authServ.authUser = function () {
return $http.head('/users/me', {
withCredentials: true
});
},
authServ.getUser = function () {
return that.currentUser;
},
authServ.setCompany = function (companyId) {
that.currentUser.company = companyId;
},
authServ.loadCurrentUser = function () {
var defer = $q.defer();
$http.get('/users/me', {
withCredentials: true
}).
success(function (data, status, headers, config) {
console.log(data);
that.currentUser.company = {};
that.currentUser.company.id = that.currentUser.company.id ? that.currentUser.company.id : data.main_company;
that.currentUser.companies = [];
for (var i in data.roles) {
that.currentUser.companies[data.roles[i]['company']] = data.roles[i]['company_name'];
if (data.roles[i]['company'] == that.currentUser.company.id){
that.currentUser.role = data.roles[i]['role_type'];
that.currentUser.company.name = data.roles[i]['company_name'];
// console.log(that.currentUser.role);
}
}
// defer.resolve(data);
defer.resolve();
}).
error(function (data, status, headers, config) {
that.currentUser.role = 'guest';
that.currentUser.company = 1;
defer.reject("reject");
});
return defer.promise;
}
return authServ;
});
Menu controller:
angular.module('myApp.controllers', []).
controller('menuCtrl', function($scope, $route, $location, authService){
//TODO: Check if this assignment should be local to each $scope func in order to be compliant with 2-way data binding
$scope.user = authService.getUser();
console.log($scope.user);
// $scope.companies = $scope.user.companies;
$scope.companyOpts = function(){
// var user = authService.getUser();
if(typeof $scope.user.company == 'undefined')
return;
var companies = [];
companies[$scope.user.company.id] = $scope.user.company.name;
for(var i in $scope.user.companies){
if(i != $scope.user.company.id){
companies[i] = $scope.user.companies[i];
}
}
// console.log(companies);
// if(nonCurrentComapnies.length > 0){
console.log(companies);
return companies;
// }
}
$scope.$watch('user.company.name', function(company){
for(var i in $scope.user.companies)
if(company == $scope.user.companies[i].id)
authService.setCompany(i);
});
$scope.$watch(function(){return authService.getUser().company; }, function(company){
//Refresh the page on company change here, first time, and each time the user changes the select
// $scope.companyOpts();
// $scope.currentComapany = company;
})
;})
Main SPA HTML page:
<div ng-init="authservice.loadCurrentUser" ng-include src="'partials/menu.html'"></div>
menu element that should be visible only to the admin:
<ul class="left" restrict access="admin">
<li>You are the admin!</li>
</ul>
Thanks in advance for any assistance!
I personally would do the "reverse" way. Which mean: I will add the menu in when the user role is "admin", or "user", etc...
This way, you can do something like this in the "restrict" directive:
...
var roleListener = $scope.$watch('user.role', function (newVal, oldVal) {
if (newVal == $scope.access) {
// add the menu items
// supposed that loadcurrentuser be called only once
// we should clear the watch
roleListener();
} else {
// personally, I would remove the item here too
// so the menu would be added or removed when user.role update
}
});
...
One more thing, for just display menu base on the user role, you can use ngSwitch, something like this:
<ul class="left" ng-switch="user.role">
<li ng-switch-when="admin">You are the admin!</li>
<li ng-switch-when="user">You are the user!</li>
<li ng-switch-default><img src="some-thing-running.gif"/>Your menu is loading, please wait...</li>
</ul>
And let the magical AngularJS binding render up the menus for you!
The call to authServ.getUser should also return a promise by calling internally
authServ.loadCurrentUser
which should be modified a bit to check if the user context exists to avoid making another API call and always returning resolve with the user context:
defer.resolve(that.currentUser);
Loading the user context should also be done early on as this enables the authorization of the app. The app.run function can be used for this purpose.
hope it helps others.

Resources