UI-Router Child/nested state does not load - angularjs

I've had a look at similar questions, but their problem seems to resolve around the lack of <ui-view /> tag in the parent HTML. Here, it's not the case.
I have a child state that does not load:
routes.js
angular.module('myapp').config(function($stateProvider, $locationProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/');
//A bunch of unrelated routes
$stateProvider.state({
'name': 'users',
'url': '/users',
'controller': 'usersCtrl',
'templateUrl': '/scripts/pages/users/users.html'
});
$stateProvider.state({
'name': 'users.editeduser',
'url': '/editeduser',
'template': '<h1>HELLO</h1>'
// 'templateUrl': '/scripts/pages/users/parts/editedUser.html',
// 'controller': 'editedUserCtrl'
// 'resolve': {
// editedUserId: function(editedUserId) {
// return editedUserId;
// }
// }
});
users.html
<div class="panel panel-default">
<div class="panel-heading">
<h3>Users List</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item" ng-repeat="user in users track by $index">{{user.firstname + " " + user.lastname}}</li>
</ul>
<ui-view name="users.editeduser"></ui-view>
</div>
<div class="panel-footer">
<button class="btn btn-primary" ui-sref="user-edit">New User</button>
</div>
</div>
In the html code uiview line, I tried using <ui-view name=".editeduser"></ui-view> and I also tried just <ui-view name="editeduser"></ui-view> to no avail. I even tried <div ui-view=....
As you can see in the commented out routes.js code, there is a supposed controller and html template that the child is supposed to have, but I removed them for the sake of trying to work out the problem.
In Chrome Dev Tools, this is the output:
Any help would be appreciated. Thanks

The ui-sref for the button doesn't match the url for that state:
<div class="panel-footer">
<!-- ERRONEOUS
<button class="btn btn-primary" ui-sref="user-edit">New User</button>
-->
<!-- INSTEAD -->
<button class="btn btn-primary" ui-sref="users/editeduser">New User</button>
</div>
I think you're onto something though because at no where to I call the child state in my code, and I think I need to call it for it to be loaded, but I am not sure of how to. I'll try to look at the documentation and see –
The UI-Router for AngularJS (1.x) - Hello Galaxy! Tutorial has a DEMO that shows a state with a list of people. The child view with information on an individual only becomes visible when the person is selected.
The DEMO on PLNKR

Related

Using a controller as uibModal Controller

So I am working on a module called "ShowTree".
Before this, I have a controller named "InviteUserCtrl". Inside the ShowTree module, there is a button that will show a uibModal that is used to add new user.
Basically this uibModal in ShowTreeCtrl is all exactly the same as InviteUserCtrl. So, instead of copy pasting all the code from the InviteUserCtrl to the new uibModalController, I decided to inject the InviteUserCtrl as the uibModalController.
The problem is I can't use the InviteUserCtrl as my uibModalController
here is my html code in ShowTree list.html
<div ng-controller="inviteUserCtrl as vm">
<script type="text/ng-template" id="inviteUser.html">
<div class="modal-header">
<h3 class="modal-title">Invite User</h3>
</div>
<div class="modal-body">
<span ng-include="'app_view/modules/invite_user/form.html'"></span>
<br><br>
</div>
<div class="modal-footer">
<button class="btn btn-warning" type="button" ng-click="vm.cancel()">Back</button>
</div>
</script>
</div>
here is the code in my ShowTreeCtrl
function open(size){
var modalInstance = $uibModal.open({
animation : true,
templateUrl: 'inviteUser.html',
controller: 'inviteUserCtrl as vm',
size: size,
});
}
Also i am using ui-router. here is my code in the router
.state('app.show_tree', {
url: '/show_tree/:action/:id',
templateUrl: base_view_url+'modules/show_tree/index.html',
data : { title: 'Show Tree' },
controller : "showTreeCtrl",
controllerAs : "vm",
resolve: load([
base_view_url+'modules/show_tree/showTreeCtrl.js',
base_view_url+'modules/invite_user/inviteUserCtrl.js',
'ui.select'
]),
params : {action:"list",id:null},
activetab: 'Activity'
})
The above code is able to display the form from the InviteUser module, but the controller seems doesn't work. The form button and the controller API do not run at all.
What am I missing here? thank you ..

Reusing a single modal to display dynamic content for a number of modules in angular js

I'm looking to create a stateful app that uses a common modal for a number of modules. On clicking items in a menu, the modal window should open and then I'm hoping to populate the modal with dynamic data based on the button that was pressed.
So for example, if I click 'Users' I would want the modal to pop up and then using angular, I want to populate the modal with all the users
I've been playing around but haven't managed to get very far with it.
Here is the body of my html:
<body ng-app="myApp">
<div class="collapse navbar-collapse navbar-center" id="js-navbar-collapse">
<ul class="nav navbar-nav">
<li class="active">
<a href="#/users" data-toggle="modal" data-target="#Modal">
<div class="users_icon"> </div>
<br>
<p>Users</p>
</a>
</li>
<li>
<a ng-href="#/settings" data-toggle="modal" data-target="#Modal">
<div class="settings_icon"> </div>
<br>
<p>Settings</p>
</a>
</li>
</ul>
</div>
<div class="modal fade" id="Modal" tabindex="-1" role="dialog" aria-labelledby="ModalLabel">
<div class="modal-dialog" role="document">
<form class="form-horizontal" role="form">
<div class="modal-content" ui-view="modal" autoscroll="false">
</div>
</form>
</div>
</div>
</body>
and my js so far:
'use strict';
angular
.module('myApp', [
'ui.router',
'js-data'
])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/calendar');
$stateProvider
.state("Modal", {
views:{
"modal": {
templateUrl: "views/modal.html"
}
},
abstract: true
})
.state("Modal.addtask", {
views:{
"modal": {
templateUrl: "views/modals/addtask.html"
}
}
});
})
I would like to be able to click an item in the menu, use the normal bootstrap javascript to open the modal dialog and then the modal to be populated by angular with dynamic data relative to the menu item clicked. I can't use angular ui as I am using SCSS for styling and it only supports LESS. Any help would be very appreciated.
At the moment, the modal pops up but then I can't get the data to populate in the modal
I managed to do this by utilising sticky states as described here: http://christopherthielen.github.io/ui-router-extras/#/sticky

