syncing the views of two angular apps - angularjs

I have two angular applications in one page, and I need them to communicate. Specifically, I want one application to use a service of another application.
I am able to get the service of the other application using Injector.get(service), but when I change the data using the service in one application, it does not reflect in the view of the other, even though both are supposed to show the same data. You can see a simplified version of the problem in jsFiddle.
To save you the click, this is the relevant script:
//myAppLeft - an angular app with controller and service
var myAppLeft = angular.module('myAppLeft', []);
myAppLeft.factory('Service1',function(){
var serviceInstance = {};
serviceInstance.data = ['a','b','c','d','e'];
serviceInstance.remove = function(){
serviceInstance.data.pop();
console.log(serviceInstance.data);
};
return serviceInstance;
} );
myAppLeft.controller('Ctrl1', ['$scope', 'Service1', function($scope, service1) {
$scope.data = service1.data;
$scope.changeData =function(){
service1.remove();
}
}]);
var leftAppInjector = angular.bootstrap($("#leftPanel"), ['myAppLeft']);
//myAppRight = an angular app with controller which uses a service from myAppLeft
var myAppRight = angular.module('myAppRight', []);
myAppRight.controller('Ctrl2', ['$scope', function($scope) {
$scope.data = leftAppInjector.get('Service1').data;
$scope.changeData =function(){
leftAppInjector.get('Service1').remove();
}
}]);
var rightAppInjector = angular.bootstrap($("#rightPanel"), ['myAppRight']);
I'd be happy to know why my code does not work as expected, and would be even happier to know if and how such thing can work.
I understand that if instead of two angular-apps I would have used one angular-app with two modules this would have worked just as I wanted, but unfortunately I cannot adopt this approach because my application consists of a pure-js core with extensions, each extension can be written in a different library/platform and I want my extensions to be angular ones.
Thanks,
Nurit.

Angular apps are separate entities, even if you use the same service in both. the second app just initializes its own version off it.
What you want can be done using localStorage, and the storage events.
Ping me if you need additional help on this!

Related

Pass multiple scope members from controller to angular service

I am new to AngularJS world. I am developing AngularJS SPA application. I have a controller paymentController which is going to use an angular custom service paymentService. The paymentController $scope has multiple members like billerId, billAccount, paymentAmount, etc. I want to pass all/most of these members to the function exposed by angular service. I don't know what is the best way to do so. My code is given below:
app.controller("paymentController", function ($scope, $rootScope, paymentService) {
$scope.billerId;
$scope.billAccount;
$scope.paymentAmount;
$scope.feeAmount=1.0;
$scope.platform = 1;
$scope.makePayment = function(){
paymentService.makePayment(/*what should be passed to this function*/);
}
});
Please suggest me the ideal way.
Better way is to create a object with all those properties and pass the object,
$scope.bill ={};
$scope.bill.billerId;
$scope.billAccount;
$scope.bill.paymentAmount;
$scope.bill.feeAmount=1.0;
$scope.bill.platform = 1;
$scope.makePayment = function(){
paymentService.makePayment($scope.bill);
}

Angular - reusing code (service or controller)

I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.

Firebase syncObject is showing redundant error in my Angular controller

I am building an app for a client who has, as one of their data sets a master list of all their members. I have the data coming in from Firebase and everything runs peachy, but it's not that DRY I am now realizing. I would like to use some of the data from the membership set in other views within the site. I copied the code listed below into other controllers that I need to have access to it and although everything works, my IDE (RubyMine) shows the 'syncObject' as redundant.
So, my question is, if there's a way to code it better and dryer to be used in other views? Thank you for your time.
.controller( 'MembershipCtrl', function MembershipCtrl( $scope, $firebase ) {
var ref = new Firebase("https://myid.firebaseio.com/members");
var sync = $firebase(ref);
var syncObject = sync.$asArray();
$scope.members = syncObject;
});

Using Angular with socket.io

