duplicate controllers being created by ng-include directive - angularjs

I'm using the ngInclude directive and defining my own controller
<ng-include
src="'app/streaming/streaming.view.html'"
ng-controller="StreamingCtrl"
ng-if="streamingActive">
</ng-include>
This is defined within main-page.view.html which is the top level page for my app and is listed in app.js as follows:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'app/common/main-page.view.html',
controller: 'HomeCtrl',
reloadOnSearch:false
})
.when('/multiview', {
templateUrl: 'app/common/main-page.view.html',
controller: 'MultiviewCtrl'
})
}]);
The streaming control is as follows:
app.controller('StreamingCtrl', ['$scope', function($scope) {
console.log($scope);
$scope.$on('destroy', function () {
console.log('destroy');
});
}]);
Everytime I navigate between the home and multiview pages it looks like a new instance of the controller is being created. The scope is logged and each one has a new id but it seems like the destroy method is never call. I think that this is causing a memory leak with new controller instances being created while the old ones are never destroyed.
Any thoughts greatly appreciated
C

This is because you have mispelled the destroy event, it should be $destroy instead of destroy.
DEMO
.controller('StreamCtrl', function($scope) {
console.log($scope);
$scope.$on('$destroy', function () {
console.log('destroy');
});
});

Related

AngularJs 1.3 check if controller exists

I have the following code in a large Angular project:
$stateProvider.state('my-app', {
url : '/',
views: {
'content#': {
templateUrl: '/?page=/home',
controller: 'HomeController'
}
}
});
I know I can remove the controller using delete element['views']['content#']['controller']; and init with element afterwards but how can I check if HomeController exists? After a day of researching. No working solution seems to exist in Angular.
You should not remove controllers. This should be done by angular.
But you could set some injected service variable on controller creation and clear it 'on destroy'. I.e. to do something like:
angular
.module('something')
.controller('HomeController', HomeController);
HomeController.$inject = ['$scope', 'myGlobalService'];
function HomeController($scope, myGlobalService) {
myGlobalService.isHomeControllerPresent = true;
$scope.$on("$destroy", function() {
myGlobalService.isHomeControllerPresent = undefined;
});
}
But it's more a hack than 'Angular style'.
If you have access to $scope and you know controller name (i.e. ng-controller="homeCtrl as HomeController" or controllerAs: 'homeCtrl') then you could just check:
if ($scope.homeCtrl) {
...
}
because any controller is a part of its scope - each controller is 'attached' to its scope by name.
But once again: no one should remove controllers except angular ;)

Angular Js controller not firing with routes

I am having a problem with AngularJS, where suddenly one of my controllers refuse to execure.
I load it with route, and i know the route is loaded as the html page attached is requested and inserted into the ng-view div i have.
My routes
when('/products/', {
templateUrl: 'app/components/products/products.html',
controller: 'productsController'
}).
when('/products/:productId', {
templateUrl: 'app/components/products/product.html',
controller: 'productController'
}).
I did not see any problems in my controller so i tried replacing all content with just a simple console log, and yet it do still not execute.
angular.module('DietPlanApp').controller('productsController',
['$scope', function ($scope) {
console.log('Get my products!');
}]);
My other controller productController workes just fine.
angular.module('DietPlanApp').controller('productController',
['$scope', '$routeParams', '$location', 'productService', 'userService',
function ($scope, $routeParams, $location, productService, userService) {
productService.getProduct(userService.getUserToken(), $routeParams.productId, function (data) {
if(data.result) {
$scope.product = transferToPortionSize(data.product);
} else {
$location.path("/products/");
}
});
$scope.save = function () {
productService.save(userService.getUserToken(), transferToUnitSize($scope.product));
$location.path("/products/");
};
$scope.delete = function () {
productService.deleteProduct(userService.getUserToken(), $scope.product.id);
$location.path("/products/");
};
}]);
And i have verified that the js files for productsController is loaded correctly in the HTML head, in the same way productController is.
It looks like the result of a namespace collision. You are overwriting productsController in public/app/components/users/authController.js.
It was probably the result of a copy/paste that your forgot to rename. That seems like the most likely culprit.
See the screenshot here.

AngularJS - Show route only after all promises are resolved

