AngularJS loading more than once when using nested states - ruby-on-rails

I'm getting a WARNING: Tried to load angular more than once. when trying to use nested views on a Ruby on Rails and Angular app. I'm using ui-router and angular-rails-templates.
Angular config and controllers:
var app = angular.module('app', ['ui.router', 'templates']);
app.factory('categories', ['$http', function($http) {
var o = {
categories: []
};
o.get = function (id) {
return $http.get('/categories/' + id + '.json').then(function (res) {
return res.data;
});
};
return o;
}]);
app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
});
$stateProvider
.state('home.categories', {
url: '/categories/:categoryId',
templateUrl: 'categories/_categories.html',
controller: 'CategoriesCtrl',
resolve: {
category: ['$stateParams', 'categories', function($stateParams, categories) {
return categories.get($stateParams.categoryId);
}]
}
});
$urlRouterProvider.otherwise('/home');
}]);
app.controller('MainCtrl', function ($state) {
$state.transitionTo('home.categories');
});
app.controller('CategoriesCtrl', [
'$scope',
'categories',
'category',
function($scope, categories, category) {
$scope.posts = category.posts;
}
]);
And the templates:
_home.html (*edited: deleted body and ng-app from this template, following #apneadiving advice, and used the right path, as pointed by #Pankaj Now, views are rendered properly, but the 'loading more than once' persists)
Category One
Category Two
<ui-view></ui-view>
_categories.html
<ul ng-repeat="post in posts">
<li>{{post.title}}</li>
</ul>
app/views/layouts/application.html.erb
<!--<rails head>-->
<body ng-app="app">
<ui-view></ui-view>
</body>
What is happening is that the state 'home' appears and when I click on the links, the URL changes but nothing else happens. And I get the message that Angular is being loaded more than once.
EDITED: Got it working. It was a <%= yield %> conflict in another part of the app that should have nothing to do with the Angular part.

As you are defined the child states, you need to change your URL's to /home as they are child of home state, the URL gets inherited from parent state.
Category One
Category Two

You shouldnt boot your app in a template like you do.
Add the ng-app to your html's body and remove it from the template (actually your template shouldnt bear the body either)

Related

AngularJS: Cant fetch any data from rails API

I made this small API on rails with a single resource called Items, that works fine while I use it on the browser (a.k.a http://localhost:9000/api/items/1.js - Configured port 9000 with Grunt proxy), but when I try to get that data from Angular I get nothing.
//app.js
.config(function ($routeProvider) {
$routeProvider
.when('/items', {
templateUrl: 'views/items.html',
controller: 'ItemsCtrl'
})
.otherwise({
redirectTo: '/'
});
});
app.factory('Item', ['$resource', function($resource) {
return $resource('http://localhost:9000/api/items/:id.json', null, {
'update': { method:'PUT' }
});
}]);
//items.js
angular.module('balenciagaApp')
.controller('ItemsCtrl', ['$scope', 'Item', function ($scope, Item) {
$scope.items = Item.query();
}]);
//items.html
<ul ng-repeat="item in items">
<li>{{item.name}}</li>
</ul>

AngularJS Ui-Router, views, and tabs

Ok, I'm using ASP.NET MVC5 + AngularJS 1.3.8 here.
Index.vbhtml:
<div ng-controller="MainController">
<tabset justified="true">
<tab ng-repeat="t in tabs" heading="{{t.heading}}" select="go(t.route)" active="t.active"></tab>
</tabset>
</div>
<div ui-view="main"></div>
Tabs are working fine.
App.js:
App.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
//$locationProvider.html5Mode(true); // HTML5 Mode to remove hashbang, want to revist this later as it requires IIS rewrite rules
$urlRouterProvider.otherwise("/");
$stateProvider
// Main Page
.state('main', {
abstract: true,
url: '/',
views: {
'main': {
templateUrl: '/',
controller: 'MainController'
}
}
})
// Home page
.state('home', {
url: '/home',
views: {
'main': {
templateUrl: '/Home/Index',
controller: 'HomeController'
}
}
})
// Requests Page
.state('requests', {
url: '/requests',
views: {
'main': {
templateUrl: '/Request/Index',
controller: 'RequestController'
}
}
})
// Leave Requests page
.state('requests.leave', {
url: '/leave',
views: {
'main': {
templateUrl: '/Request/Leave',
controller: 'LeaveController'
}
}
})
App.controller('RequestController', function ($scope, $state) {
$scope.loadComp = function () {
$state.go('requests.comp');
};
$scope.loadOvertime = function () {
$state.go('requests.overtime');
};
$scope.loadLeave = function () {
$state.go('requests.leave');
};
});
Ok, so what I'm trying to accomplish is that on my Requests tab, I have three buttons, and when I click on one of those buttons, replace the parent view "main" with the appropriate state.
I see the URL change on the button click, but nothing renders.
I am getting my partial views back from the server via AJAX with no issues, except for these button clicks :(
I'd love to know what I'm doing wrong :) Thanks!
FWIW: Angular UI-Router: child using parent's view I've been trying to rework this with no luck...
What I would do is trying to point to the real templates in templateUrl like so: templateUrl: '/Request/Index.html'

Can't get child views to load in AngularJS with ui-router

I'm trying to understand the concept of using the ui-router (v0.2.10) with AngularJS (v1.2.15) through converting an old project that previously used ngRoute and have been unable to figure out why it isn't working. I have an ASP.NET MVC website that shows the base page with something similar to the following:
index.cshtml
<!DOCTYPE html>
<html lang="en-US" ng-app="home">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
#Styles.Render("~/Content/css")
</head>
<body>
<div ui-view></div>
#Scripts.Render(*all the angular, jquery and other script references here)
</body>
</html>
app.js
angular.module('home') ['ui.router'])
.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$urlRouterProvider.when("", "/");
$urlRouterProvider.otherwise("/");
var root = {
name: 'root',
url: '/',
abstract: true,
templateUrl: '/Scripts/home/templates/root.html',
controller: 'RootController'
};
var home = {
name: 'root.home',
url: '/home',
parent: root,
templateUrl: 'Scripts/home/templates/home.html',
controller: 'HomeController'
};
$stateProvider
.state(root)
.state(home);
$locationProvider.html5Mode(true);
};
rootCtrl.js
angular.module('home').controller('RootController', function() {
});
homeCtrl.js
angular.module('home').controller('HomeController', function() {
});
root.html
<navbar/>
<div class="wrapper" style="padding-top:25px;margin-bottom:20px">
<div ui-view></div>
</div>
<footer/>
navbar.js
angular.module('home').directive('navbar', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/Scripts/home/templates/directives/navbar.html'
};
});
The navbar directive template simply contains the bootstrap navbar.
footer.js
angular.module('home').directive('footer', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/Scripts/home/templates/directives/footer.html'
};
});
Except that nothing shows up on the page except for the CSS references at the top, the following, and then the javascript references at the bottom:
Why doesn't the root.html view show up on the screen when I load up the page? Thanks!
As per another answer on this site, one cannot self-close the tags for custom directives. Thus, if I change the tags in root.html to the following:
<navbar></navbar>
<div class="wrapper" style="padding-top:25px;margin-bottom:20px">
<div ui-view></div>
</div>
<footer></footer>
It works fine.

