How to organize two tables on same view with one controller - angularjs

I have page with two tables. I need to make some actions with both tables, triggered outside: some functions in view controller.
So, both tables has same methods and variables and I should reload or search both tables with button and input in view controller. This is it.
What I want is to separate tables - I dont want to call reloadTable1 and reloadTable2, and use table1Data and table2Data in scope to render it.
First idea was to create service for each table. But the problem was data rendering, when I use just variable to render it.
Here is example: http://plnkr.co/edit/bzgvtFzRWUDad1CR4mZV?p=preview . Not really related, but question: why it doesnt work? I know workaround - use accessors, but I dont really want to create function per variable! I want to use same class for 2 services, but with difference - one argument.
Second idea to use 3 controllers: parent and 1 per each table, reload tables with $scope.$broadcast. But I don't really like this idea: it uses events(I don't think it is good idea to use it in this case) and it will use additional controllers. Also, I think, it is good idea to use service here.
Any advices?

What I normally do if I need to share data between controllers, I use a service.
I modified your Plunker to demostrate
Example:
<div ng-controller="main">
{{service.data | json }}
<button ng-click="service.query()">refresh data</button>
</div>
<div ng-controller="controller2">
{{service.data | json }}
</div>
Controllers and service:
app.controller('controller1', function($scope, myservice) {
$scope.service = myservice;
});
app.controller('controller2', function($scope, myservice) {
$scope.service = myservice;
});
app.service('myservice', function() {
return {
data: [],
query: function() {
for (var i in [1, 2, 3]) {
this.data.push(Math.floor((Math.random() * 100) + 1));
}
}
}
});
You could also use $scope.$broadcast. Or if you want to generate the event from inside the service use $rootScope.$broadcast (you need to inject $rootScope)

Related

Share Variable between controllers of different routes - AngularJS

