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;
});
}]);
Related
NET MVC to one CRM application and using angular js for client side script.
I have come across several articles to use angular js and all of them have set controller and services / factories inside the module and they are loaded all in one time.
So my question is it necessary to load all controllers and services in module or I can separate those js and use in page by page whenever I need to use page wise?
I am navigating using ASP.NET MVC pattern not single page using angularjs route.
Yes you can separate those angularjs files and use one angularjs controller per page, but you need to register the modulename of the controller or service in the main module(app.js) inside the brackets[], like this:
//app.js
var app = angular.module('myApp', ['ngRoute','myApp.controller','myApp.create.controller'])
.config(function ($routeProvider,$locationProvider) {
$routeProvider
.when("/", {
templateUrl: 'home/home.html',
controller: 'HomeController',
controllerAs: 'home'
}).when("/create",{
templateUrl: 'manage/create.html',
controller: 'CreateController',
controllerAs: 'create'
})
});
//HomeController.js
var module = angular.module('myApp.controller', ['myApp.service']);
module.controller('HomeController', ['HomeService',
function (HomeService) {
var self = this;
self.EmpData = [];
HomeService.getAllEmployeesTable().then(onSuccess, onError);
function onSuccess(response) {
self.EmpData = response.data;
}
function onError(error) {
alert('Error fetching data');
}
}]);
//CreateController.js
var module = angular.module('myApp.create.controller',['myApp.create.service']);
module.controller('CreateController',['CreateService',
function(CreateService){
var self =this;
self.person = {
name : "", email:"",address:"",telephone:""
}
self.submitPerson = function(){
console.log(self.person);
CreateService.submitPerson(self.person).then(onSuccess,onError);
}
var onSuccess = function(response){
alert("User has been added succesffully");
}
var onError = function(error){
alert("Error creating person");
}
}
]);
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 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.
This post was based on this.
My intention is to separate components on a file basis. For example, I want a specific controller to have it's own file (Same goes with services, filters and directives). Of course, files will be group together based on the module they will fall into. Here's an overview of what I currently have:
Directory
User/
User/UserModule.js
User/UserDirective.js
User/UserService.js
User/UserFilter.js
User/UserController.js
UserModules.js
UserModule = angular.module('UserModule', []);
UserModule.controller('userCtrl', ['$scope', 'UserService', UserCtrl])
.factory('userService', function() {
return new UserService();
})
.filter('userFilter', UserFilter)
.directive('userDirective', UserDirective);
UserController.js
UserCtrl = function($scope, UserService) {
// ...
};
UserDirective.js
UserDirective = function() {
return {
// ...
}
};
UserService.js
UserService = function() {
// ...
};
UserFilter.js
UserFilter = function() {
return function() {
// ...
}
};
Then I'll just push the user module to the app module.
app.requires.push('UserModule');
My concern lies on the registration of the concepts (Such as controllers, services...) to the module. I was wondering if this is the best way to go and if it's correct. Also possible issues on the parameters and the external js file.
Consider this part:
.controller('userCtrl', ['$scope', 'UserService', UserCtrl])
The UserCtrl above refers to a function defined in a separate file. Will I be able to pass the $scope and UserService dependency as parameters to the UserCtrl?
UserCtrl = function($scope, UserService) { // Pass parameters (UserController.js)
What's the correct way of doing this in terms of Services, Filters and Directives?
Finally, how can I improve the code?
I'm also using Meteor btw.
You do not need to declare global variables UserModule, UserDirective, UserService. You just need to declare/register them as below. Angular will take care of injecting dependencies.
UserModule.js
angular.module('UserModule', []);
UserDirective.js
angular.module('UserModule').directive('userDirective', function() {
return {
// ...
}
});
UserService.js
angular.module('UserModule').service('UserService', function() {
});
UserController.js
angular.module('UserModule').controller('UserController', ['$scope', 'UserService', function($scope, UserService){
}])
I have an Angular application that has three different business objects which require CRUD operations to be implemented on all of them independently. Let's call them a,b,c. I have equivalent aCtrl,bCtrl,cCtrl and aSvc,bSvc,cSvc to manage these CRUD operations.
So in this example, aCtrl manages CRUD for 'a'. aSvc helps persist the data to the backend with an ajax call.
I also have an allCtrl and allSvc which pulls all the objects a,b,c together from the backend in one json object when the application loads for the first time (instead of pulling them separately, I designed the backend to push one unified json object with embedded a,b,c in it.
The 'all' object
{
a:{},
b:{},
c:{}
}
so I'm stuck in structuring the app in a straightforward meaningful way. When the app loads for the first time, I'll have 'all' object pulled in by the allSvc from the backend. This has all the data needed to perform CRUD (of course I have to keep it in sync with the backend). And when the app loads for the first time, I want to list the objects of type 'a' and then give the user options to edit/delete/add. Similarly, I have drop down navigation to do take the user to other pages that do exactly the same for object 'b', 'c'.
Here are some snippets of what I did so far and how I fail miserably :)
index.html
<html lang="en" ng-app="myApp">
...
...
<div class="" ng-view></div>
js
var myApp = angular.module('myApp', ['ngRoute','restangular']);
myApp.controller('aCtrl', function($scope, aSvc)
myApp.controller('bCtrl', function($scope, bSvc)
myApp.controller('cCtrl', function($scope, cSvc)
myApp.controller('allCtrl', function($scope, allSvc)
routes
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/a/list', {templateUrl: 'partials/a/list.html',controller: 'aCtrl'});
$routeProvider.when('/a/add', {templateUrl: 'partials/a/add.html', controller: 'aCtrl'});
$routeProvider.when('/a/edit/:id', {templateUrl: 'partials/a/edit.html', controller: 'aCtrl'});
$routeProvider.when('/a/delete/:id', {templateUrl: 'partials/a/list.html', controller: 'aCtrl'});
.... similary I have routes for b & c to perform crud
$routeProvider.otherwise({redirectTo: '/a/list'});
}]);
aCtrl
myApp.controller('aCtrl', function($scope, aSvc,allSvc) {
$scope.allCollection= allService.getAll();
$scope.aCollection = allCollection.a;
I'm unable to meaningfully setup routeParams in this design to perform edit/delete properly. I also have to account for the userId (the user who logs in for whom the CRUD operations need to be performed). How do I better manage the routes? should I use something like /a/edit/userId/aId for editing 'a' and /a/delete/userId/aId to delete 'a' as an example?
Please help me polish this turd.
I have separated the service/update/request calls in a controller and the project can be found at the below path. Let me know if anyone thinks it can be improved further. However, to test the services, I have used strongloop and the description can be found at the repository itself.
Angular - Create, Update, Delete
The sample would look like this:
'use strict';
function MainController($scope, $state, $rootScope, $window, $stateParams, HttpService) {
$scope.init = function () {
HttpService.load("http://0.0.0.0:3000/api/custdetails")
.then(function (response) {
if (response) {
console.log(JSON.stringify(response.data));
$scope.showAllData = response.data;
}
}, function (error) {
console.log("Error occurred");
});
};
$scope.addMoreData = function () {
var data = {
cust_name: $scope.custNameModel,
phone_number: $scope.phoneNumberModel,
id: $scope.idModel
};
HttpService.update('http://0.0.0.0:3000/api/custdetails?', data);
$scope.init();
};
$scope.updateData = function () {
var data = {
cust_name: $scope.custNameModel,
phone_number: $scope.phoneNumberModel,
id: $scope.idModel
};
HttpService.patch('http://0.0.0.0:3000/api/custdetails?', data);
$scope.init();
};
$scope.deleteData = function () {
console.log("ID defined is: " + $scope.idModel);
HttpService.delete("http://0.0.0.0:3000/api/custdetails", $scope.idModel);
$scope.init();
};
}