For some reason, the second Controller isn't receiving the data from the Service.
I'm trying to make the communication between two Controllers using one Service for it.
The View:
<div class="btn-wrapper" ng-controller="FirstController">
<a ng-href="" ng-click="doPath()" id="go" class="button">GO!</a>
</div>
The FirstController.js:
angular.module('app')
.controller('FirstController', function($scope, sharedService) {
$scope.doPath = sharedService.searchPath;
});
The sharedService:
angular.module('myServices', [])
.service('sharedService', function($rootScope) {
this.searchPath = function() {
console.log("I got the service!");
$rootScope.$broadcast('Search', {
data: 'something'
});
}
});
And the SecondController.js
angular.module('app')
.controller('SecondController', function(sharedService, $scope) {
$scope.$on('Search', function(event, data){
console.log(data);
//this.search(); => I intent to run this function after this
});
});
The event is dispatched by a button in the View, that calls the doPath() function. This function does communication with the Service sharedService and the message "I got the service" is displayed.
However, the app stops here. And the communication between Service and the second Controller, using $rootScope.$broadcast, seems that not happening (the data isn't showing on console, neither any error).
I found some solutions here. I have tried already all of answers, so the problem is not the same, cause still not working.
EDIT
The ngRoute is here (app.js):
angular.module('app', ['ngRoute', 'myServices'])
.config(function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'FirstController'
})
.otherwise({
redirectTo: '/'
});
});
As everyone suggested 'Instantiation' of the Second controller is needed.
<div ng-controller="firstController">
//code
</div>
<div ng-controller="secondController">
//code
</div>
Like above.
I know you have used 'ngRoute'. Until you change your view the second controller will not be loaded in 'ngRoute' whereas in above code both the controllers are in the same view. That is why above code works and 'ngRoute' does not.
SecondController is not instantiated by Angular because you are referring only FirstController from html. You need to instantiate the SecondController on the same html using parent child or sibling relationship depending on your application.
Related
I'm building my first web app with Node.js and AngularJs. My problem lies in this code here:
var app = angular.module('Martin', ['ngResource','ngRoute']);
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'partials/home.html',
controller: 'PageCtrl'
})
.when('/etx/:id', {
templateUrl: 'partials/page.html',
controller: 'PageCtrl'
});
}]);
app.controller('PageCtrl', ['$scope', '$resource', '$routeParams',
function($scope, $resource, $routeParams){
console.log('Got here');
var Page = $resource('/api/pages/:id');
Page.get({ id: $routeParams.id }, function(page){
$scope.page = page;
});
}]);
From what I understand, if I were to request /etx/mypage, the function in PageCtrl should be run, but when I try to pull it up in a browser, I get a 404 error and nothing is printed to the console.
I tested it with the home page ('/'), and the controller works fine then.
As #Claies identified, the problem was I forgot the octothorpe (or hashtag if you want to be less cool) in the URL. So it should not be etc/mypage, but #/etc/mypage. This behavior is less than ideal in many cases (including mine), so I recommend disabling this with HTML5 mode for links.
Of course, I should mention that I'm new to this thing, so sorry if this is something trivial.
So I pretty much have 2 routes (views). localhost:3000 takes in and loads up a list of objects and localhost:3000/:slug shows information of the product the users wants to see more info about.
The initial listing is fine. You visit localhost:3000 and you see a list of items.
listPhoneController.js:
angular.module('cmscApp').controller('listPhoneController', ['$scope', '$http', '$location', 'searchBoxFactory',
function($scope, $http, $location, searchBoxFactory) {
$scope.listInfo = searchBoxFactory;
$scope.phoneList = [];
$http.get('/api/getallphones').then(function(res) {
$scope.phoneList = res.data;
}, function() {
$scope.errorMsg = 'Error in reaching data';
});
}]);
list.html:
<!-- ... --->
<div class="result" ng-repeat="phone in phoneList | hasImageFilter:listInfo.imageRequired
| nameFilter:listInfo.phoneName
| priceFilter:listInfo.price.from:listInfo.price.to">
<!-- filters don't seam to be the problem (removing them still causes the issue) -->
<a ng-href="/phone.slug">More info...</a>
<!-- ... -->
</div>
Now, if I click on the a tag, I get redirected to that phone's information view (ie. localhost:3000/samsung-galaxy-s4) and information is being loaded correctly. I also have a back button there, with a simple <a ng-href='/'>Back</a>
But, when I go back, even though the URL changes back to localhost:3000, the list doesn't appear. I get no errors, nothing, but the div's aren't there (when inspecting, nor anything).
Is this because $http is async, so it tries to load the page before it gets the info? If that's the case, why doesn't it just bind the data, as usual?
Here's my config:
angular.module('cmscApp').config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: '/pages/list.html',
controller: 'listPhoneController',
controllerAs: 'lpc'
})
.when('/:phoneSlug', {
templateUrl: '/pages/detail.html',
controller: 'detailPhoneController',
controllerAs: 'dpc'
})
.otherwise({
templateUrl: '/error/404.html'
});
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
});
Any sort of help is more than welcome!! I thought about storing the data from $http to a factory, so then it loads that data every time the controller is run, while updating it as well. would that be a viable solution, or is there something better?
Turns out the scope didn't bind for some reason, so I had to edit the $http.get().then() method:
$http.get(...).then(function(res) {
$scope.data = res;
$scope.$apply();
}, function() { ... });
for anyone encountering a similar issue
In AngularJS it has the ability to route a section of the page using ng-view. This is really nice for smaller projects but as projects get larger it does not scale very well due to the inability to load scripts in the script tag. I've seen a few other posts that regard this but I cannot get a solution to work. The only way I can get it to work (I consider it a hack and doesn't work the way I would like it) is if I include all the scripts at the top of the base Index.html.
What I would like is when the view changes to have the Javascript files, in the header of the View being loaded, be loaded at that point since they are going to actually be used at that point and not have to pre-load them at the beginning of the application.
http://plnkr.co/edit/7LMv0j2sAcjigPuW7ryv?p=preview
In the demo project the Index.html calls the console.log command but I cannot get page1 or page2 to call the log command. Any help would be greatly appreciated.
I am using Angular 1.3.0 Beta 11.
2 things, in your view you don't have to write <html> or <body> tags, you've already got those on you main page. You should see the view as a part of the main page.
second, to add javascript to your views you add a controller to the view like this:
.config(function ($routeProvider) {
$routeProvider
.when('/page1', {
templateUrl: 'page1.html',
controller: function ($log) {
$log.info('page 1');
}
})
.when('/page2', {
templateUrl: 'page2.html',
controller: function ($log) {
$log.info('page 2');
}
});
});
it's also possible to add a controller to a view like this:
var app = angular.module('routeApp', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider
.when('/page1', {
templateUrl: 'page1.html',
controller: function ($log) {
$log.info('page 1');
}
})
.when('/page2', {
templateUrl: 'page2.html',
controller: 'ViewController'
});
});
app.controller('ViewController', function ($log) {
$log.info('page 2');
})
i updated your plunker: Here
I'm going to get some data from server using $http and JSON response. $http.get() are called after route change. But template are changed before data is downloaded. My goal is:
User press a hyperlink in menu, that changes a route,
Shows Loading Spinner (DOM Element is in another controller which is on page everytime)
Initializing $scope.init() (via ng-init="init()") in controller which is in my new template, this also initializing get data from server
Data are downloaded, now I can hide spinner and change visible template
How can I do this? My App looks for example:
Javascript:
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope, $http) {
$scope.init = function() {
$http({method: 'GET', url: 'http://ip.jsontest.com/'}).success(function(data) {
console.log(data);
$scope.ip = data.ip;
});
}
});
myApp.config(function($routeProvider) {
$routeProvider.when('/link', {
controller: 'MyCtrl',
templateUrl: 'embeded.tpl.html'
});
});
HTML:
<script type="text/ng-template" id="embeded.tpl.html">
<div ng-controller="MyCtrl" ng-init="init()">
Your IP Address is: {{ip}}
</div>
</script>
<div>
<ul>
<li>change route</li>
</ul>
<div ng-view></div>
</div>
You need to resolve data before routing happens, thus, move your $http to config section.
This is good tutorial for that, http://www.thinkster.io/pick/6cmY50Dsyf/angularjs-resolve-conventions
This is config part.
$routeProvider.when('/link', {
controller: 'MyCtrl',
templateUrl: 'embeded.tpl.html',
resolve: {
data: function ($q, $http) {
var deferred = $q.defer();
$http({method: 'GET', url: 'http://ip.jsontest.com/'}).then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
}
}
and this is controller part
//`data` is injected from routeProvider after resolved
myApp.controller('MyCtrl', function($scope, $http, data) {
$scope.ip = data.ip
});
I think promise in AngularJS is very important concept for any async. processing.
You need to use this technique every time you have callback.
I will not do it all for you, however I will point you in the right direction.
First you need the ngRoute module.
var myApp = angular.module('myApp', ['ngRoute']);
And the JS file:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular-route.js"></script>
Now you routes will be working and you do not need to call a special init function in your controller since they get instanciated on every route change when used with ng-view.
For the spinner, you could add some interceptors to all ajax requests using the $httpProvider. Inside the interceptors you could emit some events on the $rootScope and listen to then in a specialed custom attribute directive e.g. spinner where the magic would occur ;)
I dont know if this is a good practice... I have a controller defined in route config but because my HomeCtrl is in ng-if statement he cannot listen for loginSuccess so I made MainCtrl which listens for loginSuccess and reacts appropriately. This code works just fine but this smells like a hack to me. Should I remove MainCtrl and make it a service? If so some example would be really great.
Index.html
<body ng-app="myApp" ng-controller="MainCtrl">
<div ng-if="!isLoged()">
<signIn></signIn>
</div>
<div ng-if="isLoged()">
<div class="header">
<div class="nav">
<ul>
<li class="book">navItem</li>
</ul>
</div>
</div>
<div class="container" ng-view=""></div>
</div>
</body>
App.js
angular.module('myApp', [])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'HomeCtrl'
})
.otherwise({
redirectTo: '/'
});
})
.controller('MainCtrl', function ($scope) {
$scope.user = false;
$scope.isLoged = function(){
if($scope.user){
return true;
}else{
return false;
}
}
$scope.$on('event:loginSuccess', function(ev, user) {
$scope.user = user;
$scope.$apply();
});
})
.controller('HomeCtrl', function ($scope, $location) {
//this is home controller
})
.directive('signIn', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
//go to the server and then call signinCallback();
}
};
})
.run(['$window','$rootScope','$log',function($window, $rootScope){
$window.signinCallback = function (res) {
if(res){
$rootScope.$broadcast('event:loginSuccess', res);
}
else{
$rootScope.$broadcast('loginFailure',res);
}
};
}]);
I start all of my Angular projects with:
<html ng-app="appName" ng-controller="appNameCtrl">
The use of a "global" controller may not be necessary, but it is always nice to have it around when a need arises. For example, I use it in my CMS to set a binding that initiates the loading of everything else - so all the sub controllers are loaded because of it. That isn't violating separation of concerns because the global controller's concern IS to facilitate the loading of other controllers.
That said, just be sure to keep things as modular/separated and reusable as possible. If your controllers rely on the global controller's existence in order to function, then there is an issue.
In my opinion angular js' power comes with separating out clearly the different controllers directives, services, resources etc. Ideally controllers are linked to templates or partials and are used to update the front end and make calls to services or resources. The sooner you start making these separations the sooner you will start making clean and scalable apps that other developers can quickly make sense of. For app structure I would highly recommend you look into either of these two tools:
Lineman.js
and
Yeomann
The lineman site actually has a really good round up of how the two differ, if you scroll down.
In your scenario there are many ways to link controllers or make function calls that are in different scopes. You can either create a service that injects to your controllers or you can use $emit and $on to set up notifications in the app eg:
In controller A
$rootScope.$on('myNotifier:call', function() {
myFunction();
});
And in Controller B or any other controller you could call myFunction() with:
$scope.$emit('newPatientModal:close');