I have a Nav in which there are two pages representing two AngularJS components - Home and About. I want to pass user name from Home component to About component.
<div ng-app="myApp">
<ul>
<li>Home
</li>
<li>About
</li>
</ul>
<div ng-view></div>
</div>
I tried to establish the component communication using bindings with < but no luck. Basically, they are both independent components. How do I pass data from one component to another in this scenario.
I've created a working example using CodePen. Could anyone please guide how to do it.
Components and Route Config
app.component("home", {
template: `<h1>Home</h1>
Hi {{$ctrl.user}}!`,
controller: function HomeController() {
this.user = "John.";
}
});
app.component("about", {
template: `<h1>About</h1>`,
controller: function AboutController() {}
});
app.config(function($routeProvider) {
$routeProvider
.when("/home", {
template: "<home></home>"
})
.when("/about", {
template: "<about></about>"
})
.otherwise({
redirectTo: "/home"
});
});
View data is destroyed when views change. Store data that needs to persist between views in a service:
app.service("appData", function() {
var user;
this.setUser= val => user = val;
this.getUser= _ => user;
});
Use that service in the component controllers:
app.component("home", {
template: `<h1>Home</h1>
Hi {{$ctrl.user}}!`,
controller: function HomeController(appData) {
this.user = "John.";
appData.setUser(this.user);
}
});
app.component("about", {
template: `<h1>About</h1>`,
controller: function AboutController(appData) {
this.user = appData.getUser();
}
});
For more information, see
AngularJS Developer Guide - Creating services
#georgeawg already answered the question sufficiently. But I feel one should use sessionStorage when storing username or other userDetails because the date in the service gets erased when the user redirects to other pages. Hence storing it once in the sessionStorage can be very helpful.
// Create item:
var myObj = { username: 'admin', privilages: 'RAED' };
$window.sessionStorage.setItem(key, JSON.stringify(myObj));
// Read item:
var item = JSON.parse($window.sessionStorage.getItem(key));
Related
I'm working with an Angular 1 frontend, talking to a very standard REST-ish API. The general structure consists of simple HTML views with corresponding controllers talking to some base URL which usually stays the same without each controller, for example /customer in this simplified example:
Controller
app.controller('customerCtrl', function($scope, $http) {
$scope.loadCustomer = function() {
$http.get('/customer/'+$scope.id)
.then(function(response) {
$scope.customer = response.customer;
});
};
$scope.loadCustomerData = function() {
$http.get('/customer/'+$scope.id+'/data')
.then(function(response) {
$scope.customerData = response.data;
});
};
});
View
<div ng-controller="customerCtrl">
<input type="text" ng-model="id"></input>
<button ng-click="loadCustomer()">Load Customer</button>
<div>{{ customer.name }}</div>
...
...
</div>
And so on. The actual files are a few hundred lines long, each. Now all of a sudden, a new group of users are required to access the app. The frontend view and controller logic are the same, but they talk to a different backend base URL, for example /externalCustomer. The load function call would instead be to $http.get('/externalCustomer/'+$scope.id), and so on.
The views also need different URLs. If accessing the current view at http://app.com/#/customerView, the new one would be at http://app.com/#/externalCustomerView.
Given that there are many more view and controller methods like this (with a hardcoded backend URLs) and I'd rather not copy and paste a few hundred lines and have the logic diverge, what would be the proper way to implement this? It would be great to be able to reuse both the views and controllers and maybe pass some base URL parameter and/or view URL, but I'm not sure how to start.
In your routes
$routeProvider
.when('/:baseUrl', {
templateUrl: 'public/app/customerView.html',
controller: 'customerViewCtrl',
controllerAs: 'customerViewCtrl'
}
});
and in your controller inject $route and read the 'baseUrl' param as
$http.get('/'+$route.current.params.baseUrl+'/'+$scope.id+'/data')
.then(function(response) {
$scope.customerData = response.data;
});
in this way when you pass externalCustomer then that will be used for baseURL and same for customer
Another approach can be like this:
$routeProvider
.when('/customerView', {
templateUrl: 'public/app/customerView.html',
controller: 'customerViewCtrl',
controllerAs: 'customerViewCtrl',
baseUrl: 'customer'
}
}).when('/externalCustomerView', {
templateUrl: 'public/app/customerView.html',
controller: 'customerViewCtrl',
controllerAs: 'customerViewCtrl',
baseUrl: 'externalCustomer'
})
and in your controller inject $route and read the 'baseUrl' as
$http.get('/'+$route.current.baseUrl+'/'+$scope.id+'/data')
.then(function(response) {
$scope.customerData = response.data;
});
I have this problem & I am unable to find the solution for it.
This is an example of code where I am trying to route to variable URL routing
$routeProvider.when('/Book', {
template: 'examples/book.html',
controller: BookCntl,
});
$routeProvider.when('/Book/chapter01', {
template: 'examples/chapter01.html',
controller: ChapterCntl,
});
If I want to fix the url till /Book/chapter and 01 can be a variable. Like if user changes 02 or 03 till 100. Do I need to write the $routeProvider 100 times or can be a simple solution, where I can use the number part as a variable and write single $routeProvider to handle 01 to 100?
No, you do not need to add 100 seperate route definitions. You add a variable to your url template by adding /:some_variable, and then you are to fetch that variable by using the $routeParams service.
Example
$routeProvider.when('/Book/chapter/:chapterid', {
templateUrl: 'examples/chapter-view.html',
controller: ChapterCntl,
});
And then inject $routeParams into your controller:
function ChapterCntl($routeParams) {
var chapterId = $routeParams.chapterid;
//use the id to fetch content.
}
It does seem like you have a different html page for each chapter. If that is the case you can set a function to the template field to generate the path for the html file:
$routeProvider.when('/Book/chapter/:chapterid', {
template: function(routeParams) {
var id = routeParams.id;
return 'examples/chapter'+id+'.html';
},
controller: ChapterCntl,
});
If that case is that you are fetching the data from an API through a service, it might be useful to be using the resolve field instead. The resolve field will loaded the data and be injectable into the controller. Which means that the data will be loaded before transitioning in to the new route.
$routeProvider.when('/Book/chapter/:chapterid', {
templateUrl: 'examples/chapter-view.html',
controller: ChapterCntl,
//Will run the below function before transitioning into new route.
resolve: {
chapter: function($routeParams, chaptersDataService) {
var id = $routeParams.chapterid;
return chaptersDataService.getChapter(id);
}
}
});
And the inject the chapter into your controller:
function ChapterCntl($scope, chapter) {
$scope.chapter = chapter;
console.log( chapter );
}
Have you considered UI Route Provider? You could easily use stateparams..
$stateProvider
.state('book.chapter', {
url: "/book/chapter/:chapterId",
templateUrl: 'book.chapter.detail.html',
controller: function ($stateParams) {
....
}
})
Sources:
https://github.com/angular-ui/ui-router/wiki/url-routing#url-parameters
http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider
You could also stick with routeprovider in a slightly different way than suggested in other answers.
$routeProvider.when('/Book/:chapter', {
templateUrl : { function (dynamicUrl) {
return '/Book/' + dynamicUrl.chapter + '.html';
},
controller: 'ChapterCntl'
});
I'm trying to do the following:
When a user accesses "localhost/people/:id", the information about the respective person is taken from a MongoDB and displayed via Angular.
I have my api, which works perfectly fine, I've double-checked.
I'm using the latest AngularJS (1.4.9) and the new Router (angular-new-router or ngNewRouter).
I have an Angular module:
var personModule = angular.module('app.personDetailed', []);
a factory:
personModule.factory('personService', ['$http', function($http) {
return {
get : function(id) {
return $http.get('/api/people/' + id);
}
}
}]);
and a controller:
personModule.controller('PersonDetailedController', ['$routeParams', '$scope', 'personService', PersonDetailedController]);
function PersonDetailedController($routeParams, $scope, personService) {
var id = $routeParams.id;
personService.get(id).then(function(res) {
$scope.item = res.data;
});
}
This all should be displayed in this view:
<div data-ng-controller="PersonDetailedController">
<h2>{{ item }}</h2>
</div>
(yes, I'm not bothering trying to parse json yet).
The problem is, I am unable to use both $scope and $routeParams at the same time. I can only have one or the other. If I use both, the $scope works fine, but the $routeParams is empty.
Here's the main controller, just in case:
var appModule = angular.module('app', ['app.main', 'app.personDetailed', 'ngNewRouter', 'ngResource']);
appModule.controller('AppController', ['$router', AppController]);
function AppController($router) {
$router.config([
{ path: '/', component: 'main'}
{ path: '/people/:id', component: 'personDetailed'}
]);
}
Seems the new router does away with $scope and binds to the controller instance in the template instead.
Looks like you should use this instead
personModule.controller('PersonDetailedController', ['$routeParams', 'personService', PersonDetailedController]);
function PersonDetailedController($routeParams, personService) {
var personDetailed = this,
id = $routeParams.id;
personService.get(id).then(function(res) {
personDetailed.item = res.data;
});
}
and your view (do not use ng-controller)
<h2>{{ personDetailed.item }}</h2>
I don't understand why, but in Angular controller I have list, and if I success save file I want to go to another page and there show list of files which I save. I put name in list and everything is ok, but probably when I change html/root they deleted all list. I use same controller for two html page.
Controller:
$scope.imenaFajlova = [];
$scope.continueFileUpload=function (){
for (var i = 0;i<(file.files.length);i++) {
$scope.nameFile.push(file.files[i].name);
}
$http({
method: 'POST',
url: uploadUrl,
})
.success(function(data, status) {
document.getElementById("fromFileUpload").reset();
$location.path('/success');
})
.error(function(data, status) {
});
};
config:
uploadFile.config(['$routeProvider', function($routeProvider,$routeParams) {
$routeProvider
.when('/', {
templateUrl : 'resources/html/home.html',
controller : 'uploadFileController'
})
.when('/success', {
templateUrl : 'resources/html/success.html',
controller : 'uploadFileController'
})
.otherwise({
redirectTo: '/'
});
How can I send date to another view or save data when chanfe root url?
You should probably use a service that will handle this data move between pages, rather than using the same controller.
You can read here about services.
As others suggested you should use a factory or a service.
In case of a factory you first write a factory
angular
.module('myApp')
.factory('namesFactory', namesFactory);
function namesFactory() {
var names = [];
return {
pushNames: pushNames,
getNames: getNames
};
function pushNames(x) {
names.push(x);
}
function getNames() {
return names;
}
}
then you inject a factory dependency to your controller and use the methods from the factory inside your controller.
angular
.module('myApp')
.controller('NamesController', NamesController);
function NamesController($scope, namesFactory) {
namesFactory.pushNames("Ante");
console.log(namesFactory.getNames()); // prints ["Ante"] to the console
$scope.names = namesFactory.getNames();
}
For ng-repeat you would then use something like
<ul>
<li ng-repeat='name in names'>{{name}}</li>
</ul>
Using Same controller doesn't mean, it is sharing the same instance of the controller. They either need to have a common parent, which is not reinitialized, or store data in a factory/service and read it from there.
I'm fairly new to Angular and I'm wondering how to go about creating a list/detail view using Angular routes as what I currently have doesn't seem to be working.
The app has a list of 'projects' and when you click on a project you see a detailed view of that selected project, standard stuff. I've got this working using ng-switch but ideally I want to use seperate routes for the list/detail views. I've read that for this I'm going to need to use a factory method but I'm having difficulty passing the selected data between the routes. Here's what I have:
app.factory('Project', [ function ($rootScope) {
var _selectedProject = {};
_selectedProject.project = {};
return _selectedProject;
}]);
app.controller('GalleryController', ['$scope', function ($scope, _selectedProject) {
$scope.sharedProject = _selectedProject || {};
$scope.selectProject = function (proj) {
_selectedProject.project = proj;
};
$scope.$watch('sharedProject', function (proj) {
$scope.chosenProject = proj;
})
}]);
I'm actually getting cannot set property 'property' of undefined which is inside $scope.selectedProject.
A nice solution for this is ui-router.
ui-router allows the nesting of states which correspond to controllers, urls and html templates.
In your example I would do the following:
Install ui-router (described in the link above)
Apply a configuration as follows:
myApp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('project', {
url: "/project",
templateUrl: "partials/project/list.html",
controller: project_list_controller
})
.state('project.details', {
url: "/details",
templateUrl: "partials/project/details.html",
controller: project_detail_controller
})
}
Split your current controller into a project list controller and a project details controller.
Finally I would use a service to store your selectedProject as its a singleton, see the correct useage and differences between services and factories in this helpful blog post
Hope this helps.
You named your factory Project but are using _selectedProject as the injection to controller. You also didn't include it in the injection array
Controller should look more like:
app.controller('GalleryController', ['$scope','Project', function ($scope, Project) {
$scope.sharedProject = Project || {};
$scope.selectProject = function (proj) {
Project.project = proj;
};
$scope.$watch('sharedProject', function (proj) {
$scope.chosenProject = proj;
});
}]);