AngularJS data binding between service and controllers - javascript

I would like to have clarifications about data-binding with AngularJS.
I wish to have an array in service and share it with multiple controllers.
I would like , when a controller modify data in service, that the modification was take in account in others controllers et theirs html templates was update automatically.
Unfortunately, I succeed a little, but not with an angular manner because I use for this the Observer pattern or angular.copy..
When I try with a pure angular way, it does not work.
Here an example of code:
The service that contains datas to shared
app.factory( "MyService", [
function () {
myTableToShare = [];
return {
myTableToShared : myTableToShare,
};
}
] );
The controller that modify data of service
app.controller( 'AddCtrl', [
'$scope', 'MyService', function ( $scope, MyService ) {
$scope.myService = MyService;
$scope.addElementToSharedTable = function( element ) {
$scope.myService.myTableToShare.push( element );
};
}] );
The controller that permit to print datas of table into template
app.controller( 'ReadCtrl', [
'$scope', 'MyService', function ( $scope, MyService ) {
$scope.myService = MyService;
}] );
**The template that print elements of table **
<div ng-controller="ReadCtrl">
<ul >
<li ng-repeat="element in myService.myTableToShare">
<span>{{element.id}} - {{element.name}}</span>
</li>
</ul> </div>
I have apply this method thanks to differents HOWTO. unfortunately these HOWTO did not use an array object but a String. Is it a path to follow?
In waiting I apply the pattern observer et I notify events from controller to services but it seems to me that its not a good solution because it too verbose.
Thaks

use app.value(...) if controllers are in the same module 'app'.

Related

How do you inject a condition service into AngularJS 1 Controller

I have an angular module that is designed to be self contained. That a consuming app can add the directives with a url param and it will use that url as it's overall data source when interacting with the widget. This has a generic LoadService that uses $http to load the data, and expects a specific JSON format to run the widget.
Well right now I am trying to refactor so that someone can also create a custom load service and inject it into the module, but if it's not injected then it will use the default data load. So I am trying to figure out how I can create a way that CustomLoadService is injected if it is defined by the app that is consuming the module. However it should not error out if the custom service isn't defined, it should just use the default service.
I was looking into the $injector.get and saw that as a possibility but I am having trouble injecting the $injector into a controller. I thought it would be as simple as $location to inject. Something like...
angular
.module('Widget')
.controller('WidgetController',[
'$scope',
'WidgetLoadService',
'$injector',
WidgetController
]);
This method doesn't seem to work so I am wondering... What is the best most "angular way" to solve this issue. How should I be using the $injector.
You can use $injector:
app.controller('MainCtrl', function($scope, $injector) {
$scope.name = $injector.get('test').name;
}).factory('test', function() { return {name: 'world'} });
So you may have something like this as a result:
app.controller('MainCtrl', function($scope, shareService) {
$scope.name = shareService.getData();
shareService.setDataService('dataService2');
$scope.name = shareService.getData();
})
.factory('shareService', function($injector) {
var dataServiceName;
return {
setDataService: function(name) {
dataServiceName = name;
},
getData: function(name) {
return $injector.get(dataServiceName || 'dataService').data;
}
}
})
.factory('dataService', function() { return {data: 'world'} })
.factory('dataService2', function() { return {data: 'world 2'} });

Dependency injection hell, what is expected?

I'm trying to separate components into several files for a simple application but angular's dependency injector is giving me headaches and I don't really know what is expected.
Unknown provider: servicesProvider <- services <- maincontroller
Is the error I'm getting.
app.js
//Application definition with injected dependencies
var app = angular.module('leadcapacity', ['services', 'utils', 'customfilters', 'controllers']);
services.js
var services = angular.module('services', []);
services.service('xrmservice',
[
'$http', function($http) {
var oDataUrl = Xrm.Page.context.getClientUrl() + '/XRMServices/2011/OrganizationData.svc/';
var service = {};
service.query = function(entitySet, query) {
return $http.get(oDataUrl + entitySet + '?' + query);
};
return service;
}
]);
controllers.js
var ctrls = angular.module('controllers', ['utils', 'services']);
ctrls.controller('maincontroller',
function ($scope, services, utils) {
};
});
And the include order in index.html
<script src="service.js"></script>
<script src="controllers.js"></script>
<script src="app.js"></script>
Looks fine to me. I know this is perhaps not the best way to organize things, but getting a "Hello world" first would be nice.
Thanks.
Error message appearing in console clearly says that, services
dependency isn't exists in the module.
You have injected incorrect service name in maincontroller controller factory function, basically you were trying to to inject services(module name) instead of xrmservice(service name)
function ($scope, services, utils) {
should be
function ($scope, xrmservice, utils) {
Additional
Do follow Inline Array annotation of DI, as you were already used the same in your xrmservice service JS file, so that in future you don't need to go back and change that when you face javascript minification related issues.
Controller
ctrls.controller('maincontroller', [ '$scope', 'xrmservice', 'utils',
function ($scope, xrmservice, utils) {
//code goes here
//....
};
}]);
Although you have injected them into the module, you need to give them to the function so you can use the injected modules
ctrls.controller('maincontroller',
['$scope', 'services', 'utils', function ($scope, services, utils) {
};
}]);

Sharing scope in different views with custom controllers