AngularJS multi-step form validation using ui-router module

I have a form that is spread over multiple tabs. Originally, I used the tabset directive from ui-bootstrap, but then came the requirement to deep link to a specific tab, so I thought I'd use nested views from ui-router.
The problem I have is that the parent form is only valid when all the sub-forms are valid, but using ui-router states, only one sub-form is loaded at a time.
Here's an example to clarify
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<script src="script.js"></script>
</head>
<body class="container-fluid">
<div ui-view>
<ul class="list-unstyled">
<li><a ui-sref="edit.basic">Basic</a></li>
<li><a ui-sref="edit.extended">Extended</a></li>
</ul>
</div>
</body>
</html>
script.js
angular.module('app', ['ui.router']).
config(function($stateProvider) {
$stateProvider.state('edit', {
abstract: true,
url: '/edit/',
templateUrl: 'edit.html',
controller: function($scope) {
$scope.model = {};
}
}).
state('edit.basic', {
url: '/basic',
templateUrl: 'basic.html'
}).
state('edit.extended', {
url: '/extended',
templateUrl: 'extended.html'
});
});
edit.html
<div class="row" ng-form="editForm">
<div class="col-xs-12">
<ul class="nav nav-tabs">
<li ui-sref-active="active">
<a ui-sref="edit.basic">Basic</a>
</li>
<li ui-sref-active="active">
<a ui-sref="edit.extended">Extended</a>
</li>
</ul>
</div>
<div class="col-xs-12" ui-view></div>
<div class="col-xs-12">
<button type="button" class="btn btn-primary" ng-disabled="editForm.$invalid">Save</button>
</div>
<div class="col-xs-12">
<hr>
<tt>model = {{model}}</tt><br>
<tt>editForm.$valid = {{editForm.$valid}}</tt><br>
<tt>editForm.basicForm.$valid = {{editForm.basicForm.$valid}}</tt><br>
<tt>editForm.extendedForm.$valid = {{editForm.extendedForm.$valid}}</tt>
</div>
</div>
basic.html
<div ng-form="basicForm">
<div class="form-group">
<label for="textProperty">Text Property</label>
<input type="text" class="form-control" name="textProperty" id="textProperty" ng-model="model.textProperty" required>
</div>
</div>
extended.html
<div ng-form="extendedForm">
<div class="form-group">
<label for="numericProperty">Numeric Property</label>
<input type="number" class="form-control" name="numericProperty" id="numericProperty" ng-model="model.numericProperty" required>
</div>
<div class="form-group">
<label for="dateProperty">Date Property</label>
<input type="date" class="form-control" name="dateProperty" id="dateProperty" ng-model="model.dateProperty" required>
</div>
</div>
I am beginning to believe that this approach is not suitable for my purpose. Instead of using nested views, I should continue to use the tabset and use a state parameter to activate the appropriate tab.
I'm interested to know how others would solve it.
UPDATE 1:
Here is one solution I came up with that uses the ui-bootstrap tabset but doesn't use nested ui-router states and instead parameterises the active tab.
UPDATE 2:
Here is a solution which uses nested states along with a validator service following the suggestion of Santiago Rebella
UPDATE 3:
Here is an alternate solution to update 2 which uses a validator object on the parent state's scope for comparison
You could try to do it in the way you state just passing to a service some kind of attributes like step1, step2, etc and go adding true or whatever your success value you may use, so once all those attributes are true, in your ng-disabled or the way you are building your form, is allowed to be submitted.
Maybe previous formsteps you could be storing the values of the inputs in an object in your service. I think in this way you could a multi-page form.
controller.js
var app = angular.module('employeeDemoApp');
app.controller('employeesCtrl', function($scope, $state, EmployeeService, $stateParams) {
console.log("Hello")
$scope.saveEmployee = function(formname){
console.log(formname.$error)
EmployeeService.saveEmployee($scope.user);
$state.go("employees");
}
$scope.employeeslist = EmployeeService.getEmployees();
if($stateParams && $stateParams.id){
$scope.user = EmployeeService.getEmployee($stateParams.id);
$scope.user.dob = new Date($scope.user.dob);
console.log("gdfg", $scope.user)
}
$scope.updateEmployee = function(req){
EmployeeService.updateEmployee($stateParams.id, $scope.user);
$state.go("employees")
}
$scope.goto_new_employee_page = function(){
$state.go("employees_new")
}
$scope.deleteEmployee = function(index){
console.log("fgdfhdh")
EmployeeService.deleteEmployee(index);
$scope.employeeslist = EmployeeService.getEmployees();
}
});