I'm new on Angularjs and I'm trying to build my first application. Let's say I have to routes that loads two different views:
127.0.0.1:8080/site
127.0.0.1:8080/site_details
Maybe having two different routes is not the right procedure but that it is another problem.
I have two controllers:
Controller 1:
app.controller('controller_1', function($scope, $http, user) {
user.set('Test Example')
});
and Controller 2
app.controller('controller_2', function($scope, $http, user) {
var xxx = user.get()
});
What I want to do is to share data between these two controllers. To do that I did a service in this way:
app.factory('user', function($rootScope) {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
By looking around it seems that having a service built like this should solve the problem. However, what I obtain with the function get() in controller 2 is always an empty return.
By setting breakpoints I can see that both set() and get() functions enters in their respective function in the service.
Is this a correct procedure to share data between controllers belonging of different routes?
EDIT1
The two views are built in the same ways and the are loaded inside ng-view
<html ng-app="app" ng-controller='controller_1'>
CONTROLLER 1
</html>
First, sharing data between a service is a correct approach.
In your case, you need to ensure the order of getting data is after setting data.
Using a $timeout is not a good approach, i think there should be another way, it depend on your detail code.
If your data is set after some event, you just need to pay attention to the order sequence like 'get after data has been set'
If you have to set data in initialization of controller_1, and controller_2 is sibling of controller_1, you can put the initialization logic of user data before bother controller_1 and controller_2 is entered.
I think you had giving factory reference to both html where first and
second controller you given have. in that case you have to give factory referee to main single page where your also loading sub pages(where you kept ng-view)
The problem occurs because, controller_1 was not created before the creation of controller_2. You can modify the controller_2 to introduce some delay using $timeout:
app.controller('controller_2', function($scope, $timeout, $http, user) {
// The time out is added to check your code working,
// You can replace the code or can use, its up to your requirement
$timeout(function(){
var xxx = user.get();
console.log(xxx);
}, 500);
});
Using $timeout will allow some time for creation of controller_1.
Also instantiate the controller_2:
<html ng-app="app">
<body>
........
<div ng-controller='controller_1'>
<div ng-controller='controller_2'>
</div>
</div>
</body>
</html>
You can use rootscope like below.
app.controller('controller_1', function($scope, $http, $rootScope) {
$rootScope.UserInfo ="Test Example";
});
app.controller('controller_2', function($scope, $http, $rootScope) {
var xxx = $rootScope.UserInfo;
console.log(xxx)
});

Angular controller load()

This is a trivia for many, but I never worked with angular before and I need help to achieve a simple task, cause I'm stucked.
My ng-controller processctrl has a load() method declared.
My view has two calls to ng-controller="processoctrl", causing load() method to run twice. processctrl also has a property currentPhase that starts null and load() set its value. I could just write ($scope.currentPhase || load()) to prevent double load(), but the bind to currentPhase occurs atop (in DOM) of the repeater.
I could place an object property into the $rootScope and update it from processoctrl.scope.load(), but it would turn into a madness.
I could simply $($(".headerCurrentPhase")[0]).html($scope.currentPhase) into my load() method, but it's insanity too.
I could rewrite load() to getProcesses = function(){} and $rootScope.$emit('getProcesses',{}) elsewhere and into the controller $rootScope.$on('getProcesses',getProcesses) to prevent this double load(), but I think this is redundate, so how to simply call a controller function instead of load()? Or how to achieve this simple task? Use a directive?
view:
<div ng-include src="'includes/overview-header.html'"></div>
<div ng-include src="'includes/process-info.html'"></div>
excerpt of includes/overview-header.html :
<div class="col-md-12">
....
<h4><strong>currentPhase</strong></h4>
<p ng-controller="processoctrl">
<span class="label label-primary headerCurrentPhase" ng-bind="currentPhase"></span>
</p>
</div>
excerpt of includes/process-info.html :
<tbody ng-controller="processctrl">
<tr ng-repeat="Process in Processes|orderBy:'ID'">
<td>{{Process.isCurrent}}</td>
<td>{{Process.isCurrent}}</td>
<td>{{Process.ID}}</td>
<td>{{Process.Title}}</a></td>
</tr>
</tbody>
processoctrl
(function (app) {
app.controller('processctrl', function ($scope, $rootScope, $routeParams, factorysvc) {
$scope.Processes = [];
$scope.currentPhase = null;
load();
//($scope.currentPhase || load())
function load() {
var promiseGet = factorysvc.getPsby($routeParams.itemId);
promiseGet.then(function (data) {
$scope.Processes = data;
$rootScope.root.empreendimento.currentPhase = $scope.currentPhase = data[0].currentPhase.Title;
}, function (err) {
$scope.Message = "Error " + err.status;
});
}
})}(angular.module('sgaapp')));
Man, I didn't catch what are you want to do, but I want to take you few advise:
Your logic it's really complicated, you should re-think it, cause now it's sucks;
Includes in angular sucks too. Really. You shouldn't use it at all. How to avoid it? Well, there is two option.
First is to use client-side routing (for pages); Instead of using built-in routing, better to use ui-router. You will be able to use nested states and routes;
Second is to use directives instead of includes. Really, just imagine - header or footer, both can be directives, which you just include in pages when it's needed.
What for me - I use first and second solution in same time.
You should avoid to put anything in $rootScope. Perhaps it's couldn't kill you, and from time to time it's the only solution, but, u know, it's a bad practice.
If you need to store data and share it between controllers, the better option is to us factories (or other services, but factories most universal).
Example:
app.factory('MyFactory', function () {
return {
myData: 'some default value',
someCommonFuction: function (a, b) {
return a + b;
}
};
});
As well as data-store, you can use factory as utility-class, with common functions
Is there any real need to have 2 includes?
If overview-header is shared across views, then we can have currentPhase at $rootScope level or having a parent level controller and have it.
If overview-header is only for this view, then we can combine both htmls into one and have processctrl at wrapped tag.

Call service method from ng-bind and other directives

I've a many functions which is repeated in many controllers and for now I just copy and paste it there but what I want is to create 1 global function and inject and call it in all controller.
for example :
<div ng-bind="mycustomreturn(scopeVal)"></div>
which is the best way to do it in service or in root scope I dont want my root scope to be very large so please suggest if we can do it by service
TIA
The best way to share data in angular is by using Services:
angular.module('test', []).service('MyService', function() {
this.mycustomreturn = function() {}
})
.controller(function(MyService) {
// you have two ways
//One:
$scope.MyService = MyService;
// and the view will be:
//<p>{{MyService.mycustomreturn()}}</p>
//Two:
$scope.mycustomreturn = MyService.mycustomreturn();
// and the view will be:
//<p>{{mycustomreturn}}</p>
});

Call Angular directive controller method from another controller

I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;

Communicating between controllers in AngularJs

I have a simple question: what's the best ('cleanest', 'scaleable') path one should go when it comes to interact between (let's say) two controllers. Would that be to define a service and watch that service's return-value in order to react?
I setup a simple example here, where I watch the service's current value:
$scope.$watch(
function() {
return myService.getValue();
},
function(newVal) {
$scope.value1 = newVal;
});
and update that service's value when one of the buttons is clicked.
Can this be done better, smaller, cleaner somehow? What's the best practice here?
Cheers.
Use service to share data between controllers
Your case is trying to share data between controllers, rather than watch service's value in controllers, I think directly reference service object to controller's scope is a better way
So your view can be
<pre ng-controller="cntrl1">Value in cntrl1: {{ myService.value }} <button ng-click="update('value1')">Change to 'value1'</button></pre>
<pre ng-controller="cntrl2">Value in cntrl2: {{ myService.value }} <button ng-click="update('value2')">Change to 'value2'</button></pre>
and change your controllers to
app.controller('cntrl1', function(myService, $scope) {
$scope.myService = myService;
$scope.update = function(str) {
$scope.myService.setValue(str);
}
});
app.controller('cntrl2', function(myService, $scope) {
$scope.myService = myService;
$scope.update = function(str) {
$scope.myService.setValue(str);
}
});
Use $broadcast/$emit
Just as #squiroid points out, you can use $broadcast to broadcast events to any controllers who is monitoring targeted events.
Please note here, you'd better not use $rootScope.$broadcast + $scope.$on but rather $rootScope.$emit+ $rootScope.$onas $broadcast event will bubble down through all descendant scopes, which might lead to serious performance problems.
This is the best way to communicate b/w the controller sharing same data via sevice but it is limited b/w controllers having the same service:-
Instead you can also choose to broadcast events that are captured by other controllers and change that data accordingly this way is more scaleable but not clean :-)
Sender ctrl :-
$rootScope.$broadcast('update', 'Some data'); //method that lets pretty much everything hear it even $scope too.
or
$rootScope.$emit('update', 'Some data');// only lets other $rootScope listeners catch it
Listen Ctrl :-
$rootScope.$on('update', function (event, data) {
console.log(data); // 'Some data'
});

Resources