I've got the next problem and I'd like to find a solution or if I should even be doing this like I am.
I have the following ui-router configuration:
$stateProvider.state('ListCounterparties', {
url:"/counterparties",
data: { NavMenuItem : 'Counterparties / Desks' },
views: {
'' : {
templateUrl:"./app/module_Counterparties/templates/ListCounterparties.html",
controller:"CounterpartiesControllerCtrl"
},
'deskLists#ListCounterparties' : {
templateUrl : './app/module_Counterparties/templates/DesksDualListTemplate.html',
controller:'DesksControllerCtrl'
}
}
The first, unnamed view, has a table from which I can select a row and then a method will be called to populate a dual list from the second view.
Up until now, I've had both in the same controller, but the controller is getting too big and I decided I had to separate them.
The method to populate the dual lists in 'deskLists#ListCounterparties' is defined in DesksControllerCtrl but it should be called in CounterpartiesControllerCtrl, as the event of row selection is in that controller.
The problem is that the scopes are not shared and the method is inaccesible to the unnamed view.
Accessing the scope in DesksControllerCtrl I could see that accessing the $parent property twice I can get to the CounterpartiesControllerCtrl, but I don't thin that's an ideal thing to do.
Thanks in advance.
Sharing data, methods, etc. between multiple controllers the Angular way would be to create service(s). That means, you create e.g. a service which holds all your data and another one which provides functionality for several controllers. Then, just inject them in your controllers:
var myApp = angular.module('myApp', []);
myApp.factory('myGlobalData', function() {
return {
data: 1
};
});
myApp.factory('myService', function(myGlobalData) {
return {
increment: function() {
myGlobalData.data++;
}
};
});
myApp.controller('MyCtrlA', function($scope, myGlobalData, myService) {
$scope.myGlobalData = myGlobalData;
$scope.myService = myService;
});
myApp.controller('MyCtrlB', function($scope, myGlobalData, myService) {
$scope.myGlobalData = myGlobalData;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<p ng-controller="MyCtrlA">
{{myGlobalData.data}}
</p>
<p ng-controller="MyCtrlB">
{{myGlobalData.data}}
</p>
<div ng-controller="MyCtrlA">
<button ng-click="myService.increment()">Increment data in myGlobalData service</button>
</div>
</div>

Syncing data between controllers through a service

From this stackoverflow question, my understanding is that I should be using services to pass data between controllers.
However, as seen in my example JSFiddle, I am having trouble listening to changes to my service when it is modified across controllers.
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.status = App.data.status;
$scope.$watch('App.data.status', function() {
$scope.status = App.data.status;
});
})
.controller('Ctrl2', function ($scope, App) {
$scope.status = App.data.status;
$scope.$watch('status', function() {
App.data.status = $scope.status;
});
})
.service('App', function () {
this.data = {};
this.data.status = 'Good';
});
In my example, I am trying to subscribe to App.data.status in Ctrl1, and I am trying to publish data from Ctrl1 to App. However, if you try to change the input box in the div associated with Ctrl2, the text does not change across the controller boundary across to Ctrl1.
http://jsfiddle.net/VP4d5/2/
Here's an updated fiddle. Basically if you're going to share the same data object between two controllers from a service you just need to use an object of some sort aside from a string or javascript primitive. In this case I'm just using a regular Object {} to share the data between the two controllers.
The JS
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.localData1 = App.data;
})
.controller('Ctrl2', function ($scope, App) {
$scope.localData2 = App.data;
})
.service('App', function () {
this.data = {status:'Good'};
});
The HTML
<div ng-controller="Ctrl1">
<div> Ctrl1 Status is: {{status}}
</div>
<div>
<input type="text" ng-model="localData1.status" />
</div>
<div ng-controller="Ctrl2">Ctrl2 Status is: {{status}}
<div>
<input type="text" ng-model="localData2.status" />
</div>
</div>
Nothing wrong with using a service here but if the only purpose is to have a shared object across the app then I think using .value makes a bit more sense. If this service will have functions for interacting with endpoints and the data be sure to use angular.copy to update the object properties instead of using = which will replace the service's local reference but won't be reflected in the controllers.
http://jsfiddle.net/VP4d5/3/
The modified JS using .value
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, sharedObject) {
$scope.localData1 = sharedObject;
})
.controller('Ctrl2', function ($scope, sharedObject) {
$scope.localData2 = sharedObject;
})
.value("sharedObject", {status:'Awesome'});
I agree with #shaunhusain, but I think that you would be better off using a factory instead of a service:
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.localData1 = App.data;
})
.controller('Ctrl2', function ($scope, App) {
$scope.localData2 = App.data;
})
.factory('App', function () {
var sharedObj = {
data : {
status: 'Good'
}
};
return sharedObj;
});
Here are some information that might help you understand the differences between a factory and a service: When creating service method what's the difference between module.service and module.factory

AngularJS tab navigation and module injection

I have two modules in two separate files as so:
first.js
var app = angular.module('first',['ngGrid']);
app.controller('firstTest',function($scope))
{
...
});
second.js
var app = angular.module('second',['ngGrid']);
app.controller('secondTest',function($scope))
{
...
});
I now want to use these two modules again in a navigation type view as so:
tabs.js
var app = angular.module('myTabs',['first','second']);
$scope.tabs = [
{title: "first", content:first.firstTest},
{title: "second", content:second.secondTest},
];
$scope.navType='pills';
});
What happens is that I get the following error:
unknown provider firstProvider <- first
So the questions I have are
1) Is this the correct way to go about doing tabbed navigation
2) What is the proper technique to handle the injection of the first and second modules?
In our application we have used ngClass directive.
ng-class="{active:isActive('/tab1')}"
Where isActive is the function defined in a scope. This function requires $location variable.
myApp.controller('MyCtrl', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
}
});
You can directly jump to a specific tab using URL location if you use this kind of solution.
Also look at $routeProvider for reference.

Categories

Resources