I just took an app I'm working on and converted it to a Plunk but Angular and/or ui-router is not populating the two views I have in index.html. On my local box the app loads fine but there I have the app modularize. So when I converted it to a Plunk I had to rewire the files together since I can't make modules in Plunker AFAIK. Also, when I load the Plunk in a separate window and open Dev Tools I get no errors so I'm at a loss right now.
Here is link to the Plunk code I made:
http://plnkr.co/edit/2f1RITT6ysZhB5i0UcUw?p=preview
And here is the link to the embedded view (more convenient if you want to use Dev Tools):
http://embed.plnkr.co/2f1RITT6ysZhB5i0UcUw/preview/posts
I should mention that the route has to end in /posts since that it the url of the state named posts. I have no state defined for the root / url. Also the following url failed:
http://embed.plnkr.co/2f1RITT6ysZhB5i0UcUw/posts
Thanks in advance.
I've made few changes. Here is a working plunker
Firstly I upgraded your version to UI-Router 0.2.13 (fixes some issues, simply always use the latest)
The /post is now default
//$urlRouterProvider.otherwise('/');
$urlRouterProvider.otherwise('/posts');
I changed your controller, to not use router params,
// wrong old
/*
app.controller('ProfileCtrl', function ($scope, $routeParams, Profile) {
var uid = $routeParams.userId;
$scope.profile = Profile.get(uid);
Profile.getPosts(uid).then(function(posts) {
$scope.posts = posts;
});
});
*/
// the way with UI-Router
app.controller('ProfileCtrl', function ($scope, $stateParams, Profile) {
var uid = $stateParams.userId;
$scope.profile = Profile.get(uid);
...
JUST to know what is post holding
Also, the passed userId into state contains values like: "simplelogin:82", to observe taht, I added overview of processed post, which is showing info like this:
{
"creator": "3lf",
"creatorUID": "simplelogin:82", // this is passed as userId, is it ok?
"title": "a",
"url": "http://a",
"$id": "-JazOHpnlqdNzxxJG-4r",
"$priority": null
}
Also, this is a fixed way how to call state posts.postview
<!-- wrong -->
<!-- <a ui-sref="posts({postId:post.$id})">comments</a> -->
<!-- correct -->
<a ui-sref="posts.postview({postId:post.$id})">comments</a>
And alos, if the postview should be injected into main area, this should be its defintion
var postView = {
name: 'posts.postview',
parent: posts,
url: '/:postId',
views: {
'navbar#': {
templateUrl: 'nav.tpl.html',
controller: 'NavCtrl'
},
//'#posts.postview': {
'#': {
templateUrl: 'postview.tpl.html',
controller: 'PostViewCtrl'
}
}
};
Check it all here
SUMMARY: Working navigation is among posts - users... the "comments" link is also working, but the target is just loaded ... with many other errors... out of scope here
Related
Recently I started to use ui-router to manage my page status.
I noticed that when I run the server for the first time, the content is there but if I navigate through the pages or reload the page the contents disappears.
I'm aware there was a bug on ui-router that could be causing this. I updated ui-router however the error still persists.
Did anyone manage to find a fix or work around it?
Edit ----
My investigation has lead me to believe that it has nothing to do with the ui-router and more on the time the app takes to complete the promise. As I will get Violation warnings on the setTimeout as part of jQuery
So I was partially right on my last edit hence this answer; jQuery had something to do with my problem (through the use of promises to retrieve data), however it had also to do with how the ui-router works:
As the page content is loaded, ui-router will manage the first load (and subsequent reloads) with the data that is provided by the GET request.
As this is a promise it is not guaranteed (especially as you grow your DB) that the data will be there in time to render the page.
To avoid from happening use the resolve property as part of ngRoute. This property allows for all the necessary data to be loaded before rendering the DOM.
If you want to read more about follow the link below:
https://medium.com/opinionated-angularjs/advanced-routing-and-resolves-a2fcbf874a1c
My code below:
App.js
'use strict';
angular
.module('knoweeApp', ['ui.router'])
.config(['$stateProvider','$urlRouterProvider',function($stateProvider,$urlRouterProvider) {
$stateProvider
.state('home', {
url:'/',
templateUrl: 'views/user.html',
controller: 'UserCtrl as user',
resolve: {
user: function(userFinder) {
return userFinder.getUsers();
}
}
})
.state('teacher', {
url:'/teacher/:name',
templateUrl: 'views/teacher.html',
controller: 'TeacherCtrl as teacher'
});
$urlRouterProvider.otherwise('/');
}]);
Service: userFinder
'use strict';
/**
* #ngdoc service
* #name knoweeApp.userFinder
* #description
* # userFinder
* Service in the knoweeApp.
*/
angular.module('knoweeApp')
.service('userFinder', function () {
// AngularJS will instantiate a singleton by calling "new" on this function
this.getUsers = function (){
return $.get('users.json');
};
});
Controller: UserCtrl
'use strict';
angular.module('knoweeApp')
.controller('UserCtrl', function (userFinder,user) {
this.teachers = user;
this.increment = function (item) {
item.rating += 1;
};
this.decrement = function (item) {
item.rating -= 1;
};
});
Hope this helps, contact me if in doubt
So, I have an HTML page, /profile#IDGoesHere which is tied an an ng-app. The ng-app has three columns (with Bootstrap) and the middle of which is utilising Angular's ng-view.
So it's something like this:
/profile#IDGoesHere (and within it):
All Activity
Posts
Likes
Dislikes
etc
The href links are set on the HTML page outside of the ng-view so when I go into the /profile#IDGoesHere page, I set the userID into a variable using a service. Like below:
profileApp.service('globalParams', function() {
var profileID = '';
var user = {};
return {
getProfileID: function() {
return profileID;
},
setProfileID: function(value) {
profileID = value;
}
};
});
I pass the 'globalParams' service into each controller as I need to access the profileID in order to make further calls to get the specific data for the user.
My Angular Router looks like this:
$routeProvider
.when('/profile:id', {
templateUrl : 'partial/profile/feed.html',
controller : 'mainController',
resolve:{
myData: ['$http', function($http){
return $http.get('/session');
}]
}
})
.when('/posts', {templateUrl: 'partial/profile/posts.html', controller: 'postsController'})
.when('/agreed', {templateUrl: 'partial/profile/likes.html', controller: 'likesController'})
.when('/disagreed', {templateUrl: 'partial/profile/dislikes.html', controller: 'dislikesController'})
.when('/comments', {templateUrl: 'partial/profile/comments.html', controller: 'commentsController'});
});
Now the problem, the links to Posts, Likes, Dislikes etc do not have the profileID in them as they are set when you go to the main route, /profile#IDGoesHere.
This works when I am on the page and keep navigating by using the 'globalParams' service however, if I were to refresh the page when I was on one of the sub-pages, the data is lost.
Note: I can't make the whole page to reload which is why I used the ng-view. I could fix this by doing that but it will defeat the purpose of a single page application.
Does anyone have a good idea on this? Been pulling my hair but feel I am missing something very obvious.
Thanks in advance
Edit: as it was causing some confusion, I have added a screenshot to demonstrate how it looks like:
Got it fixed by attaching the whole < body > to a parent controller and under that controller, I used the $window.location.href and split the ID from URL and then added it to the service, which I could then add to the outer hrefs for my sub-pages, essentially making the sub-pages to have a routeParam as well
I have a main app on my application, called "bionico", this app utilizes UI-router
It has it's own $stateProvider configurations!
And that's fine, it works
BUT I needed to create a new module (cuz I'm trying to insert a step-by-step form on my application, you can find the code here: https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router)
To make this step-by-step form I need to create a new module with it's own $stateProvider configurations, the thing is that it doesn't work, I have included this module called "bionico.formApp" in my main app like this:
angular.module('bionico', ['ui.router', other modules, 'bionico.formApp'])
And that works fine, if I try to use a controller inside "bionico.formApp" it will also work fine. This is the code for this module:
// create our angular app and inject ngAnimate and ui-router
// =============================================================================
angular.module('bionico.formApp', ['ngAnimate', 'ui.router', ])
// configuring our routes
// =============================================================================
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('form', {
url: '/form',
templateUrl: 'templates/campaignForm/form.html',
controller: 'formCtrl'
})
// nested states
// each of these sections will have their own view
// url will be nested (/form/profile)
.state('form.profile', {
url: '/profile',
templateUrl: 'templates/campaignForm/form-profile.html'
})
// url will be /form/interests
.state('form.interests', {
url: '/interests',
templateUrl: 'templates/campaignForm/form-interests.html'
})
// url will be /form/payment
.state('form.payment', {
url: '/payment',
templateUrl: 'templates/campaignForm/form-payment.html'
})
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/profile');
})
// our controller for the form
// =============================================================================
.controller('formCtrl', function($scope) {
// we will store all of our form data in this object
$scope.formData = {};
$scope.test = "FUNCIONA";
// function to process the form
$scope.processForm = function() {
alert('awesome!');
};
});
But to make this form work I need to use <div ui-view></div> and when I put this on my file nothing happens, and I think it's because the app is using my main module "bionico" $stateProvider configurations and not "bionico.formApp"
Here is my html code:
<div ng-controller="formCtrl">
<!-- views will be injected here -->
<div ui-view></div>
{{test}}
the {{test}} from formCtrl works fine, it is printing the variable on my screen, but like I said ui-view is not. Is there a way to tell angular that I want to use "bionico.formApp" configurations and not the configs from my main module?
How would you solve this?
Like I think I have to tell angular that from that moment on I want to use "bionico.formApp" but I don't know how to do it.
I've tried
<div ng-app="bionico.formApp"> but nothing happened, not even errors
I saw then on stack over flow that I have to bootstrap it manually, but then I got an error saying that "bionico.formApp" had already been bootstraped
Update
People suggested that I only needed to rout stuff in only one module, but I needed two different default routs one for my main application and one or my form, but that wouldn't work.
and the reason why I was trying to make this work was because I needed a step by step form (wizard) on my application like this one: https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router)
But I've decided it is not worth the hassle, I'll try to find another wizard for angular js
I'm using now this walktrough wizard:
https://github.com/mgonto/angular-wizard
It was easy to install and it is very easy to customize the template, I recommend!
I'm in the process of learning AngularJS, working on a more in-depth ToDo app. I'm having an issue with trying to limit access to a url or "route" using angular.
When you hit my dev url on my machine (todo.ang) it brings you to todo.ang/#/home, on this view you see the categories which have todos associated to each. EG (category = cat, cat has a todo of "feed", and "play"), when you click a category I'm calling the $scope.goToCategory function (seen in my JS fiddle) which sets a variable for my firebase ref then redirects you too /#/todo. This is working correctly.
My problem is, I don't want the user to be able to access /#/todo if the todoRef variable is still undefined. But it seems like even after $scope.goToCategory is called and todoRef is set to a firebase URL, the routerprovider never gets recalled to know that todoRef has been set to a different value so it always forces you back to /#/home.
code:
var todoRef = undefined;
if (todoRef !== undefined) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl'
});
}
$scope.goToCategory = function(catId) {
test = catId;
todoRef = new Firebase("URL HERE");
$location.path('/todo');
}
I didn't include the entire file of code but if thats necessary, I can do that as well.
JSFiddle
All routes are only being set during the config phase.
what happens in your code is that 'todo' route is ignored during the initiation of ngRoute.
What you should do is to setup the route but have a resolve like so:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl',
resolve: {
todoRef: ['$q', function($q) {
return todoRef ? todoRef : $q.reject('no ref');
}]
}
});
}]);
If 'todoRef' is undefined the route is rejected.
Also you should consider moving 'todoRef' into a service and not on global scope.
You can also listen for route errors and for example redirect to home route:
app.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function() {
$location.path('/home');
});
}]);
Whenever I start the site it works fine, but all the links are broken. They work, I can click on them and it directs me to the right URL but no content relative to that specific page shows up. But if I were to copy and paste that URL into a different browser window, itd redirect me back home as if that URL didnt exist...
Heres my apps JS file:
var app = angular.module("myApp", ['dotjem.routing']);
app.config(function($routeProvider, $stateProvider){
$routeProvider.otherwise({redirectTo: '/home'});
$stateProvider.state('home', {route: '/home', views:{"main":{template:"template/guide.html"}}});
$stateProvider.state(['$register', '$http', function(reg, $http){
return $http.get("api/getpages.php").success(function(data){
for(element in data){
reg(data[element].pagename, {route: data[element].path, view:{"main":{template:data[element].templateUrl}}});
}
});
}]);
});
Im getting this error when I try and click on pages after refreshing on a page that I clicked ona link to previously, then all the links on the menu bar go dead:
Error: Could not locate 'mybuilds' under '$root'.
at Error ()
at a.lookup (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:23732)
at a.resolve (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:23975)
at Object.J.url (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:18670)
at f (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:29260)
at i (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:29537)
at link (http://url.co.uk/angular/myblog/scripts/angular-routing.min.js:8:29694)
at nodeLinkFn (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js:6230:13)
at compositeLinkFn (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js:5640:15)
at compositeLinkFn (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js:5643:13)
<a sref="'{{link.path}}'" class="ng-binding">
Is how my link works.
A JSON sample if it helps:
[{"id":1,"displayName":"Tour","pagename":"home","templateUrl":"templates\/tourview.html","path":"\/home"}]
I have also tried putting in the links manually but still no joy.
<div jem-view="main"></div>
Is my view. The home page works perfectly.
One of the things i can easily spot that seems incorrect is your use of sref, as you point it towards the route rather than the state, sref is meant to make it easier to manage states and their URLS in that you can link directly to them rather than having to duplicate the route in multiple places.
So rather than what you did, you should just say <a sref="link.pagename"> (it's a regular binding, we use '' around it when we make static links, just like ng-include) assuming you have a service or something where you store those links and they are just the json object you got from the server.
Working example: http://plnkr.co/edit/M9Ey6VnJn7wHLqNdHmwh?p=preview
To look at the use of sref, we can have a look at how it's done in that sample:
app.service('menu', function() {
var self = this;
self.register = function(title, state) {
self.items.push({
title: title, state: state
});
}
self.items = [];
});
As I said, you probably have some sort of menu service, in the sample I called this menu, that isn't the best name, but it serves it's purpose in the demo. This is where we register links.
Note that we CAN actually access the whole internal "state" tree, BUT that is not an official API. (it's exposed for testing). So instead, we simply manage it outside. This will make it possible to categorize items as well etc. as we might not want every single state to figure on the top bar.
app.config(['$stateProvider',
function(sp) {
sp.state('home', {
route: '/',
views: {
"main": {
template: "template/guide.html"
}
}
});
sp.state(['$register', '$http', 'menu',
function(register, http, menu) {
// This is just a workaround, home is a static state, but we can't access the
// menu service in the provider so we just register it here. There is 2 "correct"
// aproaches to this problem in my mind:
// - User a menu provider which can be used along static state registration.
// - Just write static references in the HTML.
//
// What I did here was just easier for this sample.
menu.register('HOME', 'home');
return http.get("routes.json").success(function(data) {
angular.forEach(data, function(state){
// Register the "state link" with the menu service.
// All we need here is it't title and the state name.
menu.register(state.displayName, state.pagename);
// Register the actual state. I would have prefered different names here
// but thats preference.
register(state.pagename, {
route: state.path,
views: { main: { template: state.templateUrl } }
});
});
});
}
]);
}
]);
Won't elaborate much more on this that the comments does.
app.controller('siteController', ['$scope', '$state', 'menu',
function($scope, $state, menu) {
//Some ugly bindup just so we can demonstrate that the latebound registration works.
this.menu = menu;
this.state = $state;
}
]);
I like to have a global site controller in my angular apps, in this example we just use it to expose the menu service and state service directly, that is not a recommended approach, but its a quick approach for a demo.
<body ng-controller="siteController as site">
...
<ul class="nav navbar-nav">
<li ng-repeat="page in site.menu.items"
ng-class="{ active: site.state.isActive(page.state) }">
<a sref="page.state">{{ page.title }}</a>
</li>
</ul>
And finally an example of how we build the menu.