Go to another page after popup, ionic - javascript

I want to give two option to user by the popup and after selection I go for the same page, but if some diferences.
mapsWazePopup = function () {
var importPopup = $ionicPopup.show({
title: "Selecione uma das opcoes:",
scope: $scope,
cssClass: "popup-import",
buttons: [
{
text: '<b>Maps</b>',
type: 'button button-positive',
onTap: function () {
$location.path("/Home");
}
},
{
text: '<b>Waze</b>',
type: 'button button-positive',
onTap: function () {
$location.path("/Home");
}
},
]
});
}
and the routes.
angular.module("routes", [])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('index', {
url: '/',
templateUrl: 'index.html',
controller: 'ionicApp'
})
.state('mapa', {
url: '/mapa',
templateUrl: 'templates/mapa.html',
controller: 'mapaCtrl'
});
});
But nothing happen :( I created a .js file just for the routes. I want just to go for this page.

To navigate in angular you have to use $state service instead of $location:
$state.go('home');
//or
$state.go('mapa');
The parameter is the state's name instead of the url.

The onTap function returns a promise, so you will want to do something like this instead where you return the path of the button pressed:
angular.module('mySuperApp', ['ionic'])
.controller('PopupCtrl', function ($scope, $ionicPopup, $state) {
$scope.mapsWazePopup = function () {
var importPopup = $ionicPopup.show({
title: "Selecione uma das opcoes:",
scope: $scope,
cssClass: "popup-import",
buttons: [
{
text: '<b>Maps</b>',
type: 'button button-positive',
onTap: function (e) {
//To do nothing on button press -> e.preventDefault();
return ('mapa');
}
},
{
text: '<b>Waze</b>',
type: 'button button-positive',
onTap: function (e) {
//To do nothing on button press -> e.preventDefault();
return ('Home');
}
},
]
});
importPopup.then(function (res) {
//res is your response from the promise,
//which will be the path depending on which button was pressed
console.log('Path: ', res);
$state.go(res)
});
};
});
However, in your routes file you have no path to /Home.
Use $state not location, more info on it here. You can read more on ionicPopup here, and a codepen of ionics nightly with this type of functionality for you to play around with.

Related

AngularJS - How to render template AND data from a promise

Alright, I'm pretty new to Angular, spent the last two days trying to find a good way to do this and failed. I'm not sure if the title is good either.
I'm trying to make a simple page with a menu. The person would click a menu link and then the view that's below the menu should render accordingly (without refreshing the whole page of course). Sounds easy.
But the thing is, there's going to be a menu for admins and a menu for regular users, so the menu is loaded through ajax. This is an example of what I get:
[
{
name: "Manage games",
templateUrl: "/view/mygames.html",
url: "/games",
},
{
name: "Weekly Reports",
templateUrl: "/view/myreports.html",
url: "/reports"
},
{
name: "Manage Users",
templateUrl: "/view/users.html",
url: "/users",
adminRequired: true
}
];
After the menu is loaded, when a menu element is clicked, I want to get data making an ajax call to the url property, and the template to fetch this data to will also be an ajax call to the templateUrl property.
So, how is that achievable? Basically what I want is to have a directive/component/whatever, that by default will be empty, not displayed or rendered. But when I click on a element from the menu, I'm going to $broadcast an event with the dataUrl/templateUrl to the directive/component/whatever, and it will make two ajax calls, one two get the data, and another to get the template, after both get done, it will render and appear on the page.
Any way to this, or suggestion to do something similar to this would be greatly appreciated
By the way, I'm using Angular 1.5.7
You should use routing (in my example it's the ui-router) for achieving this.
The ui-router has a resolve property that lets you resolve controller dependencies and lets you then inject them into your controller for using them.
Here's a full example that I've made (sorry for the poor ui):
HTML:
<div ng-app="app">
<div ng-controller="homeCtrl as vm">
<menu items="vm.items"></menu>
</div>
<div>
<ui-view></ui-view>
</div>
</div>
JS:
var app = angular.module('app', ['ui.router']);
app.controller('gamesCtrl', function(data) {
this.title = data.title;
}).
controller('reportsCtrl', function(data) {
this.title = data.title;
}).
controller('usersCtrl', function(data, adminData) {
this.title = data.title;
this.removedUsers = adminData.removedUsers;
}).
controller('homeCtrl', function() {
this.items = [{
name: 'Manage games',
state: 'games'
}, {
name: 'Weekly Reports',
state: 'reports'
}, {
name: 'Manage Users',
state: 'users',
adminRequired: true
}];
});
app.component('menu', {
bindings: {
items: "="
},
template: '<div ng-repeat="item in $ctrl.items"><span ng-click="$ctrl.goToState(item)">{{item.name}}</span></div>',
controller: function($state) {
this.goToState = function(item) {
console.log('redirecting to state:' + item.state);
$state.go(item.state);
}
}
});
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider.
state('games', {
url: '/games',
template: '<div><h1>{{vm.title}}</h1></div>', // use templateUrl..
resolve: {
data: function($q) {
return $q.when({
title: 'games'
})
}
}, // return injectables who return promises and inject them into your ctrl
controller: 'gamesCtrl as vm'
}).
state('reports', {
url: '/reports',
template: '<div><h1>{{vm.title}}</h1></div>', // use templateUrl..
resolve: {
data: function($q) {
return $q.when({
title: 'reports'
})
}
}, // return injectables who return promises and inject them into your ctrl
controller: 'reportsCtrl as vm'
}).
state('users', {
url: '/users',
template: '<div><h1>{{vm.title}}</h1><div>Removed Users:</div><div ng-repeat="user in vm.removedUsers">{{user}}</div></div>', // use templateUrl..
// return injectables who return promises and inject them into your ctrl
resolve: {
data: function($q) {
return $q.when({
title: 'users'
})
},
adminData: function($q) {
return $q.when({
removedUsers: ['user1', 'user2', 'user3']
})
}
},
controller: 'usersCtrl as vm'
}).
state('default', {
url: '/default',
template: '<h1>This is the default state</h1>'
});
$urlRouterProvider.otherwise('/default');
});
JSFIDDLE.
Remarks:
You should use templateUrl instead of template when configurating the states.
I've used $q.when to demonstrate a return of a promise. You will probally use $http.get\post instead.