Angular-ui router doesn't update links

I am building a basic rss reader, Ihave a basic form which uploads a series of url into a dropdown menu, when a user chooses a specific one, I call an api to access it.
My form is the following:
<div class="panel-body" ng-controller="NewsCtrl">
<form class="form-inline" role="form">
<div class="input-group">
<div class="input-group-btn">
<button class="btn btn-info" type="button">{{loadButtonText}}</button>
<button class="btn btn-info dropdown-toggle" type="button" data-toggle="dropdown"><span class="caret"></span><span class="sr-only">Toggle-dropdown</span></button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="rss in RSSList">
{{rss.Title}}
</li>
</ul>
<input type="text" class="form-control" autocomplete="off" placeholder="This is where your feed's url will appear" data-ng-model="url">
</div>
</div>
</form>
My routes/states are defined as follows:
var myApp = angular.module('myApp',['ngSanitize','ui.router', 'myAppControllers', 'myAppFactories']);
myApp.config(['$stateProvider','$urlRouterProvider','$locationProvider',
function ($stateProvider,$urlRouterProvider,$locationProvider) {
$stateProvider
.state('home', {
url:'/home',
templateUrl: 'views/partials/partial-home.html'
})
.state('app', {
url:'/api',
templateUrl: 'views/partials/partial-news.html',
controller: 'NewsCtrl'
});
$urlRouterProvider.otherwise('/home');
$locationProvider
.html5Mode(true)
.hashPrefix('!');
}]);
And my controller is the following:
var myApp = angular.module('myAppControllers',[]);
myApp.controller('NewsCtrl', ['$scope','MyService', function($scope, Feed){
$scope.loadButtonText = 'Choose News Feed ';
$scope.RSSList = [
{Title: "CNN ",
url: 'http://rss.cnn.com/rss/cnn_topstories.rss'},
{Title: "CNBC Top News ",
url: 'http://www.cnbc.com/id/100003114/device/rss/rss.html'},
];
//Loading the news items
$scope.loadFeed = function (url, e) {
$scope.url= url;
Feed.parseFeed(url).then(function (res) {
$scope.loadButtonText=angular.element(e.target).text();
$scope.feeds=res.data.query.results.item;
});
}
}]);
My problem arised when I changed to use ui-router, I know I have to change this line
{{rss.Title}}
by using ui-sref, but just changing to <a ui-sref="loadFeed(rss.url, $event);">{{rss.Title}}</a>
still doesn't upload my urls into my NewsCtrl controller, any clues?
Bonus question: When I insert a console.log just before $scope.loadButtonText and I open the developer console, it seems to upload NewsCtrl 2 times, why is that?
ui-sref="app"
Should work.
sref stands for "state" reference, instead of the familiar "hyperlink" reference. So it will look for a state defined with .state();
Alternatively, you can go with a good old fashioned url,
href="#/api"
This would have the same effect. sref is recommended by project developers.

