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
Related
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.
I want to display an HTML file when a user has logged in. Right now we have a slideshow that is presented to the user after logging in, but after it, I want to display another view.
I jumped on this project although I'm new to Angular and Ionic, I assume this is how we render out the slideshow:
In our controller, we have an IntroCtrl which operates the logic behind the slideshow. So, do I need to create another controller to display my view afterwards?
.controller('IntroCtrl', function ($scope, $state, $localStorage, $ionicSlideBoxDelegate, $ionicScrollDelegate, $stateParams) {
$scope.$on('$ionicView.enter', function (e) {
$ionicScrollDelegate.resize()
$ionicSlideBoxDelegate.slide(0, 1)
})
$scope.email = $stateParams.email
$scope.startApp = function () {
$localStorage.hasViewedSlideBox
$state.transitionTo('tab.dash')
}
$scope.next = function () {
$ionicSlideBoxDelegate.next();
}
})
I found this in our app.js and this file contains $stateProvider, $urlRouterProvider, $ionicConfigProvider.
At the bottom I found a lot of these:
.state('login', {
url: '/login',
cache: false,
templateUrl: 'templates/login.html',
controller: 'LoginCtrl'
})
Therefore I assume I should create a new one containing the templateUrl and controller of my view? I read online that these .state has to do with Angular routing, but I can be wrong.
However, I would be really happy if anyone could guide me here.
Like I said, I want to display a new view after the slideshow.
Thanks!
I suggest create a new html and design your "afterLogin" html and copy your state(to which you will be redirected after login), comment it out and change the templateUrl to your new html file.
That should do all what you want.
You don't need your IntroCrl in the new file because it will only run a slideshow which is not present.
But you should insert
$scope.startApp = function () {
$localStorage.hasViewedSlideBox
$state.transitionTo('tab.dash')
}
into your new controller.
If you don't have a route to which you are redirected after login, you have to provide more code, so we can dive more deep into it
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
Currently I have a local server running with my main page at localhost
I ideally want the setup where the url has a key that I send off to my database to get data and the main page loads that data
For example, someone enters the url of localhost/4962 then 4962 gets sent off to the database, while localhost loads. I am using angularJS so I do have a way of getting the url by doing...
var module = angular.module('app', []).run(function($rootScope, $location) {
$rootScope.location = $location;
});
Then location.absUrl() gets me the complete URL.
But obviously there is no page at localhost/4962, so i just get a 404.
How do i ignore the numbers in the url? Also how do I get just the numbers, I would be willing to do localhost/#/4962 if a hash would simplify matters.
EDIT:
Below are some useful answers. Here is what I went with -
var app = angular.module("app", ["ngRoute"]);
app.config(function($routeProvider) {
$routeProvider
.when('/:message',
{
template: " ",
controller: "AppCtrl"
}).otherwise({redirectTo: '/'});
});
app.controller("AppCtrl", function($scope, $routeParams) {
$scope.model = {
message: $routeParams.message
}
//debugging
console.log($scope.model.message)
});
I needed to have
<ng-view></ng-view>
in the index.html.
Angularjs using $routeProvider without ng-view has a way of avoiding the addition of ng-view. Will try that next.
Do it like this:
angular.module('app', ['ngRoute']).config(function($routeProvider, $locationProvider){
$routeProvider.when('/:number?', {
controller: 'homeCtrl',
templateUrl: '/home.html'
});
$locationProvider.html5Mode(true);
}).run(function(){
...
});
In this case, number is optional route paramerer (because it has ? at the end). Then you can get number from controller:
angular.module('app').controller('homeCtrl', function($routeParams){
var number = $routeParams.number;
});
Don't forget to include angular-route.js file ;)
You can use the $location.path()-Function, should return just the path (without hashtag).
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');