$scope passing has a Delay

I'm building an Angular pop-up system for multiple purposes. The way it works is that I have a directive called bitPopup which three variables get passed on to (type, action and data) as shown below:
index.html
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
popup.js
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
[***]
}
}
The popup controller then loads a different directive based on the type:
popup.html (The HTML template shown above)
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
false_positives.js (Containing the bitFalsePositivePopup directive)
[...]
scope: {
type: '=',
action: '=',
data: '='
}
[...]
And then the html template for the bitFalsePositivePopup directive displays some properties from data.
Now the way I'm triggering a pop-up works like this:
From a template inside a directive containing the bitPopup directive i'll change $scope.popup's type, action and data.
I'll do $scope.$broadcast('showPopup');
The bitPopup directive will react because of $scope.$on('showPopup', [...]}); and makes the pop-up visible.
Now this really weird thing occurs where it works on the first try (the pop-up opens with the correct data information), but after the first try it will display the data from the previous try.
Now what's even weirder is that I tried logging the information on the first try and what I found out is that:
$scope.popup at index.html just before calling $scope.$broadcast('showPopup'); displays the right information.
$scope.data at the bitPopup directive displays null
$scope.data at the bitFalsePositivePopup directive displays the right information.
On the second try:
$scope.popup at index.html is correct again.
$scope.data at the bitPopup directive displays the information from the previous attempt
The same holds for the bitFalsePositivePopup directive.
Another weird thing is that when I use $scope.$apply() it does work correctly, only it displays the $apply already in progress error. I know I shouldn't use $scope.$apply() in this case, because it's all Angular events. But how is it possible that the passed scope is always a step behind?
Am I doing something wrong to begin with?
EDIT:
Because of amahfouz's answer I decided to post some more code for clarification. I left out some unimportant details for more clear reading.
index.html
<div class="falsePositives" ng-controller="falsePositives">
<i class="fa fa-minus color-red" ng-click="triggerPopup('falsePositive', 'delete', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<i class="fa fa-pencil" ng-click="triggerPopup('falsePositive', 'edit', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
</div>
index.js
var app = require('ui/modules').get('apps/falsePositives');
app.controller('falsePositives', function ($scope, $http, keyTools, bitbrainTools, stringTools) {
function init() {
$scope.getDetection = getDetection;
$scope.popup = {
type: null,
action: null,
data: null
};
}
function getDetection(hash, source) {
return {
'ids': 'BitSensor/HTTP/CSRF',
'name': 'CSRF Detection',
'description': 'Cross domain POST, usually CSRF attack',
'type': [
'csrf'
],
'severity': 1,
'certainty': 1,
'successful': false,
'input': ['s'],
'errors': []
};
}
$scope.triggerPopup = function (type, action, data) {
$scope.popup = {
type: angular.copy(type),
action: angular.copy(action),
data: angular.copy(data)
};
test();
$scope.$broadcast('showPopup');
};
function test() {
console.log('$scope.popup: ', $scope.popup);
}
}
popup.html
<div class="pop-up-back" ng-click="hidePopup()" ng-class="{visible: visible}"></div>
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
popup.js
var app = require('ui/modules').get('apps/bitsensor/popup');
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope) {
$scope.visible = false;
$scope.$on('showPopup', function () {
console.log('$scope.data: ', $scope.data);
$scope.visible = true;
});
$scope.$on('hidePopup', function () {
hidePopup();
});
function hidePopup() {
$scope.visible = false;
}
$scope.hidePopup = hidePopup;
}
};
});
false_positives.js
var app = require('ui/modules').get('apps/bitsensor/falsePositives');
app.directive('bitFalsePositivePopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope, objectTools, bitbrainTools, keyTools) {
function init() {
console.log('$scope.data # fp: ', $scope.data);
}
function hidePopup() {
$scope.data = null;
$scope.$emit('hidePopup');
}
$scope.$on('showPopup', function () {
init();
});
init();
$scope.hidePopup = hidePopup;
}
}
}
Without the rest of the code I can only guess: You either need to use a promise when displaying the popup or use the $apply service to make the change to the popup visibility.
surround your $broadcast event in $timeout like follow:
$timeout(function() {
$broadcast('eventName');
});
It will wait for $scope update and then will trigger the event.

