I have a framework that is supposed to be router agnostic, and while I have gotten it to work with ngRoute, I cannot for the life of me to get it to work with UI Router
The main app module Looks like this:
(function () {
"use strict";
angular.module("app", ["ptFramework", "ui.router", "ngStorage", "ui.bootstrap"]);
})();
The framework is built on three modules using custom directives the first module is the framework module
Here is the module:
(function () {
"use strict";
angular.module("ptFramework", [,"ptMenu", "ptDashboard"]);
})();
The module that I am having trouble with is the Menu module Below is the menu module:
(function () {
"use strict";
angular.module("ptMenu", ["ngAnimate"]);
})();
In index.html I have included a custom directive which looks something like this:
HTML:
<pt-menu>
<pt-menu-item label="Dasboard" icon="fa-tachometer" state="dashboard"></pt-menu-item>
<pt-menu-group label="Patients" icon="fa-users">
<pt-menu-item label="Patients" icon="fa-users" state="patient"></pt-menu-item>
<pt-menu-item label="Enter Results" icon="fa-area-chart" state="enterresults"></pt-menu-item>
<pt-menu-item label="View Results" icon="fa-area-chart" state="viewresults"></pt-menu-item>
</pt-menu-group>
<pt-menu-group label="Providers" icon="fa-user-md">
<pt-menu-item label="Providers" icon="fa-user-md" state="provider"></pt-menu-item>
<pt-menu-item label="Injury Dict." icon="fa-book" state="injurydictionary"></pt-menu-item>
<pt-menu-item label="Excercise Dict." icon="fa-book" state="excercisedictionary"></pt-menu-item>
</pt-menu-group>
<pt-menu-group label="Therapist" icon="fa-user-md">
<pt-menu-item label="Therapist" icon="fa-user-md" state="therapist"></pt-menu-item>
<pt-menu-item label="Program Entry" icon="fa-user-md" state="programentry"></pt-menu-item>
<pt-menu-item label="Results" icon="fa-area-chart" state="results"></pt-menu-item>
<pt-menu-item label="Excercises" icon="fa-bicycle" state="excercise"></pt-menu-item>
</pt-menu-group>
</pt-menu>
Here is the directive for Menu item:
(function () {
"use strict";
angular.module('ptMenu').directive('ptMenuItem', function () {
return {
require: '^ptMenu',
scope: {
label: '#',
icon: '#',
state: '#'
},
templateUrl: 'ext-modules/ptMenu/ptMenuItemTemplate.html',
link: function (scope, el, attr, ctrl) {
scope.isActive = function () {
return el === ctrl.getActiveElement();
};
scope.isVertical = function () {
return ctrl.isVertical() || el.parents('.pt-subitem-section').length > 0;
}
el.on('click', function (evt) {
evt.stopPropagation();
evt.preventDefault();
scope.$apply(function () {
ctrl.setActiveElement(el);
ctrl.setState(scope.state);
});
});
}
};
});
})();
As you can see I have state in the directive so that I can use it in my mark up. There is a el.onclick event that calls the parent controllers setState function.
That controller is here:
ptMenuController:
(function () {
"use strict";
angular.module('ptMenu').controller('ptMenuController',
['$scope', '$rootScope',
function ($scope, $rootScope) {
$scope.isVertical = true;
$scope.openMenuScope = null;
$scope.showMenu = true;
$scope.allowHorizontalToggle = true;
this.getActiveElement = function () {
return $scope.activeElement;
};
this.setActiveElement = function (el) {
$scope.activeElement = el;
};
this.isVertical = function () {
return $scope.isVertical;
}
this.setState = function (state) {
$rootScope.$broadcast('pt-menu-item-selected-event',
{ state: state });
};
This broadcasts to the next controller in the chain which is framework controller.
Framework Controller:
(function () {
"use strict";
angular.module("ptFramework").controller("ptFrameworkController",
['$scope', '$window', '$timeout', '$rootScope', '$state',
function ($scope, $window, $timeout, $rootScope, $state) {
$scope.isMenuVisible = true;
$scope.isMenuButtonVisible = true;
$scope.isMenuVertical = true;
$scope.$on('pt-menu-item-selected-event', function (evt, data) {
$scope.stateString = data.state;
$state.go(data.state);
checkWidth();
broadcastMenuState();
});
This message is getting to the front end with the correct state, but it is complaining about my controller, and when I comment out the controller I get no view
Here is the route config file
(function () {
"use strict";
angular.module('app').config([
'$stateProvider', "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state("dashboard", {
url: "/dashboard",
template: "<pta-dashboard></pta-dashboard>"
})
.state("patient", {
url: "/patient",
temlateUrl: "app/patient/patientViewTemplate.html",
controller: "patientController"
})
.state("enterresults", {
url: "/enterresults",
templateUrl: "app/patient/enterResultsTemplate.html",
controller: "patientController"
})
.state("viewresults", {
url: "/viewresults",
templateUrl: "app/patient/viewResultsTemplate.html",
controller: "patientController"
})
.state("provider", {
url: "/provider",
templateUrl: "app/provider/providerVierTemplate.html",
controller: "providerController"
})
.state("injurydictionary", {
url: "/injurydictionary",
templateUrl: "app/provider/injuryDictionaryTemplate,html",
controller: "providerController"
})
.state("excercisedictionary", {
url: "/excercisedictionary",
templateUrl: "app/provider/excerciseDictionaryTemplate.html",
controller: "providerController"
})
.state("therapist", {
url: "/therapist",
templateUrl: "app/therapist/therapistViewTemplate.html",
controller: "therapistController"
})
.state("programentry", {
url: "/programentry",
templateUrl: "app/therapist/programEntryTemplate.html",
controller: "therapistController"
})
.state("results", {
url: "/results",
templateUrl: "app/results/resultsViewTemplate.html",
controller: "resultsController"
})
.state("excercise", {
url: "/excercise",
templateUrl: "app/excercise/excerciseViewTemplate.html",
controller: "excerciseController"
})
.state("programs", {
url: "/programs",
templateUrl: "app/programs/programsViewTemplate.html",
controller: "programsController"
});
$urlRouterProvider.otherwise( "/dashboard");
}
]);
})();
I am stumped on this, as is everybody I have spoken with. I have sucessfully made this work with ngRoute. I should also be getting my directive should be showing up as well. I have my ui-sref in place in the frameworkTemplate.html
<div class="pt-title-bar">
<div class="row">
<div class="pt-logo-area col-sm-6">
<img class="pt-icon" ng-src="{{ iconFile }}" />
<div class="pt-title-area">
<p class="pt-logo-title">{{ title }}</p>
<p class="pt-logo-subtitle">{{ subtitle }}</p>
</div>
<div ng-if="isMenuButtonVisible" ng-click="menuButtonClicked()"
class="pt-collapsed-menu pull-right">
<button type="button" class="btn pt-nav-button">
<i class="fa fa-bars"></i>
</button>
</div>
</div>
<div class="pt-right-side-controls col-sm-6">
<pt-user-profile-small></pt-user-profile-small>
</div>
</div>
Any thoughts or input would be appreciated, I am getting ready to abandon the broadcast all together and go to ui-srefs in the index.html. But that feels like giving up.
Thanks,
John
This is apparently a known issue with UI-Router and custom directives
https://github.com/angular-ui/ui-router/pull/858
Related
https://plnkr.co/edit/PWuKuVw9Dn9UJy6l8hZv?p=preview
I have 3 modules, routerApp, tickers and tags. Basically trying to rebuild my app out of smaller mini-apps.
The routerApp template contains the 2 directives for the other modules.
Expected
When you login, then click on the Count button in the tickers.component, I want to send the counter var into the tags.component $scope via $state.go.
Result
Nothing happens. No $state/variable update in the tags.component
Plnkr app.js code:
// TICKERS app module
var tickers = angular.module('tickers', ['ui.router'])
tickers.config(function($stateProvider) {
const tickers = {
name: 'tickers',
url: '/tickers',
parent: 'dashboard',
templateUrl: 'tickers-list.html',
bindToController: true,
controllerAs: 'tik',
controller: function() {
}
}
$stateProvider
.state(tickers);
})
tickers.component('tickersModule', {
templateUrl: 'tickers-list.html',
controller: function($scope, $state) {
console.log('Tickers init')
$scope.counter = 0;
$scope.increase = function() {
$scope.counter++;
console.log('increase', $scope.counter)
$state.go('dashboard.tags', { counter: $scope.counter });
}
}
})
// TAGS app module
var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {
const tags = {
name: 'tags',
url: '/tags?counter',
parent: 'dashboard',
params: {
counter: 0
},
templateUrl: 'tags-list.html',
bindToController: true,
controllerAs: 'tags',
controller: function($state) {
}
}
$stateProvider
.state(tags);
})
tags.component('tagsModule', {
templateUrl: 'tags-list.html',
controller: function($scope, $state) {
// Expecting this to update:
console.log('Tags init', $state)
$scope.counter = $state.params.counter;
}
})
// MAIN ROUTERAPP module
var routerApp = angular.module('routerApp', ['ui.router', 'tickers', 'tags']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
const login = {
name: 'login',
url: '/login',
templateUrl: 'login.html',
bindToController: true,
controllerAs: 'l',
controller: function($state) {
this.login = function() {
$state.go('dashboard', {})
}
}
}
const dashboard = {
name: 'dashboard',
url: '/dashboard',
templateUrl: 'dashboard.html',
controller: function() {
}
}
$stateProvider
.state(login)
.state(dashboard);
})
dashboard.html
<div class="jumbotron text-center">
<h1>The Dashboard</h1>
</div>
<div class="row">
<tickers-module></tickers-module>
<tags-module></tags-module>
</div>
The function in the tickers component that is trying to update the $state of the tags component:
$scope.increase = function() {
$scope.counter++;
console.log('increase', $scope.counter)
$state.go('dashboard.tags', { counter: $scope.counter });
}
Also tried: $state.go('tags', { counter: $scope.counter });
Finally the tags.component.
Note that here I'm not seeing the $scope.counter update nor the controller for the component getting refreshed due to a state change.
tags.component('tagsModule', {
templateUrl: 'tags-list.html',
controller: function($scope, $state) {
console.log('Tags init', $state)
$scope.counter = $state.params.counter;
}
})
Is the way I am architecting this going to work? What am I missing?
Update: Added some $rootScope events to watch for $state changes, hope this helps:
This is after clicking the login button and going from the login state to the dashboard state, but still nothing for clicking on the Count button.
So, it looks like you're properly passing the parameters in your $state.go call.
I think the issue here is that you're not properly handling the state parameters you're passing from the tickers component to the tags component.
Try injecting $stateParams into your tags component and pull the parameters off that object, for example (in your tags controller):
$scope.counter = $stateParams.counter;
Figured it out!
https://plnkr.co/edit/CvJLXKYh8Yf5npNa2mUh?p=preview
My problem was that in the $state object tags, I was using the same template as the tags.component.
Instead I needed to change it to something like <p>{{ counter }}</p>
var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {
const tags = {
name: 'tags',
url: '/tags',
parent: 'dashboard',
params: {
counter: 0
},
template: '<p>{{ counter }}</p>',
bindToController: true,
controller: function($scope, $state) {
console.log('tags state object', $state)
$scope.counter = $state.params.counter;
}
}
$stateProvider
.state(tags);
})
tags.component('tagsModule', {
templateUrl: 'tags-module-template.html',
controller: function($scope, $state) {
}
})
Then in my tags-module.template.html I needed to add a <div ui-view></div>
<div class="jumbotron text-center">
<h2>Tags list</h2>
<div ui-view></div> // <- here and this is where the $state object updates
{{ counter }}
</div>
I have been trying to send data from one controller to another. A little background this is code being used in an ionic application if that helps any. I want the to send the data from send() function to the SubCtrl. The send function is being called in MainCtrl. I have created a service for this but the data is still not being shared. What am I missing to complete this action?
var app = angular.module('testapp', []);
app.config(function($stateProvider, $urlRouterProvider) {
"use strict";
/* Set up the states for the application's different sections. */
$stateProvider
.state('page2', {
name: 'page2',
url: '/page2',
templateUrl: 'page2.html',
controller: 'MainCtrl'
})
.state('page3', {
name: 'page3',
url: '/page3',
templateUrl: 'page3.html',
controller: 'SubCtrl'
});
$urlRouterProvider.otherwise('/page2');
});
app.factory('dataShare', function($rootScope) {
var service = {};
service.data = false;
service.sendData = function(data) {
this.data = data;
$rootScope.$broadcast('data_shared');
console.log(data);
};
service.getData = function() {
return this.data;
};
return service;
});
app.controller('MainCtrl', function($scope, $state, $http, dataShare) {
$scope.text = 'food';
$scope.send = function() {
dataShare.sendData(this.text);
};
});
app.controller('SubCtrl', function($scope, $state, dataShare) {
"use strict";
var sc = this;
$scope.text = '';
$scope.$on('data_shared', function() {
var text = dataShare.getData();
sc.text = dataShare.data;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script id="page2.html" type="text/ng-template">
<div>{text}}</div>
<input type='text' ng-model='text' />
<button class="button button-outline button-royal" ng-click="send();">add</button>
</script>
<script id="page3.html" type="text/ng-template">
<div>text: {{text}}</div>
</script>
I was able to figure this issue out after reading this page. If anyone is having a similar issue I would encourage this reading. Also the video link on this post was really helpful.
Why $location.url( "/messages" ); is not working? is there any restriction about $location inside a factory in AngularJS? any idea what is going on?
If I'm doing something wrong can you please tell my how should I redirect to another url "/messages"? I can't reload the page using so $window is not an option.
Thanks
My factory:
(function () {
'use strict';
angular
.module('app.conversation')
.factory('conversationFactory', conversation);
conversation.$inject = ['$rootScope', '$q', 'authFactory', '$location'];
function conversation($rootScope, $q, authFactory, $location) {
var service = {
changeLocation: changeLocation
};
return service;
function changeLocation() {
$location.url( "/messages" ); // recarga MIERDA!!!!!
}
}
})();
Route works fine:
(function () {
'use strict';
angular
.module('app.messages')
.config(configFunction);
configFunction.$inject = ['$routeProvider'];
function configFunction($routeProvider) {
$routeProvider
.when('/messages', {
templateUrl: 'app/messages/messages.html',
controller: 'messagesController',
controllerAs: 'meC',
resolve: {
user: function ($q, $timeout, authFactory) {
return authFactory.isLoggedInPromise();
}
}
}).when('/compose/:to?', {
templateUrl: 'app/messages/compose.html',
controller: 'composeController',
controllerAs: 'mcC',
resolve: {
user: function ($q, $timeout, authFactory) {
return authFactory.isLoggedInPromise();
}
}
});
}
})();
use $location.path( "/messages" );
I did:
$location.path( "/messages" ); // and then
$rootScope.$apply()
$location.path doesn't change in a factory with AngularJS
I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>
I am integrating some Angular and ui-router into my start-up's website and I have come across an issue with getting ui-router and ui-view to work. It lead to wonder what the / in the url property of .state points to. Does it point to where the Angular app is declared or somewhere else? Essentially I'd appreciate any feedback into how I can fix my issue. (See "careers" state in "App Module" code below to see what I am referring to)
As you can see here, it's not currently working.
App Module
var careersApp = angular.module("ultradiaCareers", ["ui.router", "services"]);
careersApp.config(function($stateProvider, $locationProvider){
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
$stateProvider
.state('careers', {
url: "/",
templateUrl: "openings.html",
controller: "CareersController"
})
.state('positions', {
url: "/position/:id",
templateUrl: "position.html",
controller: "PositionsController"
});
});
Controllers
angular.module("ultradiaCareers")
.controller('CareersController', ["$scope", "GetOpenings", function ($scope, GetOpenings) {
"use strict";
GetOpenings.openings.then(function (positions) {
$scope.openings = positions.data;
});
}])
.controller('PositionsController', ["$scope", "$http", "$stateParams", "GetOpenings", function ($scope, $http, $stateParams, GetOpenings) {
"use strict";
GetOpenings.openings.then(function (positions) {
$scope.openings = positions.data;
$scope.positon = positions.data[GetOpenings.getPositionById($scope.openings, $stateParams.id)];
});
}]);
Index HTML
<html>
<head></head>
<body ng-app="ultradiaCareers">
<div ui-view></div>
</body>
</html>
Service for JSON
var careersServices = angular.module("services", [])
.factory("GetOpenings", ["$http", function ($http) {
"use strict";
var GetOpenings = this, i;
GetOpenings.openings = $http.get("/careers/positions.json");
return {
openings: GetOpenings.openings,
getPositionById: function (data, value) {
for (i = 0; i < data.length; i++) {
if (data[i].pid == value) {
return i;
}
}
}
};
}]);