stateChangeStart event in Angular doesn't fire when refreshing page - angularjs

I have a custom directive:
export class XHideDirective {
static $inject = ["$rootScope"];
static $rootScope: any;
public static build($rootScope) {
var directive: ng.IDirective = {
link: (scope, element, attributes: any) => {
var itemToHide = attributes["xHide"];
$rootScope.$on("$stateChangeStart",
(event, toState) => {
if (toState.data && toState.data.hasOwnProperty(itemToHide)) {
element.hide();
} else {
element.show();
}
});
}
};
return directive;
}
}
And what that does, is when a state has it, it'll hide all elements on the page with that directive set to that value.
.state("deposit.x.successful", {
url: "/successful/:transactionId",
controller: "DepositResultController",
templateUrl: "deposit/templates/x/successful.html",
data: { hideDepositMenu: null }
})
.state("deposit.x.pending", {
url: "/pending",
templateUrl: "deposit/templates/x/pending.html",
data: { hideDepositMenu: null }
})
.state("deposit.x.rejected", {
url: "/rejected",
templateUrl: "deposit/templates/x/rejected.html",
data: { hideDepositMenu: null }
This all works very well except in the case when I don't transition to that page naturally but I get forwarded there (either a server redirect) or if I refresh the page with Ctrl+F5. Then the "stateChangeStart" event doesn't get hit.
The directive is registered like this:
module Utils {
angular.module("Utils", [])
.directive("xHide", ["$rootScope", (r) => { return XHideDirective.build(r); }]);
}
How do I get the state in those cases?
I found this very similar issue with no solution
$stateChangeStart don't work when user refresh browser

Did you try to put this listener in a .run section ?
$rootScope.$on("$stateChangeStart",
(event, toState) => {
if (toState.data && toState.data.hasOwnProperty(itemToHide)) {
element.hide();
} else {
element.show();
}
});

I think I solved it by doing this:
export class XHideDirective {
static $inject = ["$rootScope", "$timeout"];
static $rootScope: any;
public static build($rootScope, $timeout) {
var directive: ng.IDirective = {
controller: DepositController,
link: (scope, element, attributes: any) => {
var itemToHide = attributes["xHide"];
$timeout(() => {
if (scope.hideMenu && scope.hideMenu.hasOwnProperty(itemToHide)) {
element.hide();
} else {
element.show();
}
}, 0);
$rootScope.$on("$stateChangeStart",
(event, toState) => {
if (toState.data && toState.data.hasOwnProperty(itemToHide)) {
element.hide();
} else {
element.show();
}
});
}
};
return directive;
}
}
and then inside the controller:
module Deposit {
export class DepositController extends Utils.BaseController {
constructor(public $state) {
this.$scope.hideMenu = this.$state.$current.data;
}
}
}
No idea if it's the optimal solution but it seems to work well so far.
Hope it helps someone.

Related

How can I inject with resolve async data to a ui bootstrap modal in angular?

How can I inject with resolve async data to a ui bootstrap modal in angular?
The following code is the controller that opens the modal.
import insertOpportunityModal from './insertOpportunityModal.html';
export default class OpportunitiesCtrl {
constructor($uibModal, OpportunitiesService, EngagementsService, PAGE_SIZE) {
this.$uibModal = $uibModal;
this.OpportunitiesService = OpportunitiesService;
this.EngagementsService = EngagementsService;
}
openInsertModal() {
var modalInstance = this.$uibModal.open({
animation: false,
size: 'md', //['lg', 'md', 'sm']
template: insertOpportunityModal,
controller: 'InsertOpportunityModalCtrl',
controllerAs: 'vm',
resolve: {
opportunityTypes: function (EngagementsService) {
return EngagementsService.all();
}
}
});
modalInstance.rendered.then(() => {
});
modalInstance.result.then(() => {
console.log('modal closed');
}, (error) => {
console.log(error);
});
}
}
OpportunitiesCtrl.$inject = ['$uibModal', 'OpportunitiesService', 'EngagementsService', 'PAGE_SIZE'];
opportunityTypes is undefined in the modal controller.
It works with sync data but not with promises.
How can fix?
export default class InsertOpportunityModalCtrl {
constructor($uibModalInstance, OpportunitiesService, opportunityTypes) {
this.$uibModalInstance = $uibModalInstance;
this.OpportunitiesService = OpportunitiesService;
this.opportunityTypes = opportunityTypes;
console.log('opportunityTypes', opportunityTypes);
}
save() {
}
cancel() {
this.$uibModalInstance.dismiss('cancel');
}
}
InsertOpportunityModalCtrl.$inject = ['$uibModalInstance', 'OpportunitiesService'];
You have to inject opportunityTypes in InsertOpportunityModalCtrl too.
InsertOpportunityModalCtrl.$inject =
['$uibModalInstance', 'OpportunitiesService', 'opportunityTypes'];

Cancel function that has $timeout running

I have the following code. It checks a factory that polls a server every few minutes. It works fine, but I want to pause the "fetchDataContinously" method when a modal is opened, and resume it when the modal is closed. In short, I need to find a way to toggle the "fetchDataContinously" on the opening and closing of modals. Please advise, or let me know if my question is not clear.
angular.module('NavBar', []).controller('NavBarCtrl', function NavBarCtrl($scope,$modal,$timeout,MyPollingService) {
var ctrl = this;
fetchDataContinously ();
function fetchDataContinously () {
MyPollingService.poll().then(function(data) {
if(data.response[0].messages[0].important_popup){
ctrl.showImportantNotice(data.response[0].messages[0]);
return;
}
$timeout(fetchDataContinously, 3000);
});
}
ctrl.showNotices = function () {
var noticesModalOptions = {
templateUrl: "notices.html",
controller: "NoticesCtrl",
controllerAs: "ctrl",
show: true,
scope: $scope,
resolve: {
notices: function(NoticesFactory) {
return NoticesFactory.getMessages();
}
}
}
var myNoticesModal = $modal(noticesModalOptions);
myNoticesModal.$promise.then(myNoticesModal.show);
}
ctrl.showImportantNotice = function (importantNotice) {
ctrl.importantNotice = importantNotice;
var importantNoticeModalOptions = {
templateUrl: "/importantNotice.html",
controller: "ImportantNoticeCtrl",
controllerAs: "ctrl",
show: true,
scope: $scope,
onHide: function() {
console.log("Close !");
fetchDataContinously();
},
onShow: function() {
console.log("Open !");
}
}
var myImportantNoticeModal = $modal(importantNoticeModalOptions);
myImportantNoticeModal.$promise.then(myImportantNoticeModal.show);
}
})
Wrap your $timeout in function and return it's promise
var timer;
function startPolling(){
return $timeout(pollData, 3000);
}
// start it up
timer = startPolling();
To cancel:
$timeout.cancel(timer);
Then to start again :
timer = startPolling();

Can there be an AngularJS directive to manage an array of conditions?

I'm new to AngularJS and my project at the moment has a menu that only needs to be displayed sometimes.
I therefore have:
<div class="iframe-hide"
ng-show="$state.includes('deposit.card.start')||
$state.includes('deposit.card.3ds')||
$state.includes('deposit.card.waiting')||
$state.includes('deposit.bank')||
$state.includes('deposit.x')||
$state.is('deposit.x.start')||
$state.is('deposit.y.start')||
$state.is('deposit.y.frame')">
As you can imagine, as the project grows this becomes unmanageable, so I want to look into tidying it up and creating perhaps a custom directive that will handle these conditions better.
I've been thinking of adding a custom data parameter like this:
.state("deposit.card.waiting", {
url: "/waiting",
templateUrl: "app/deposit/templates/card/waiting.html",
data: { includeMenu: true }
})
The Html instead would be:
<div class="iframe-hide" show-if-true="includeMenu">
And then a directive that will check whether includeMenu is true. I wrote it here:
export class showIfTrueDirective {
static $inject = ["$", "$rootScope"];
static $rootScope: any;
public static build($, $rootScope) {
var directive: ng.IDirective = {
link: (scope, element, attributes: any) => {
var itemToShow = attributes["showIfTrue"];
// this correctly prints "includeMenu"
// grab the data from current state?. If includeMenu == true then show element, otherwise hide element
}
};
return directive;
}
}
if I hook that up:
.directive("showIfTrue", ["$", "$rootScope", (r, s) => { return ShowIfTrueDirective.build(r,s); }])
If I manage to grab the scope data then this might work but this is my first week using Anglular and not entirely sure what I'm doing. Is there a better solution for this scenario?
I managed to solve it:
export class NgHideDirective {
static $inject = ["$rootScope"];
static $rootScope: any;
public static build($rootScope) {
var directive: ng.IDirective = {
link: (scope, element, attributes: any) => {
var itemToHide = attributes["ngHide"];
$rootScope.$on('$stateChangeStart',
(event, toState) => {
if (toState.data.hasOwnProperty(itemToHide)) {
element.hide();
} else {
element.show();
}
});
}
};
return directive;
}
}
So if we now do this on an element:
<div class="iframe" ng-hide="hideMenu">
And this on the state:
.state("deposit.x.rejected", {
url: "/rejected",
templateUrl: "app/deposit/templates/x/rejected.html",
data: { hideDepositMenu: null }
Then the div will be hidden.
However this doesn't work when page is refreshed for some reason.

AngularJS Typescript directive to class

I am using AngularJS and had been so for quite a while recently i started to mess around with Typescript i was able to convert most of my Angular code to Typescript and found great benefit especially when it get to services, but i can not convert the following directive to typescript class anyone has an idea how to do that, it works perfectly fine as AngularCode
angular.module('myValidation', [])
.directive('myValidation', function() {
return {
restrict: 'A',
require: "ngModel",
link: function(scope, elm, attrs, ctrl) {
switch (attrs.myValidation) {
case 'email':
var regex = /^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/;
break;
}
ctrl.$parsers.unshift(function(viewValue) {
if (regex.test(viewValue)) {
ctrl.$setValidity('myValidation', true);
}
else {
ctrl.$setValidity('myValidation', false);
}
return viewValue;
});
ctrl.$formatters.unshift(function(viewValue) {
if (regex.test(viewValue)) {
ctrl.$setValidity('myValidation', true);
}
else {
ctrl.$setValidity('myValidation', false);
}
return viewValue;
});
}
};
});
https://github.com/aleksey-pastuhov/AngularJS-Typed-Core
#directive(app)
export default class CopyText implements IDirective {
angularModule = app;
restrict = "E";
scope: any = {
model: "="
};
templateUrl = "Templates/Directives/copyText.html";
link: (scope: any, elm: any) => void = (scope, elm: any) => {
var input: JQuery;
scope.select = ($event) => {
input = $($event.target).next().first();
};
scope.copy = () => {
input.on("focus", function() {
this.setSelectionRange(0, this.value.length);
});
input.focus();
};
};
}
directive angularjs required a function. If you want to use directive with typescript class, you need to implement a function Factory for return instance of your class. Look this exemple:
Module Directive{
class myDirectiveCtrl {
private name: string;
private items: Array<string>;
private $q: ng.IQService;
static $inject = ['$scope','$window','$q'];
constructor($scope: ImyDirectiveScope, $window) {
this.name = $scope.name;
this.items = ['salut', 'hello', 'hi', 'good morning'];
}
clickMe = ():void => {
console.log('dssff');
this.items.push('yo');
}
}
export interface ImyDirectiveScope {
name: string
}
export class myDirective implements ng.IDirective {
restrict = 'E';
template = '<div><h1>{{vm.name}}</h1><div ng-repeat="i track by $index in vm.items">{{i}}</div><hr/><button type="button" ng-click="vm.clickMe()">Click Me</button></div>';
replace = true;
scope = {
name: '#'
};
controller = myDirectiveCtrl;
controllerAs = 'vm';
link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller: myDirectiveCtrl) => {
};
constructor() { };
static factory(): ng.IDirectiveFactory {
var directive = () => new myDirective();
return directive;
}
}
app.directive('myDirective',Directive.myDirective.factory());
}

how can I pass a service to a controller dynamically by some conditions

I am using the ui-router module and have defined these states:
.state('projects.create', {
url: '/create',
views: {
'outer#': {
templateUrl: 'views/projects.create.html',
resolve: {
schoolyear: function(schoolyearService) {
return schoolyearService.createSchoolyear();
}
},
controller: 'ProjectWizardController'
}
}
})
.state('projects.edit', {
url: '/edit',
views: {
'outer#': {
templateUrl: 'views/projects.edit.html',
resolve: {
schoolyear: function(schoolyearService) {
return schoolyearService.editSchoolyear();
}
},
controller: 'ProjectWizardController'
}
}
})
As each of both ui-router states know which states they are they also know what dependencies should be passed to the ProjectWizardController.
When the projects.create state is activated I want to pass the CreateWizardDataService to the ProjectWizardController.
When the projects.edit state is activated I want to pass the EditWizardDataService to the ProjectWizardController.
HOW can I manually inject the service dependency into the ProjectsWizardController?
'use strict';
angular.module('schoolyearProjectModule').controller('ProjectWizardController',
function ($scope, wizardDataService, $state, schoolyear) {
// wizardDataService => could be the CreateWizardDataService or EditWizardDataService
// The wizardDataService is the individual service for an AddService or EditService
// service contain the 3 same main properties: schoolyearData, schoolclasscodesData, timetableData
wizardDataService.schoolyearData = schoolyear.schoolyearData;
wizardDataService.schoolyearData = schoolyear.schoolclassCodesData;
wizardDataService.schoolyearData = schoolyear.timetableData;
// The if and else if should be injected into this Controller becaue the outside ui router states know their state edit/create
if ($state.current.name === 'projects.create') {
$scope.steps = [wizardDataService.schoolyearData, wizardDataService.schoolclassCodesData, wizardDataService.timetableData];
}
else if ($state.current.name === 'projects.edit') {
$scope.steps = [wizardDataService.schoolyearData, wizardDataService.schoolclassCodesData, wizardDataService.timetableData];
}
$scope.steps = [wizardDataService.schoolyearData, wizardDataService.schoolclassCodesData, wizardDataService.timetableData];
$scope.activeStep = $scope.steps[0];
$scope.step = 0;
var stepsLength = $scope.steps.length;
$scope.isLastStep = function () {
return $scope.step === (stepsLength - 1);
};
$scope.isFirstStep = function () {
return $scope.step === 0;
};
$scope.getCurrentStep = function () {
return $scope.activeStep.name;
};
$scope.getNextLabel = function () {
return ($scope.isLastStep()) ? 'Submit' : 'Next';
};
$scope.previous = function () {
if ($scope.step > 0) {
$scope.step--;
$scope.activeStep = $scope.steps[$scope.step];
}
};
$scope.next = function () {
if ($scope.isLastStep() && $scope.activeStep.isValid()) {
$state.go('^');
}
else if ($scope.activeStep.isValid()) {
$scope.step += 1;
$scope.activeStep = $scope.steps[$scope.step];
}
}
});
You have two ways to do this:
Option 1 - Use resolve with a string as the value. As per the documentation:
The resolve property is a map object. The map object contains
key/value pairs of:
key – {string}: a name of a dependency to be injected into the
controller.
factory - {string|function}: If string, then it is an
alias for a service. Otherwise if function, then it is injected and
the return value is treated as the dependency. If the result is a
promise, it is resolved before the controller is instantiated and its
value is injected into the controller.
Option 1 example:
.state('projects.create', {
url: '/create',
views: {
'outer#': {
templateUrl: 'views/projects.create.html',
resolve: {
schoolyear: function(schoolyearService) {
return schoolyearService.createSchoolyear();
},
wizardDataService: 'CreateWizardDataService'
},
controller: 'ProjectWizardController'
}
}
})
.state('projects.edit', {
url: '/edit',
views: {
'outer#': {
templateUrl: 'views/projects.edit.html',
resolve: {
schoolyear: function(schoolyearService) {
return schoolyearService.editSchoolyear();
},
wizardDataService: 'EditWizardDataService'
},
controller: 'ProjectWizardController'
}
}
})
Option 2 - Use $injector.get('CreateWizardDataService') or $injector.get('EditWizardDataService') directly in your controller depending on which state you are in.

Resources