AngularJS ui router $stateChangeStart with promise inifinite loop

I'm trying to build some sort of authentication in my angular app and would like to redirect to a external URL when a user is not logged in (based on a $http.get).
Somehow I end up in an infinite loop when the event.preventDefault() is the first line in the $stateChangeStart.
I've seen multiple issues with answers on stackoverflow, saying like "place the event.preventDefault() just before the state.go in the else". But then the controllers are fired and the page is already shown before the promise is returned.
Even when I put the event.preventDefault() in the else, something odd happens:
Going to the root URL, it automatically adds the /#/ after the URL and $stateChangeStart is fired multiple times.
app.js run part:
.run(['$rootScope', '$window', '$state', 'authentication', function ($rootScope, $window, $state, authentication) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault();
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
return;
} else {
$state.go(toState, toParams);
}
});
});
}]);
authentication.factory.js identity() function:
function getIdentity() {
if (_identity) {
_authenticated = true;
deferred.resolve(_identity);
return deferred.promise;
}
return $http.get('URL')
.then(function (identity) {
_authenticated = true;
_identity = identity;
return _identity;
}, function () {
_authenticated = false;
});
}
EDIT: Added the states:
$stateProvider
.state('site', {
url: '',
abstract: true,
views: {
'feeds': {
templateUrl: 'partials/feeds.html',
controller: 'userFeedsController as userFeedsCtrl'
}
},
resolve: ['$window', 'authentication', function ($window, authentication) {
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
})
}]
})
.state('site.start', {
url: '/',
views: {
'container#': {
templateUrl: 'partials/start.html'
}
}
})
.state('site.itemList', {
url: '/feed/{feedId}',
views: {
'container#': {
templateUrl: 'partials/item-list.html',
controller: 'itemListController as itemListCtrl'
}
}
})
.state('site.itemDetails', {
url: '/items/{itemId}',
views: {
'container#': {
templateUrl: 'partials/item-details.html',
controller: 'itemsController as itemsCtrl'
}
}
})
}])
If you need more info, or more pieces of code from the app.js let me know !
$stateChangeStart will not wait for your promise to be resolved before exiting. The only way to make the state wait for a promise is to use resolve within the state's options.
.config(function($stateProvider) {
$stateProvider.state('home', {
url: '/',
resolve: {
auth: function($window, authentication) {
return authentication.identity().then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
});
}
}
});
});
By returning a promise from the function, ui-router won't initialize the state until that promise is resolved.
If you have other or children states that need to wait for this, you'll need to inject auth in.
From the wiki:
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.

How to use ncy-breadcrumb in child state of abstract parent

