AngularJS, single controller or multiple controllers for ng-included views? - angularjs

I have a dashboard page. Say it is Student dashboard and it has lots of details about the student like his personal details, course details, project details and etc. Inside student.html personal-details.html, course-details.html,project-details.html are included using ng-include.
Student can view those details as well as they can edit it. For this functionality, I have written a single controller StudentController as follows,
(function(){
"use strict";
angular.module('myApp')
.controller('StudentController',StudentController);
StudentController.$inject=['StudentService'];
function StudentController(StudentService){
var vm = this;
vm.studentData = null;
vm.editPersonalDetails = editPersonalDetails();
vm.editCourseDetails = editCourseDetails();
activate();
function activate(){
StudentService.getStudent().then(queryStudentCompleted);
}
function queryStudentCompleted(res){
vm.studentData = res.data;
}
function editPersonalDetails(){
StudentService.editPersonalDetails(vm.studentData);
}
function editCourseDetails(){
StudentService.editCourseDetails(vm.studentData);
}
}
In my real application there are lots of panels to be shown and everything is editable.
1)Should I use different controller for different ng-included pages or single controller is enough(think that more than 7 htmls are included and every panel is editable)? if yes how do the child controllers(say personalDetailsController, courseDetailsController which have edit methods in it) get access to the vm.studentData which is received in the studentController? In Page I can directly show it using ControllerAs syntax, but when I edit it I need to send the edited values to the child controller..
2)Currently studentData has every details that should be shown in the page. Should I keep that in that way or I need to split the data as well into smaller chunks?

Related

AngularJS shared service not acting as singleton between modules

I have seen numerous flavors of this question, but cannot seem to find the right answer for my issue.
The problem with the following is the service cannot share data between the other modules/controllers. It does not act as a singleton. Meaning, if I add windows in dashboard and call windowService.getWindows() I will see the windows I added. But if I do the same from myWidget after adding them via the dashboard, or vice versa, the collection in the service is empty as if the other module/controller hasn't added anything. What am I doing wrong here? I expected a single collection of windows to be retained by the service and be accessible from either of my other modules.
Module 1
var dashboard = angular.module('dashboard', [windowServiceModule]);
dashboard.controller('DashboardController', function DashboardController(windowService) {...stuff});
Module 2
var myWidget = angular.module('myWidget', [windowServiceModule]);
myWidget.controller('MyWidgetController', function MyWidgetController(windowService) {...stuff});
Service
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function() {
var windows = [];
this.addWindow = function(win)
{
this.windows.push(win)
}
this.getWindows = function()
{
return this.windows;
}
});
EDIT: while walking through other answers, I tried this: Share a single service between multiple angular.js apps
That exposed a part of the problem, I think. The above suggests iterating over a $rootScope collection, but in my case, there is only one $rootScope in the collection and it is not the same between dashboard and myWidget. As it is, myWidget is in an iframe and now I'm thinking I need to take a different approach, currently testing a solution similar to the link I left in the comments.
Ok, I'm pretty sure my initial code would have worked fine if the myWidget wasn't being loaded in an iframe. I have a dashboard that can open windows embedded or popped out and I wanted to use the service to manage the windows and handle passing state back and forth since popping out the iframe requires a reload of the page.
Anyways, I now have something like this in place in the service and it is working:
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function($window) {
...stuff
var parentScope = $window.parent.getScope();
...other stuff
After that you can access anything in the parent's scope with parentScope. I pass in the angular $window.
Just for reference, getScope() already existed in the parent JSP file. This is what is looks like:
function getScope()
{
return angular.element($("#DashboardController")).scope();
}

How to manage data from one page to another in AngularJs?

I am facing problem to manage the data while redirecting from one page to another in angulrJS.
I have one page (Tyre Card) where few fields like Name, address, isPersonalUser etc... From this page user can go into another page (Vehicle card) where few fields like regNo, vehicle type etc...
Two different controllers are there. Tyre Card and Vehicle card are linked using FK.
When user do some changes into TyreCard and navigate to another page(Vehicle card). I want to manage data of Tyre card, so when user back to this page from vehicle card then those data should be there on Tyrehotelcard.
angular.module('ContentApp').run(function ($rootScope) {
$rootScope.TyreCard = null;
})
})();
angular.module('ContentApp')
.controller('ContentCtrl', ContentCtrl)
ContentCtrl.$inject = ['$scope', '$rootScope', 'api'];
function ContentCtrl($scope, $rootScope, api) {
$rootScope.TyreCard=typecard
}
AngularJS is Single Page Application(SPA). So the idea of the angular application is to design an application with only one page.
So you cannot pass data from one page to another.
What you can use is partials and use ngRoute to navigate from one partials to another within the same page.
See ngRoute documentation
In case you're application is a single page app, the most common solution is to use a Factory (single instance) injected to both controllers, as described here:
Share data between AngularJS controllers
I found the solution. I will use sessionstorage.
Tyre Card data stored here.
vm.createSessionStorage("TyreHotelCardData", vm.data);
When I will back from vehicle page to Tyre card page.
var result = vm.getSessionStorage("TyreHotelCardData");
So in result I will have the Tyrecard data.

How to get the active status of nested Angular UI Bootstrap tabs