I try to load route only after promises are resolved
var app = angular.module("thethaoso", ["ngRoute"]);
app.config(['$routeProvider', '$locationProvider', function ($routeProvider) {
$routeProvider
.when('/', {
resolve: {
message: function (repoService) {
return repoService.getMsg();
}
}
});
}]);
app.factory('repoService', function ($http) {
return {
getMsg: function () {
return "hihihi";
}
};
});
app.controller('teamLoadCtrl', function ($scope,message) {
$scope.message= message;
});
View:
<div ng-app='thethaoso' ng-controller='teamLoadCtrl'>
{{message}}
</div>
Always get the error Error: [$injector:unpr]http://errors.angularjs.org/1.3.7/$injector/unpr?p0=messageProvider%20%3C-%20message%20%3C-%20teamLoadCtrl
full code at http://jsfiddle.net/c0y38yp0/5/
Am I missing something ?
Thanks all.
The problem is that you have not specified a template and a controller to resolve the message object to. If you used the following syntax, it will work.
.when("/", {
templateUrl: "yourView.html",
controller: "yourController",
resolve: {
message: function(yourService){
return yourService.get();
}
}
Here is a working jsfiddle: http://jsfiddle.net/c0y38yp0/10/
You can also resolve the promise manually in your controller like so:
repoService.getMsg()
.then(function (msg) {
$scope.message = msg;
}
When the promise is resolved onto the scope as I did above, the ui will update. You can show a loading bar and use ng-hide to make the pages feel fluent while the loading occurs.
When you resolve, service have to return promise not value.
Here is example service
app.factory('repoService', function ($http, $q) {
var user = {};
var q = $q.defer();
$http.get('https://api.github.com/users/Serhioromano')
.success(function(json){
user = json;
q.resolve();
}).error(function(){
q.reject();
});
return {
promise: function() {
return q.promise;
},
get: function() {
return user;
}
};
});
The point here is that you return promise only. You handle how you save result. And then you can use this result like in get(). You know that by the time you call get() the user variable already set because promise was resolve.
Now in router.
app.controller('MainCtrl', function($scope, repoService) {
$scope.user = repoService.get();
});
app.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: '/view.html',
controller: 'MainCtrl',
resolve: {
message: function (repoService) {
return repoService.promise();
}
}
})
.otherwise({ redirectTo: '/' });
});
You return promise by repoService.promise()
In controller repoService.get() is triggered only after that promise resolved.
So you get your data.
Another thing in your code, you used ng-controller. But that thing is not binded to router and thus it avoid if it is resolved or not. You have to delete ng-controller and use controller router controller: 'MainCtrl',.
This affect your HTML
<body ng-app="myapp">
<ng-view></ng-view>
<script type="text/ng-template" id="/view.html">
<p>Hello {{user.name}}!</p>
</script>
<body>
You have to use <ng-view> to include subtemplate there and then in sub template you can use scope of the controller.
See plunker.
There are a few things wrong with the code you posted, in contrast to the code you are attempting to draw inspiration from.
When you resolve a route with the $routeProvider, the results are applied against an element <ng-view></ngview>, not a base element <div> as you have specified. Without the <ng-view> element, there is no way for the $routeProvider to bind the correct controller to the correct html fragment. Using ng-controller instantiates a controller instance when the dom element is rendered, and does not allow passing parameters to the controller as you have tried. Thus your resolution error due to an unknown message object. Effectively, message is not available outside the $routeProvider instance.

Angular Controller showing $ is not a function error in Dynamic Routing

I am trying to create an Angular Dynamic Routing. My routing is like this:
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives']).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: 'partials/blank.html' });
$routeProvider.when('/:name', { templateUrl: 'partials/blank.html', controller: PagesController });
$routeProvider.otherwise({redirectTo: '/'});
}]);
Here I am using $http to get a template file inside a controller and compile it to a div id like this:
function PagesController($scope, $http, $route, $routeParams, $compile) {
$route.current.templateUrl = 'partials/' + $routeParams.name + ".html";
$http.get($route.current.templateUrl).then(function (msg) {
$('#view-template').html($compile(msg.data)($scope));
});
}
In the template view, I have a div like this:
<div id="view-template" ng-view></div>
I thought the above code will compile and add the html data to the div but I am receiving the error that says: $ is not a function. What have I got wrong here?
EDIT: After the help from the comments and answers below
SOLUTION:: I was playing around with this a bit more and I went with another solution for this. I added the $route.current.templateUrl to the $scope.theTemplateUrl and then used ng-include in the template file. That did the trick and I also dont need to use the jquery $ function to manipulate the DOM.
Please make a fiddle. The limited scope of this snippet inhibits help :)
By just looking at what you are doing I can only make a few recommendations. But I think your issue lies in .html().
Stop using jQuery while you learn Angular.
Use $scope to change content on page. Instead of
$('#view-template').html($compile(msg.data)($scope));
do this
$scope.viewTemplate = msg.data
then use angular in your view :)
Only use the controller to coordinate the flow of information. There should not be and DOM manipulation happening here. The DOM should reflect a state of the controller.
Define routes in your app config. This is not correct.
$route.current.templateUrl = 'partials/' + $routeParams.name + ".html";
I have some example site in my github repo that you can look at if you want to see a few full sites working: https://github.com/breck421
It seems like you have missed some key parts of Angular. Make sure you take your time and learn it right. It will make you life much easier later.
Thanks,
Jordan
Added for a route provider example:
MyApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'js/views/index.html',
controller: 'AppController',
activeTab: 'home'
})
.when('/home', {
templateUrl: 'js/views/index.html',
controller: 'AppController',
activeTab: 'home'
})
.when('/thing1', {
templateUrl: 'js/views/thing1.html',
controller: 'Thing1Controller',
activeTab: 'thing1'
})
.otherwise({redirectTo: 'home'});
}]);
Then use links like this: Components
EDIT Adding a compile directive per request:
angular.module('CC.directive.Compile', [], function($compileProvider) {
$compileProvider.directive('compile', ['$compile', function($compile) {
// directive factory creates a link function
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}]);
});
The $ function is defined by jQuery, not angular. Make sure that you have included the jQuery library in order to use $

Custom state-enter and state-exit code with routing

I have a fairly simple Angular project which routes to a couple different URLs in the JavaScript:
function RootController($scope) {};
function PageOneController($scope) {};
angular.module('mymodule', []).config(
['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: "templates/root.html",
controller: RootController
}).when('/page1/', {
templateUrl: "templates/page1.html",
controller: PageOneController
}).otherwise({redirectTo: '/'});
}]
);
Everything works great so far, but I do need a way to have some JavaScript function run on when these routes are entered and exited. ie:
$routeProvider.when('/', {
templateUrl: "templates/root.html",
controller: RootController,
onenter: function() { console.log("Enter!"); },
onexit: function() { console.log("Exit!"); }
});
Is there a way to do this in Angular? On enter of a state/route, I need to bind event listeners, and on exit I need to destroy them and tear down.
$route service has two events $routeChangeStart and $routeChangeSuccess, these can be of some help for you.
You can use $routeChangeStart before exiting and $routeChangeSuccess on entering.
You can do $scope.$on on any controller to watch for these events.

Resources