Iron-router _id after url

I am using Iron-router and everything works fine. Changing urls goes nicely from within the app, but if I enter the url directly in the navigation toolbar of the browser, it gives an extra string.
For instance /home becomes /home#.UwPUPc6AnwX
Router.map( function () {
this.route('home', {
data: function() {
Session.set('cmsPage', 0);
}
});
this.route('home2', {
path: '/',
template: 'home',
data: function() {
Session.set('cmsPage', 0);
}
});
});
Any thoughts?
I found the part which generates the strange URL. It's the 'Addthis' library placed in the head section.
<!-- messes up the URL -->
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-52e579bc6a1e3f4c"></script>

How can I have an AngularJS module routes use its own controllers?

I'm trying to learn AngularJS' view and routing mechanism, following AngularJS' own tutorial.
My problem is the tutorial is declaring all its controllers in the global scope, and I belive this is a bad practice because we're polluting it as we add more controllers.
This is a quick working page I've been able to build following the aforementioned tutorial (there's a fiddle, too):
<!doctype html>
<html>
<head>
<title>Test</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js"></script>
<script>
"use strict";
var MyFirstController = function ($scope) {
// Do something here.
};
var MySecondController = function ($scope) {
// Do something here.
};
var myModule = angular.module("MyModule", []);
myModule.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/first-page", {
template: "<p>My first controller.</p>",
controller: MyFirstController
});
$routeProvider.when("/second-page", {
template: "<p>My second controller.</p>",
controller: MySecondController
});
}]);
$(document).ready(function () {
angular.bootstrap(document, ["MyModule"]);
});
</script>
</head>
<body>
<h1>Test</h1>
<div data-ng-view></div>
<p>Click me!</p>
<p>Click me too!</p>
</body>
</html>
Being naïve, I tried to move the controllers inside the module:
myModule.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/first-page", {
template: "<p>My first controller.</p>",
controller: MyFirstController
});
$routeProvider.when("/second-page", {
template: "<p>My second controller.</p>",
controller: MySecondController
});
}]);
myModule.controller("MyFirstController", ["$scope", function ($scope) {
// Do something here.
}]);
myModule.controller("MySecondController", ["$scope", function ($scope) {
// Do something here.
}]);
Alas, it doesn't (obviously) work, throwing a ReferenceError: MyFirstController is not defined exception.
How can I have an AngularJS module use its own controllers in its own routes configuration?
Once you know the solution, it's really simple: just specify the controller as strings instead of objects:
myModule.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/first-page", {
template: "<p>My first controller.</p>",
controller: "MyFirstController"
});
$routeProvider.when("/second-page", {
template: "<p>My second controller.</p>",
controller: "MySecondController"
});
}]);
This way AngularJS will resolve the controller name to the one you've defined inside the module.
And it's minification safe too!
I've created a fiddle demonstrating it.

Resources