I know that Angular provides awesome two way data binding on the client side, but I want more!
I'm looking for the right way to hook up angular with my server side (SailsJS which includes socket.io).
Thanks!
Have you tried angular-sails-bind? (https://github.com/diegopamio/angular-sails-bind) I bet you haven't, as I've just released to the world a couple of minutes ago :). I made it for my own project and then decided to put it as a separated library so everybody could benefit and I could have my first experience developing a bower package.
I hope it could help you.
BTW: it works with sails 0.10 (as some things, like topic names had changed since 0.9). If you need to make it work with 0.9, just let me know and I'll happy to help.
I would recommend trying out https://github.com/btford/angular-socket-io that allows to simply use socket object in your controllers like that:
var socketApp = angular.module('socketApp', [
'btford.socket-io'
]);
socketApp
.controller('messageListController', ['$scope', 'socket', function($scope, socket) {
$scope.messages = [];
$scope.postMessage = function(message) {};
socket.on('connect', function () {
$scope.$on('socket:update', function(event, data) {
$scope.messages.push(data);
});
$scope.postMessage = function(message, callback) {
socket.emit('post', message, function(commitedMessage) {
$scope.messages.push(commitedMessage);
callback(commitedMessage);
});
};
});
}]);

Angularjs and Meteor "Session" reactivity, is there a way?

I'm trying to work with Meteor and Angularjs. I'm using Meteor_angularjs package, which works OK with Collections.
Now I'm trying to use Session and my reactive data store:
TestCtrl = [
"$scope",
function($scope){
$scope.value = Session.get('someValue');
}
]
This does not work.
QUESTION: Any suggestions on how to tie down Meteor's Session and Angular?
As far as I understand, I can write directive that will be polling Session every so ofter, however I don't think that's a good choice.
Thanks
UPDATE:
I've tried the following:
TestCtrl = [
"$scope",
function($scope){
Meteor.autorun(function(){
$scope.config = Session.get('testsConfig');
if (!$scope.$$phase){
$scope.$digest();
}
});
}
]
and it sort of works, however I get the following error:
Error: INVALID_STATE_ERR: DOM Exception 11
Error: An attempt was made to use an object that is not, or is no longer, usable.
at derez (http://localhost:3000/test:95:41)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30)
at derez (http://localhost:3000/test:95:30) angular.js:5526
$get angular.js:5526
$get angular.js:4660
$get.Scope.$digest angular.js:7674
(anonymous function) controllers.js:46
Meteor.autorun.rerun deps-utils.js:78
_.extend.run deps.js:19
Meteor.autorun.rerun deps-utils.js:78
_.extend.flush deps.js:63
_.each._.forEach underscore.js:79
_.extend.flush deps.js:61
_.each._.forEach underscore.js:79
_.extend.flush deps.js:60
UPDATE 2:
I've tried the service like this (might be wrong usage), still nothing. Now it doesn't update at all on Session value's changes.
Meteor.autorun(function(){
app.factory('ssn', function(){ return{
get: function(val){
return Session.get(val);
}
}});
});
TestCtrl = [
"$scope","ssn",
function($scope, ssn){
$scope.config = ssn.get('testsConfig');
}
]
UPDATE 3: Angular has $apply() for
to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries)
At the same time Meteor has Meteor.render() for
Most of the time, though, you won't call these functions directly — you'll just use your favorite templating package, such as Handlebars or Jade. The render and renderList functions are intended for people that are implementing new templating systems.
However, it seems like I just cannot put 2 and 2 together. :(
this as an old question with old answers but I see people referring to it so here is the updated answer.
First - there is a new library for angular-meteor that handles those cases for you.
And this library gives you two possible solutions:
If you want to bind a Session variable to a scope variable, use the $meteorSession service.
What it does is that every time the scope variable will change, it will change to Session variable (and trigger an autorun if it's placed inside one).
and every time the Session variable will change, the scope variable will change as well (and change the view that it's placed upon).
If you are using the Session variable just to get a variable reactive (meaning trigger an autorun), you should use getReactively . this just returns the already existing scope variable but trigger an autorun every time it changes. a good example of this can be found it our tutorial.
Note: In anyway, when you use Tracker.autorun inside Angular, you need to connect it to a scope. this can be easily done if you replace Tracker.autorun with the $meteorUtils autorun function
Hi here is an option (might not be the best but it works I think)
app.service('Session',function($rootScope){
var self = this;
self.objects = {};
self.get = function(name){
self.objects[name] = {"value" : Session.get(name)};
Meteor.autorun(function() {
var i = Session.get(name);
if(self.objects[name].value != i){
if (!$rootScope.$$phase){
$rootScope.$apply(function(){
self.objects[name].value = i;
});
}
}
});
return self.objects[name];
}
self.set = function(name,value){
self.objects[name].value = value;
Session.set(name,value);
}
return self;
});
Call it in the $scope like this
$scope.test = Session.get("test");
In the view as {{test.value}}. Sorry for the late answer .
Happy new year!
try
angular.module('yourmod', [])
.controller('TestCtrl', ['$scope', function($scope) {
var c = Deps.autorun(function (comp) {
//... put reactive stuf on scope.....
if(!comp.firstRun) {
// only do not do aply at first run becaulse then apply is already running.
$scope.$apply()
}
});
// and to realy make it nice...
$scope.on('$destroy', function () {c.stop()});
}])

Resources