AngularJS - "10 $digest() iterations reached" when ng-view ng-repeat dependant on $routeParams

I am really new to Angular and this is the first time that I am dealing with routing, so please excuse me if my questions are a bit confusing. My actual logic and structure for this app is much more complex and I am using several services, but this is the general idea of the part that I am having issues with. I would really APPRECIATE your help! Thanks.
SOLVED
I was adding the same template recursively. Inside the template, I changed that ng-repeat template to another one and it worked perfectly. http://jsfiddle.net/GeorgiAngelov/9yN3Z/111/
UPDATED
jsFiddle: http://jsfiddle.net/GeorgiAngelov/9yN3Z/106/
I updated my fiddle thanks to Chandermani ( I removed all the controllers and left only one copy of it) but now I get "10 $digest() iterations reached" whenever I click on the Add Root button OR whenver I click on a section it keeps giving me that error... No idea why.
So I have an app where I have a menu on my left side where I can add more elements called sections to that menu. That menu section item is derived from an array called sections that sits in my controller and when you add a section it adds it to the array named sections.
$scope.sectionId = $routeParams.sectionId;
$scope.sections = [];
$scope.counter = 1;
$scope.section = {};
$scope.addSection = function(){
$scope.sections.push({
id: $scope.counter++,
name: 'Random Name',
roots: []
});
};
*HTML for sections *
<body ng-app="myApp" ng-controller="MyController">
<div class="tabbable tabs-left pull-left">
<ul class="nav nav-tabs">
<li ng-repeat="section in sections" ng-click="setSectionSelected(section)" ng-class="{active : isSectionSelected(section)}">{{section.name}}</li>
</ul>
<button class="addSection btn btn-success" ng-click="addSection()" style="position:relative; bottom: 0;">Add Section</button>
</div>
<div >
<div class="tab-pane" ng-class="{active : isSectionSelected(section)}" id="{{section.id}}" ng-repeat="section in sections">
</div>
</div>
<div ng-view></div>
</body>
Every object in that array has another array called roots and with another button, named Add Root, I want to push objects(elements) to whatever section I am currently on, depending on the $routeParams id variable.
$scope.addRoot = function(section){
section.roots.push({
id: $scope.counter++,
name: 'Random Root',
});
};
HTML template that is called from the router
<script type="text/ng-template" id="/tpl.html">
<div class="map-container">
<div class="map">
<h2>{{section.name}}</h2>
<h4>{{section.description}}</h4>
<button class="add btn btn-success" ng-click="addRoot(section)">Add Root</button>
<div ng-repeat="root in section.roots" ng-include="'/tpl.html'"></div>
</div>
</div>
</script>
app.config(function ($routeProvider) {
$routeProvider
.when('/section', {
templateUrl: '/tpl.html',
sectionId: '1',
})
.when('/section/:sectionId', {
templateUrl: '/tpl.html',
})
.otherwise({
redirectTo: '/'
});
});

Resources