I am new to AngularJS and I used ncy-breadcrumb for my AngularJS project. There is an abstract true parent state and two child states of it. I used these child states for tabs. But I couldn't find a way to show these states in the breadcrumb dynamically. The only thing I can do is hardcode one child state name as parent in other state. But I need a solution to display these child states in collectionsWorkPage state dynamically.
.state('collectionsLibrary', {
url: '/headquarters/collections-library/',
templateUrl: 'app/views/collectionsLibrary/base.html',
controller: 'CollectionsLibraryBaseController',
ncyBreadcrumb: {
label: 'Collections Library',
parent: 'headquarters'
},
abstract: true,
resolve: {
controller: function ($q) {
var deferred = $q.defer();
require(['controllers/collectionsLibrary/CollectionsLibraryBaseController'], function () {
deferred.resolve();
});
return deferred.promise;
}
}
})
.state('collectionsLibrary.available', {
url: 'available/',
templateUrl: 'app/views/collectionsLibrary/available.html',
controller: 'CollectionsLibraryAvailableController',
ncyBreadcrumb: {
label: 'Collections Library-Available',
parent: 'headquarters'
},
resolve: {
controller: function ($q) {
var deferred = $q.defer();
require(['controllers/collectionsLibrary/CollectionsLibraryAvailableController'], function () {
deferred.resolve();
});
return deferred.promise;
}
}
})
.state('collectionsLibrary.my', {
url: 'my/',
templateUrl: 'app/views/collectionsLibrary/my.html',
controller: 'CollectionsLibraryMyController',
ncyBreadcrumb: {
label: 'Collections Library-My',
parent: 'headquarters'
},
resolve: {
controller: function ($q) {
var deferred = $q.defer();
require(['controllers/collectionsLibrary/CollectionsLibraryMyController'], function () {
deferred.resolve();
});
return deferred.promise;
}
}
})
.state('collectionsWorkPage', {
url: '/headquarters/collections-library/:id/edit/',
templateUrl: 'app/views/collectionsWorkPage.html',
controller: 'CollectionsWorkPageController',
ncyBreadcrumb: {
label: 'Edit Collection',
parent: 'collectionsLibrary.available'
},
params: {
data: {}
},
resolve: {
controller: function ($q, $stateParams) {
var deferred = $q.defer($stateParams);
require(['controllers/CollectionsWorkPageController'], function () {
deferred.resolve();
});
return deferred.promise;
}
}
})
The parent property can be either a string or a function returning the parent name. The function provides the scope of the current state controller (the same used for labels interpolation).
So you can do something like this:
ncyBreadcrumb: {
label: 'Edit Collection',
parent: function($scope) {
if($scope.tab === 'MY') // Constant defined in CollectionsLibraryMyController
return 'collectionsLibrary.my';
else if($scope.tab === 'AVAILABLE') // Constant defined in CollectionsLibraryAvailableController
return 'collectionsLibrary.available';
}
}
See API reference for more details
app.config(['$breadcrumbProvider', configNcyBreadcrumb])
function configNcyBreadcrumb($breadcrumbProvider) {
$breadcrumbProvider.setOptions({
includeAbstract : true
});
}

Ionic , separated controllers, not working

I created ionic app using yeoman generator. I started app using grunt serve and added one new controller named settings.
Index.html:
<script src="scripts/controllers/settings.js"></script>
Settings js:
'use strict';
/**
* #ngdoc function
* #name musicPadApp.controller:SettingsCtrl
* #description
* # SettingsCtrl
* Controller of the musicPadApp
*/
angular.module('musicPadApp')
.controller('SettingsCtrl', function ($scope) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
});
app.js:
.state('app.settings', {
url: '/settings',
views: {
'menuContent' :{
templateUrl: 'templates/settings.html',
controller: 'SettingsCtrl'
}
}
})
But on the settings page i always get following error:
Error: [ng:areq] Argument 'SettingsCtrl' is not a function, got undefined
What i'm doing wrong and how to solve it?
Default file for all controllers is following:
'use strict';
angular.module('MusicPad.controllers', [])
.controller('AppCtrl', function($scope, $ionicModal, $timeout) {
// Form data for the login modal
$scope.loginData = {};
// Create the login modal that we will use later
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: $scope
}).then(function(modal) {
$scope.modal = modal;
});
// Triggered in the login modal to close it
$scope.closeLogin = function() {
$scope.modal.hide();
},
// Open the login modal
$scope.login = function() {
$scope.modal.show();
};
// Perform the login action when the user submits the login form
$scope.doLogin = function() {
console.log('Doing login', $scope.loginData);
// Simulate a login delay. Remove this and replace with your login
// code if using a login system
$timeout(function() {
$scope.closeLogin();
}, 1000);
}
})
.controller('PlaylistsCtrl', function($scope) {
$scope.playlists = [
{ title: 'Reggae', id: 1 },
{ title: 'Chill', id: 2 },
{ title: 'Dubstep', id: 3 },
{ title: 'Indie', id: 4 },
{ title: 'Rap', id: 5 },
{ title: 'Cowbell', id: 6 }
];
})
.controller('PlaylistCtrl', function($scope, $stateParams) {
});
Thanks for any help.
I solved it by this way:
Each of your controllers can be in its own file and you declare it like this.
angular.module('ionicApp').controller('MainCtrl', function ($scope) { ... });
Link:
http://forum.ionicframework.com/t/separating-out-the-controllers-into-different-js-files/2554

Categories

Resources