In my code I'm using a service (ngResource) to get data dynamically from the server API to create a series of nested tabs.
My code is loosely like this:
var AdminMenu = angular.module('AdminMenu', ['ngResource', 'ui.bootstrap', 'ngGrid']);
AdminMenu.factory('myServices', ['$resource', function($resource) {
return $resource('/api/categories?sort=createdAt asc');
}
and my controller
$scope.categories = myServices.categories.query();
On my template (jade) I have
tabset
if(categories)
tab(ng-repeat="category in categories", heading="{{category.name}}", active="category.active", disabled="category.disabled")
tabset
tab(ng-model="subCats", ng-repeat="subcat in subCats", heading="{{subcat.name}}", active="tab.active") {{subcat.content}}
And that works ok, however I need to get the status (active and heading) of the second and third(not created yet) generation tabs. I can get it from the first generation as Angular seems to automatically add bits to each object of the first generation like active:true, some methods, etc. The same doesn't seem to apply to the subsequent generations.
Interestingly, the tabs are not declared in my controller (like $scope.tabs = obj for instance), they are being added dynamically.
How can I access the status and heading (all details, really) of each subsequent generation of tabs?
I've tried assign the second and third generation in my controller like so: $scope.tabs2 = $scope.categories.subcategories but that didn't work. I suspect it was because the data is being collected dynamically, but I'm not sure. Since I can't assign the tabs in my controller, I can't simply console $scope.tabs2.active to find out which tab is active.
Any suggestions?

Angularjs scope not retaining value

Friends..
For my understanding of how routing works in Angular I have created a simple application. This application has only two pages:
1. The first page will display all rows of the employee table. Upon clicking on a particular row, second page will display a form with details of that employee.
The list that is displayed on the first page uses the following code:
<table>
<tr ng-repeat="employee in employees">
<td>{{employee.firstname}} - {{employee. address}}</td>
<td><span ng-click="getSingleEmployeeDetails(employee.id)">Edit</a></td>
</tr>
</table>
I am using the same controller for both these pages and this controller looks like below:
function EmployeeCtrl($scope,$http,Employee,$location,$routeParams) {
// Get all employee details
var data;
Employee.query().then(function(_data) {
$scope.employees = _data.data;
});
// Get Single Employee Details
$scope.getSingleEmployeeDetails = function(id) {
$scope.employee = scope.employees[id];
$location.path('/editemployee/' + id);
}
}
However the issue I am facing is that when the code gets routed to /editemployee/1
for some reason the $scope.employees looses its values.
In other words the form never gets populated with employee details.
What am I doing wrong here ?
This has to do with scoping. The employees are loaded into the EmployeeCtrl when it is instantiated. Once you perform a routing event in getSingleEmployeeDetails() that causes a different controller to load with a different $scope. A $scope that is separate from the $scope inside EmployeeCtrl. One easy way around this is to let EmployeeCtrl handle the functionality of loading/displaying all employees and a single employee without routing to a new controller. The pros here is that it makes it easier to share information, and you don't have to reload the single employee information when the user clicks on a single employee because you can share that information more easily. The con is that you don't get back button navigation to navigate between selections of single employees.
The other option is to let the SingleEmployeeCtrl reload the information when it navigates. The pro is you get back button access again, but the con is you load the information twice (once for loading the full list, and twice for loading the employee information again). This also allows the user to bookmark single employee records, but who bookmarks things anymore?
Others have already explained the fact that a new controller (and $scope) are created when you change routes. Also note that $scope.employees is populated asynchronously, when the promise is resolved. What is likely happening is that getSingleEmployeeDetails() is being called before the promise is resolved, so the employees array is empty.
To solve the problem, I suggest a different architecture.
You have two views/pages. Each view in Angular typically has its own controller. Models/data are typically stored in services, and an API to retrieve and manipulate those models/data is made available/public by the service. A controller just glues everything together: it injects the service(s) it needs, and then references only the models/data that the associated view needs.
So, even though your app is simple, I suggest the above approach: one service (which stores your employee objects), two controllers, two views. In particular, put the query() call into your service (so it will be called once, when the service is created) and store your data in the service. The service API should define functions/methods that return a promise that will eventually contain the desired data (list of employees, or just one). The controllers should use those methods to get a reference to the desired data.
See also Should services expose their asynchronicity? for an example of how to store the data in the service.

AngularJS - augmenting dynamic HTML content with user-generated content

I'm new to AngularJS and hoping someone can help me get my head round this please!
I'm developing a web e-reader that pulls in pages of HTML content dynamically. So far, I'm doing that with an $http AJAX call and binding it in with 'ng-bind-html-unsafe' (the HTML is our own, simply served from a directory on the same server. I have control over the HTML so I could do this differently if needs be). So each time the user presses previous/next, the controller simply fetches in the previous/next page of HTML and switches that in the model - that works great.
But next I need to augment this dynamic HTML with user additions, e.g. comments and highlights. They need to appear where the user adds them, e.g. a comment would most likely sit underneath a particular paragraph.
With JQuery I guess I would give each of the HTML elements its own ID and associate each bit of user-generated content with a particular ID. Then I could manipulate the DOM myself, for example adding each comment under its associated element.
What's the right approach with AngularJS, since the principle seems to be to avoid direct DOM manipulation altogether?
I could see how it could be done by defining the HTML content as separate elements in the model, having individual JavaScript objects for each paragraph, header, image, etc. But that would basically be defining DOM-like data in JavaScript - and that feels quite wrong and messy...
Use an "ng-include" and dynamically bind a src attribute from the controller. Adding dynamic, user generated content is as adding the binding variables to your html. Following is angular code that implements previous/next buttons that dynamically loads html templates and populates them with the user added comments. At the bottom is a link to a fully functional jsfiddle.
angular.module('app',[]).controller('controller', function($scope){
var change;
change = function(){
$scope.src = $scope.page + '.html';
};
$scope.page = 1;
$scope.items = [];
change();
$scope.submit = function(text){
$scope.items.push(text);
};
$scope.next = function () {
if($scope.page < 3){
$scope.page++;
change();
}
};
$scope.previous = function () {
if($scope.page > 1){
$scope.page--;
change();
}
};
});
http://jsfiddle.net/jwanga/rnL7c/

Resources